TIL — Gradient Descent, SGD 그리고 Weight Initialization

정승민·4일 전

TIL

목록 보기
4/6

오늘의 핵심
Gradient Descent가 Parameter를 어떻게 업데이트하는지 이해하고, 초기 Weight를 왜 적절하게 설정해야 하는지 학습했다.


1. Gradient란?

📌 Gradient의 의미

함수

f(x)=x2f(x)=x^2

를 미분하면

f′(x)=2xf'(x)=2x

이고 x=3x=3이라면

f′(3)=6f'(3)=6

이다.

여기서 6은 x를 1 증가시키면 y가 정확히 6 증가한다는 의미가 아니다.

💡 현재 x=3x=3 근처에서 x가 아주 조금 변했을 때, 함수값은 그 변화량의 약 6배만큼 변한다는 의미다.

즉,

Δf≈f′(x)Δx\Delta f \approx f'(x)\Delta x

이므로 x=3x=3에서

Δf≈6Δx\Delta f \approx 6\Delta x

이다.

예를 들어 xx가 0.01 변하면

Δf≈6×0.01=0.06\Delta f \approx 6\times0.01=0.06

정도 변한다.


📌 Gradient가 크다는 것은?

Gradient의 절댓값이 크다는 것은

현재 Parameter가 조금만 변해도 Loss가 크게 변할 수 있다는 뜻이다.

즉 현재 위치의 경사가 가파르다는 의미다.

🚨 하지만

Gradient가 크다
≠
최솟값에서 멀리 떨어져 있다

이다.

Gradient는 어디까지나 현재 위치에서의 순간적인 변화율이다.


2. Gradient Descent

우리는 Loss를 최소화하고 싶다.

Gradient는 함수가 가장 빠르게 증가하는 방향을 알려주므로, 반대 방향으로 이동하면 된다.

θnew=θold−η∇θL\theta_{new} = \theta_{old} - \eta\nabla_\theta L
  • θ\theta: Weight, Bias 등의 Parameter
  • LL: Loss
  • ∇θL\nabla_\theta L: Parameter에 대한 Gradient
  • η\eta: Learning Rate

📌 Gradient와 Learning Rate의 역할

Gradient는

어느 방향으로 이동해야 하는지 + 현재 경사가 얼마나 가파른지

를 알려준다.

Learning Rate는

그 방향으로 실제로 얼마나 이동할지

결정한다.

즉,

Δθ=−η∇θL\Delta\theta = -\eta\nabla_\theta L

이다.


3. Learning Rate가 너무 크면?

예를 들어 Gradient가 6이고 Learning Rate가 0.1이라면

Δx=−0.1×6=−0.6\Delta x=-0.1\times6=-0.6

현재 위치가 x=3x=3이라면

xnew=3−0.6=2.4x_{new}=3-0.6=2.4

가 된다.

Gradient가 크고 LR까지 크면:

큰 Gradient
    ×
큰 Learning Rate
    ↓
Parameter가 크게 이동
    ↓
Minimum을 지나침
    ↓
Loss 증가
    ↓
더 큰 Gradient
    ↓
발산

이 발생할 수 있다.


4. 입력 데이터의 Scale과 Gradient

선형회귀를 생각하면

y^i=wxi+b\hat y_i = wx_i+b

MSE는

L=1N∑i=1N(y^i−yi)2L= \frac1N \sum_{i=1}^{N} (\hat y_i-y_i)^2

이고 Weight에 대한 Gradient는

∂L∂w=2N∑ixi(y^i−yi)\frac{\partial L}{\partial w} = \frac{2}{N} \sum_i x_i(\hat y_i-y_i)

이다.

여기서

  • xix_i: i번째 입력
  • yiy_i: 실제 정답
  • y^i\hat y_i: 모델 예측값
  • ww: Weight

즉 Gradient 안에 입력 xix_i가 직접 곱해진다.

키 데이터가 155 ~ 190처럼 크고 예측 오차도 크면 Gradient 역시 커질 수 있다.

📌 그래서 Scaling을 사용한다

Standardization:

x′=x−μσx'=\frac{x-\mu}{\sigma}

등을 사용하면 입력값의 Scale을 조절할 수 있다.

입력 Scale 안정화
↓
Gradient Scale 안정화
↓
Parameter Update 안정화
↓
학습이 쉬워짐

