밑바닥부터 시작하는 딥러닝 1 - optimizer 심화
앞 글에서는 SGD가 왜 지그재그로 움직이고, Momentum과 AdaGrad가 무엇을 기억하는지 큰 흐름을 잡았다. 이번 글은 그 흐름을 코드와 숫자 표로 조금 더 내려놓는 심화편이다.
목표는 optimizer 공식을 모두 외우는 것이 아니다. 같은 기울기가 들어왔을 때 각 방법이 어떤 값을 저장하고, 다음 갱신에 그 값을 어떻게 다시 쓰는지 확인하는 것이다.
이번 글에서 잡을 것
- SGD는 현재 기울기만 사용한다.
- Momentum은 이전 이동량 `v`를 누적해 관성을 만든다.
- AdaGrad는 과거 기울기 제곱합 `h`를 누적해 보폭을 줄인다.
- Adam은 기울기의 방향 평균과 크기 정보를 함께 사용한다.
- optimizer의 차이는 결국 기억 변수와 보폭 조절 방식의 차이다.
공통 출발점: W를 어떻게 바꿀 것인가
모든 optimizer는 결국 같은 질문을 푼다. 현재 가중치 `W`가 있고, 손실 함수가 알려준 기울기 `grad`가 있을 때 `W`를 얼마나, 어떤 방향으로 바꿀 것인가?
| 기호 | 뜻 | 예시 |
|---|---|---|
| W | 현재 매개변수 | 1.0 |
| grad | 손실을 줄이기 위한 방향 단서 | 0.5 |
| lr | 한 번 움직일 보폭 | 0.1 |
| update | 실제로 W에 적용되는 변화량 | 방법마다 다름 |
SGD: 기억 없이 바로 움직인다
SGD는 가장 단순하다. 현재 기울기에 학습률을 곱해서 바로 뺀다. 같은 기울기가 계속 들어오면 매번 같은 크기로 이동한다.
W = 1.0
lr = 0.1
grad = 0.5
for _ in range(3):
W -= lr * grad
print(round(W, 4))
예상 출력
0.95
0.9
0.85
Momentum: 이전 이동량을 섞는다
Momentum은 `v`라는 이동량을 둔다. 이전에 움직이던 방향이 남아 있기 때문에 같은 방향의 기울기가 반복되면 더 빠르게 움직이고, 반대 방향으로 흔들리면 일부가 상쇄된다.
W = 1.0
lr = 0.1
grad = 0.5
momentum = 0.9
v = 0
for _ in range(3):
v = momentum * v - lr * grad
W += v
print(round(v, 4), round(W, 4))
| 반복 | v | W | 해석 |
|---|---|---|---|
| 1 | -0.05 | 0.95 | 처음 이동량 생성 |
| 2 | -0.095 | 0.855 | 이전 이동량이 섞여 더 크게 이동 |
| 3 | -0.1355 | 0.7195 | 같은 방향이면 관성이 붙음 |
AdaGrad: 많이 움직인 곳은 보폭을 줄인다
AdaGrad는 `h`에 과거 기울기 제곱을 계속 더한다. `h`가 커질수록 분모가 커지므로 같은 기울기가 들어와도 실제 이동량은 점점 줄어든다.
import numpy as np
W = 1.0
lr = 0.1
grad = 0.5
h = 0
for _ in range(3):
h += grad * grad
update = lr * grad / (np.sqrt(h) + 1e-7)
W -= update
print(round(h, 4), round(update, 4), round(W, 4))
| 반복 | h | 이동량 | W |
|---|---|---|---|
| 1 | 0.25 | 0.1000 | 0.9000 |
| 2 | 0.50 | 0.0707 | 0.8293 |
| 3 | 0.75 | 0.0577 | 0.7716 |
숫자로 읽는 기준
AdaGrad는 `h`가 커질수록 분모가 커지고, 같은 기울기가 들어와도 실제 이동량이 줄어드는 방식이다.
Adam: 두 종류의 기억을 함께 쓴다
Adam은 보통 Momentum의 방향 누적과 AdaGrad/RMSProp 계열의 보폭 조절을 함께 가진 방법으로 이해하면 된다. 실제 Adam은 기울기의 1차 모멘트와 2차 모멘트를 추적하고, 초반 편향 보정까지 사용한다.
| 기억 | 무엇을 담나 | 왜 필요한가 |
|---|---|---|
| m | 기울기의 이동 평균 | 방향을 부드럽게 만든다 |
| v | 기울기 제곱의 이동 평균 | 크게 흔들리는 매개변수의 보폭을 조절한다 |
| bias correction | 초반 평균값 보정 | 처음 몇 번의 과소평가를 줄인다 |
과장하지 않기
Adam은 무조건 최고인 만능 optimizer가 아니다. 다만 많은 문제에서 초기 선택지로 안정적이라 자주 쓰인다.
이번 심화편에서 가져갈 기준
| 방법 | 기억 변수 | 한 줄 판단 |
|---|---|---|
| SGD | 없음 | 단순하지만 지형에 따라 흔들릴 수 있음 |
| Momentum | v | 이전 이동 방향을 섞어 지그재그를 줄임 |
| AdaGrad | h | 많이 움직인 축의 보폭을 줄임 |
| Adam | m, v | 방향과 크기 정보를 함께 추적함 |
이 정도를 잡으면 optimizer를 이름으로 외우지 않고, 학습 과정에서 무엇을 기억하는 방법인지로 읽을 수 있다.
스스로 점검
- SGD가 기억 변수를 두지 않는다는 점을 코드로 설명할 수 있는가?
- Momentum의 `v`가 왜 관성처럼 작동하는지 말할 수 있는가?
- AdaGrad에서 `h`가 커질수록 이동량이 줄어드는 이유를 설명할 수 있는가?
- Adam이 어떤 두 종류의 정보를 함께 추적하는지 말할 수 있는가?
이번 글에서 기억할 것
- SGD는 현재 기울기만 사용한다.
- Momentum은 이전 이동량 `v`를 누적해 관성을 만든다.
- AdaGrad는 과거 기울기 제곱합 `h`를 누적해 보폭을 줄인다.
- Adam은 기울기의 방향 평균과 크기 정보를 함께 사용한다.
- optimizer의 차이는 결국 기억 변수와 보폭 조절 방식의 차이다.
다음 글로 이어지는 질문
다음 글에서는 optimizer가 매개변수를 바꾸는 방법을 넘어, 학습 자체를 안정시키는 초깃값과 정규화 기법을 본다.
한 줄 정리: optimizer 심화의 핵심은 공식 암기가 아니라, 각 방법이 어떤 기억 변수를 만들고 그 기억으로 다음 이동을 어떻게 바꾸는지 읽는 것이다.