🚨 정규화를 하지 않으면 무조건 발산하는 것은 아니다.
작은 Learning Rate를 사용하면 학습할 수도 있지만, Scaling을 하면 최적화가 훨씬 쉬워지는 경우가 많다.


5. Batch Gradient Descent

Parameter가 w,bw,b이고 데이터가 철수, 영희, 윤수 3개 있다고 하자.

각 데이터에서:

철수 → dw철수, db철수
영희 → dw영희, db영희
윤수 → dw윤수, db윤수

가 계산된다.

GD에서는 Parameter별로 전체 데이터의 Gradient를 평균낸다.

∂L∂w=dw철수+dw영희+dw윤수3\frac{\partial L}{\partial w} = \frac{ dw_{\text{철수}} + dw_{\text{영희}} + dw_{\text{윤수}} }{3}
∂L∂b=db철수+db영희+db윤수3\frac{\partial L}{\partial b} = \frac{ db_{\text{철수}} + db_{\text{영희}} + db_{\text{윤수}} }{3}

그리고 한 번 업데이트한다.

💡 Parameter끼리 Gradient를 평균내는 것이 아니다.
각 Parameter가 자신의 Gradient를 가지며, 그 Parameter에 대한 여러 데이터의 Gradient를 평균내는 것이다.


6. MNIST에서 Gradient Descent

MNIST 학습 데이터가 60,000장이고

model = nn.Linear(784, 10)

이라면 Parameter는

Weight = 784 × 10 = 7,840
Bias   = 10

총 7,850개

이다.

Batch GD라면:

MNIST 60,000장
↓
Forward
↓
Loss 계산
↓
60,000장 전체를 기준으로
7,850개 Parameter의 Gradient 계산
↓
Parameter 전체 Update 1회

즉 한 Epoch에 Update가 한 번 일어난다.


7. SGD

SGD는 전체 데이터를 기다리지 않고 데이터 하나를 사용해 Gradient를 계산하고 바로 Parameter를 업데이트한다.

∂L∂w=∂Li∂w\frac{\partial L}{\partial w} = \frac{\partial L_i}{\partial w}

여기서 LiL_i는 i번째 데이터 하나에 대한 Loss다.

MNIST라면:

1번째 이미지 → Gradient → Update
2번째 이미지 → Gradient → Update
3번째 이미지 → Gradient → Update
...
60,000번째 이미지 → Gradient → Update

따라서 순수 SGD라면:

1 Epoch=60,000 Updates1\ Epoch = 60,000\ Updates

이다.

📌 왜 사용하는가?

Batch GD는 정확한 전체 Gradient를 얻지만, 업데이트 한 번을 위해 모든 데이터를 계산해야 한다.

SGD는 데이터 일부로 전체 Gradient를 확률적으로 추정한다.

그래서 계산 하나당 훨씬 자주 Parameter를 업데이트할 수 있다.


8. Mini-batch SGD

실제 딥러닝에서는 순수 SGD보다 Mini-batch SGD를 훨씬 많이 사용한다.

예를 들어:

DataLoader(
    dataset,
    batch_size=100,
    shuffle=True
)

라면 100개의 데이터에 대해

gB=1100∑i∈Bgig_B = \frac1{100} \sum_{i\in B}g_i

를 계산하고 Parameter를 한 번 업데이트한다.

MNIST 60,000장이라면:

60000100=600\frac{60000}{100}=600

이므로 한 Epoch에 600번 Update한다.

방법한 번에 사용하는 데이터60,000개 기준 Update
Batch GD60,0001
Mini-batch SGD100600
Pure SGD160,000

9. Batch Size는 클수록 좋은가?

Batch Size가 커지면 일반적으로 여러 데이터의 Gradient가 평균되므로

Batch Size↑⇒Gradient Variance↓Batch\ Size\uparrow \Rightarrow Gradient\ Variance\downarrow

한다.

즉 전체 데이터 Gradient에 더 가까운 안정적인 Gradient를 얻을 수 있다.

하지만 무조건 크게 하는 것도 좋은 것은 아니다.

작은 Batch
→ Gradient가 noisy
→ Update를 자주 함

큰 Batch
→ Gradient가 안정적
→ Update 횟수가 줄어듦

Full Batch
→ 가장 정확한 전체 Gradient
→ Epoch당 Update 1회

🎯 Batch Size는 Gradient의 안정성, Update 빈도, GPU 효율 사이의 Trade-off다.


10. Weight Initialization이 필요한 이유

신경망을 학습하기 전에 Weight의 시작값을 결정해야 한다.

좋은 초기화에서 중요한 것은 크게 두 가지다.

📌 1. Weight들이 서로 다른 값을 가져야 한다

그래야 각 뉴런이 서로 다른 Feature를 학습할 수 있다.

📌 2. Weight가 적절한 크기를 가져야 한다

Weight가 너무 크거나 너무 작으면 Layer를 지나면서 Activation과 Gradient의 Scale이 불안정해질 수 있다.

🎯 좋은 초기화 = Symmetry를 깨면서 Activation과 Gradient의 Scale을 안정적으로 유지하는 것


11. Zero Initialization

모든 Weight를 0으로 초기화한다.

Wij=0W_{ij}=0

PyTorch:

nn.init.zeros_(model.weight)

문제는 모든 뉴런이 동일하게 시작한다는 것이다.

같은 Weight
↓
같은 출력
↓
같은 Gradient
↓
같은 Update
↓
계속 같은 Weight

이를 Symmetry Problem이라고 한다.

결국 여러 뉴런을 만들어도 비슷한 Feature만 학습하게 된다.

📌 Bias는?

Bias는 0으로 초기화하는 경우가 흔하다.

nn.init.zeros_(model.bias)

따라서

Deep Network의 Weight 전체를 Zero Initialization하는 것이 문제이지, 모든 Parameter를 절대 0으로 만들면 안 된다는 뜻은 아니다.


12. Constant Initialization

모든 Weight를 같은 특정 값으로 초기화한다.

Wij=cW_{ij}=c

예:

nn.init.constant_(model.weight, 0.5)

Zero가 아니므로 괜찮아 보이지만 여전히 문제가 있다.

Neuron 1 = [0.5, 0.5, 0.5]
Neuron 2 = [0.5, 0.5, 0.5]

두 뉴런이 똑같은 상태로 시작한다.

따라서:

같은 출력
→ 같은 Gradient
→ 같은 Update

가 반복된다.

💡 문제는 0이라는 숫자가 아니라 뉴런들의 Weight가 동일하다는 것이다.


13. Uniform Initialization

Weight를 Uniform Distribution에서 랜덤하게 뽑는다.

Wij∼U(a,b)W_{ij}\sim U(a,b)

예:

Wij∼U(−0.1,0.1)W_{ij}\sim U(-0.1,0.1)

PyTorch:

nn.init.uniform_(
    model.weight,
    a=-0.1,
    b=0.1
)

각 Weight가 서로 다른 값을 가지므로 Symmetry가 깨진다.

서로 다른 Weight
↓
서로 다른 Activation
↓
서로 다른 Gradient
↓
서로 다른 Feature 학습

Uniform Distribution의 평균과 분산은

E[W]=a+b2E[W]=\frac{a+b}{2}
Var(W)=(b−a)212Var(W)=\frac{(b-a)^2}{12}

이다.


14. Normal Initialization

Normal Distribution에서 Weight를 랜덤하게 뽑는다.

Wij∼N(μ,σ2)W_{ij} \sim \mathcal N(\mu,\sigma^2)

PyTorch:

nn.init.normal_(
    model.weight,
    mean=0.0,
    std=0.01
)

보통 평균은 0 주변으로 두고 Standard Deviation을 통해 Weight의 Scale을 결정한다.

Uniform과 달리 Normal Distribution은:

          값 많음
             ↓
            /\
          /    \
--------        --------
             0

처럼 평균 근처의 값이 많이 나온다.


15. Uniform vs Normal

UniformNormal
특정 범위 안에서 고르게 추출평균 근처 값이 많이 등장
범위를 벗어나지 않음큰 값도 낮은 확률로 등장
U(a,b)N(μ,σ²)

하지만 더 중요한 것은

Uniform과 Normal 중 무엇을 선택했느냐보다 Weight의 Scale과 Variance를 적절하게 설정했느냐이다.

실제로 Xavier와 Kaiming 모두 Uniform / Normal 버전이 존재한다.


16. Weight의 크기는 왜 중요한가?

뉴런 하나를 생각해보자.

z=w1x1+w2x2+⋯+wnxn+bz = w_1x_1+w_2x_2+\cdots+w_nx_n+b

여기서:

  • xix_i: 이전 Layer에서 들어오는 입력
  • wiw_i: Weight
  • bb: Bias
  • zz: Activation Function 적용 전 값, Pre-activation

그리고:

a=ϕ(z)a=\phi(z)

이다.

여기서:

  • zz: Activation 이전 값
  • ϕ\phi: ReLU, Sigmoid 등의 Activation Function
  • aa: Activation Function을 통과한 출력

전체 흐름:

이전 Layer의 출력 x
↓
z = Wx + b
↓
Activation Function
↓
a = φ(z)
↓
다음 Layer

17. 입력 개수가 많으면 왜 Weight를 작게 해야 할까?

z=∑i=1nwixiz= \sum_{i=1}^{n}w_ix_i

에서 nn개의 값을 계속 더한다.

입력이 2개인 경우:

w1x1 + w2x2

입력이 1000개인 경우:

w1x1 + w2x2 + ... + w1000x1000

Weight의 Scale을 똑같이 사용한다면 입력 개수가 많을수록 zz의 Scale이 커질 가능성이 있다.

단순한 독립성 등의 가정 아래에서는 대략:

Var(z)≈nVar(w)Var(x)Var(z) \approx nVar(w)Var(x)

라고 생각할 수 있다.

따라서

n↑n\uparrow

일수록 일반적으로 Weight의 Scale을 어느 정도 줄여줄 필요가 있다.

이때 들어오는 입력 뉴런의 개수를 fan-in이라고 한다.


18. Weight가 너무 크거나 작으면?

Weight가 너무 큰 경우

큰 Weight
↓
큰 Pre-activation
↓
Activation Scale 불안정
↓
다음 Layer
↓
Scale이 계속 커질 가능성

Backward에서도 Weight가 반복해서 관여하기 때문에 Gradient Scale 역시 불안정해질 수 있다.

Weight가 너무 작은 경우

작은 Weight
↓
작은 Pre-activation
↓
작은 Activation
↓
Layer를 거치며 Signal 감소 가능

Backward에서도 Gradient가 지나치게 작아질 가능성이 있다.

🚨 다만:

Activation이 크다 → Gradient가 반드시 크다

는 것은 아니다.

Activation Function에 따라 다르다.

예를 들어 Sigmoid는 입력 절댓값이 너무 커지면 포화되어 오히려 미분값이 0에 가까워지고 Gradient Vanishing이 발생할 수도 있다.


19. 초기화의 핵심 정리

초기화 방법의 흐름은 다음과 같다.

Zero Initialization
↓
모든 뉴런이 동일
↓
Symmetry 문제


Constant Initialization
↓
0이 아니어도 동일한 값
↓
여전히 Symmetry 문제


Random Initialization
       ↓
 ┌─────────────┐
 ↓             ↓
Uniform      Normal
 ↓             ↓
뉴런마다 서로 다른 Weight
       ↓
Symmetry Breaking

하지만 여기서 끝이 아니다.

Random하게 만들었다
↓
그런데 Weight를 얼마나 크게 해야 하지?
↓
너무 크면 Scale 불안정
너무 작으면 Signal 감소
↓
입력 개수까지 고려해서
적절한 Weight Variance를 결정하자
↓
Xavier / Kaiming Initialization

20. 오늘의 핵심

📌 Gradient Descent

θ←θ−η∇L\theta \leftarrow \theta-\eta\nabla L
  • Gradient: 방향 + 현재 경사의 가파른 정도
  • Learning Rate: 실제 Parameter 이동 크기
  • Batch GD: 전체 데이터로 Gradient 계산 후 Update

📌 SGD

  • Pure SGD: 데이터 1개마다 Update
  • Mini-batch SGD: 일부 데이터를 평균내어 Update
  • 실제 딥러닝에서는 Mini-batch 방식이 일반적

📌 Weight Initialization

좋은 초기화의 핵심은 두 가지다.

1. Weight들이 서로 다른 값을 가져 Symmetry를 깨야 한다.
2. Weight가 적절한 Scale을 가져 Activation과 Gradient가 안정적으로 전달되어야 한다.

그리고 입력 개수 fan-in이 많아질수록 같은 Weight Scale을 그대로 사용하는 것이 적절하지 않을 수 있다.

이 문제를 해결하기 위해 다음으로 배우게 되는 것이:

  • Xavier / Glorot Initialization
  • Kaiming / He Initialization

이다.

🎯 오늘 가장 중요한 연결

Gradient를 이해하면 → GD / SGD를 이해할 수 있고
Activation과 Gradient의 Scale을 이해하면 → Xavier / Kaiming이 왜 필요한지 이해할 수 있다.

profile
매일 꾸준히

0개의 댓글