포스트

SGD에서 Adam까지 — 기울기를 얻은 다음의 문제

SGD에서 Adam까지 — 기울기를 얻은 다음의 문제

서론

1편에서 역전파로 모든 파라미터의 기울기를 얻었습니다. 그런데 기울기는 방향만 알려 줄 뿐 얼마나 갈지는 말해 주지 않습니다. 이 글은 그 “얼마나”를 정하는 이야기입니다.

이게 왜 3DGS 시리즈에 필요한지부터 말하겠습니다. 3DGS가 학습하는 파라미터는 성격이 제각각입니다.

파라미터단위전형적 크기
위치 $\mu$미터$10^0$
스케일 (로그)무차원$10^0$
불투명도 (로짓)무차원$10^0$
SH 계수$10^{-1}$

크기는 비슷해 보이는데, 손실이 각각에 대해 얼마나 민감한지는 자릿수가 다릅니다. 가우시안을 1cm 옮겼을 때의 픽셀 변화와 불투명도를 0.01 바꿨을 때의 픽셀 변화는 비교가 안 됩니다. 이런 상황에서 학습률 하나로 전부를 굴리는 건 불가능에 가깝고, 그걸 가능하게 만든 것이 Adam입니다.


1. 경사하강 — 그리고 학습률의 딜레마

가장 단순한 규칙입니다.

\[\theta_{t+1} = \theta_t - \eta\, \nabla L(\theta_t)\]

문제는 $\eta$ 하나로 모든 방향을 다뤄야 한다는 점입니다. 이차함수 $L(\theta) = \frac12 \theta^\top A \theta$ 로 보면 명확합니다. $A$ 의 고유값을 $\lambda_1 \ge \dots \ge \lambda_n > 0$ 이라 할 때, 각 고유방향의 갱신은

\[\theta^{(i)}_{t+1} = (1 - \eta \lambda_i)\, \theta^{(i)}_t\]
입니다. 발산하지 않으려면 가장 가파른 방향이 $1 - \eta\lambda_1< 1$, 즉 $\eta < 2/\lambda_1$ 이어야 합니다. 그런데 그렇게 잡으면 가장 완만한 방향의 수축률은
\[1 - \eta \lambda_n \;>\; 1 - \frac{2\lambda_n}{\lambda_1} = 1 - \frac{2}{\kappa}\]

가 됩니다. $\kappa = \lambda_1/\lambda_n$ 이 조건수입니다. 조건수가 크면 완만한 방향은 사실상 멈춥니다. 학습률을 올리면 가파른 방향이 터지고, 내리면 완만한 방향이 안 움직입니다. 이게 딜레마입니다.

조건수라는 말이 낯설다면 다중 시점 기하를 읽기 위한 최소한의 수학의 5장에 같은 개념이 선형 방정식 쪽 관점으로 정리돼 있습니다. 거기서는 “해가 얼마나 불안정한가”였고, 여기서는 “얼마나 느리게 수렴하는가”입니다. 같은 양이 두 얼굴을 보이는 것입니다.


2. SGD — 전부 보지 않고 한 걸음

$N$ 개 데이터의 손실은 평균입니다. 기울기도 평균입니다.

\[\nabla L = \frac{1}{N}\sum_{i=1}^{N} \nabla \ell_i\]

$N$ 이 백만이면 한 걸음 떼는 데 백만 번의 역전파가 필요합니다. 확률적 경사하강(SGD) 은 무작위로 뽑은 미니배치 $B$ 로 이 평균을 근사합니다.

\[\nabla L \approx \frac{1}{|B|}\sum_{i \in B} \nabla \ell_i\]

이건 편향 없는 추정량이라 평균적으로는 옳은 방향을 가리키고, 대신 매 스텝 잡음이 섞입니다. 그 잡음이 나쁘기만 한 것도 아닙니다. 얕은 국소 최소점이나 안장점에서 빠져나오게 해 주는 순기능이 있습니다.1

3DGS와 NeRF에서의 미니배치. 여기서 “데이터 한 개”는 사진 한 장(3DGS)이거나 광선 한 다발(NeRF)입니다. 3DGS의 한 스텝은 학습 이미지 하나를 골라 그 시점으로 전체 장면을 렌더링하고, 원본 사진과의 손실을 역전파하는 것입니다. 배치 크기가 1인 SGD인 셈이라 기울기 잡음이 큽니다. 잡음이 큰 판일수록 옵티마이저의 선택이 중요해집니다.


3. 모멘텀 — 지난 방향을 기억하기

잡음과 지그재그를 동시에 줄이는 가장 오래된 방법은 관성입니다.2

\[m_t = \beta m_{t-1} + \nabla L(\theta_t), \qquad \theta_{t+1} = \theta_t - \eta\, m_t\]

$m_t$ 는 과거 기울기의 지수 가중합입니다. 매 스텝 부호가 뒤집히는 방향(지그재그)은 서로 상쇄돼 작아지고, 매번 같은 부호인 방향(골짜기를 따라 내려가는 방향)은 누적돼 커집니다. 일관된 방향만 증폭하는 필터라고 보면 됩니다.

$\beta = 0.9$ 면 대략 최근 10스텝을 평균하는 것과 같습니다. 유효 학습률이 최대 $\eta/(1-\beta) = 10\eta$ 까지 커질 수 있어서, 모멘텀을 켜면 학습률을 그만큼 내려야 합니다.


4. AdaGrad와 RMSProp — 방향마다 다른 보폭

모멘텀은 방향을 다듬을 뿐 보폭은 여전히 하나입니다. 다른 갈래는 파라미터마다 보폭을 다르게 주는 쪽입니다. AdaGrad는 지금까지 본 기울기 제곱을 전부 누적해 그 제곱근으로 나눕니다.3

\[v_t = v_{t-1} + g_t^2, \qquad \theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{v_t} + \epsilon}\, g_t\]

기울기가 컸던 파라미터는 작게, 작았던 파라미터는 크게 움직입니다. 앞의 딜레마를 정면으로 겨냥한 아이디어입니다. 문제는 $v_t$ 가 단조 증가라 시간이 지나면 보폭이 0으로 수렴해 학습이 멈춘다는 것입니다.

RMSProp은 합 대신 지수이동평균을 씁니다.4

\[v_t = \rho\, v_{t-1} + (1-\rho)\, g_t^2\]

최근 기울기 크기만 보므로 학습이 멈추지 않습니다. 사실상 “각 파라미터의 기울기를 자기 최근 RMS로 나눈다”는 규칙이고, 그래서 갱신량이 대략 $\eta$ 수준으로 정규화됩니다.


5. Adam — 둘을 합치고, 초반을 보정한다

Adam은 모멘텀(1차 모멘트)과 RMSProp(2차 모멘트)을 함께 씁니다.5

\[m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t, \qquad v_t = \beta_2 v_{t-1} + (1-\beta_2) g_t^2\] \[\hat{m}_t = \frac{m_t}{1-\beta_1^t}, \qquad \hat{v}_t = \frac{v_t}{1-\beta_2^t}\] \[\theta_{t+1} = \theta_t - \eta\, \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}\]

기본값은 $\beta_1 = 0.9$, $\beta_2 = 0.999$, $\epsilon = 10^{-8}$ 입니다.

편향 보정이 왜 필요한가

$m_0 = 0$ 에서 시작하므로, 첫 스텝에서 $m_1 = (1-\beta_1) g_1 = 0.1\, g_1$ 입니다. 실제 기울기의 10분의 1입니다. $v$ 쪽은 더 심해서 $v_1 = 0.001\, g_1^2$ 로, 진짜 값의 1000분의 1입니다. 0으로 초기화한 탓에 추정값이 0 쪽으로 끌려간 것입니다.

$m_t$ 의 기댓값을 전개하면 이 편향의 정체가 정확히 보입니다. 기울기가 대략 정상적(stationary)이라고 두면

\[\mathbb{E}[m_t] \approx \mathbb{E}[g]\,(1 - \beta_1^t)\]

이므로 $(1-\beta_1^t)$ 로 나누면 편향이 정확히 상쇄됩니다. 이것이 $\hat{m}_t$, $\hat{v}_t$ 입니다. $t$ 가 커지면 $\beta^t \to 0$ 이라 보정은 저절로 사라지고, 초반 수십~수천 스텝에서만 의미가 있습니다.

보정을 빼면 초반에 $\sqrt{\hat v}$ 가 지나치게 작아져(분모가 작아져) 첫 스텝들이 폭주하기 쉽습니다. 3DGS처럼 초기 기하가 엉성한 상태에서 시작하는 문제에서는 초반 몇백 스텝이 결과를 좌우하므로, 이 보정이 실제로 중요합니다.

옵티마이저별 경로와 잡음 하에서의 수렴 왼쪽: 조건수 60인 이차함수. 순수 경사하강(회색)은 가파른 방향으로 튕기며 골짜기를 거의 못 내려가고, 모멘텀(초록)은 진동한다. 기울기 크기로 나누는 RMSProp(보라)과 Adam(빨강)은 골짜기를 따라 곧게 간다. 오른쪽: 같은 문제에 기울기 잡음을 섞고 시드 12개를 평균한 것. 여기서는 모멘텀까지 갖춘 Adam이 확실히 낮게 내려간다.

왼쪽 그림에서 RMSProp이 Adam만큼(혹은 더) 잘 가는 건 우연이 아닙니다. 잡음 없는 매끄러운 문제에서는 모멘텀이 오히려 오버슈트를 만듭니다. Adam의 값어치는 오른쪽처럼 기울기가 시끄러울 때 나옵니다. 그리고 실제 딥러닝과 3DGS 학습은 언제나 오른쪽 상황입니다.


6. 파라미터마다 스케일이 다를 때

Adam이 3DGS에서 결정적인 이유가 여기 있습니다.

파라미터 스케일이 다를 때 SGD와 Adam 네 파라미터의 곡률이 $0.01$부터 $200$까지 4자릿수에 걸쳐 있는 상황. 왼쪽 SGD는 학습률 하나를 가장 가파른 쪽에 맞출 수밖에 없어서, 완만한 두 파라미터는 400스텝 동안 사실상 제자리다. 오른쪽 Adam은 각 파라미터를 자기 기울기 크기로 나누므로 네 곡선이 거의 포개진다.

Adam의 갱신량을 다시 보면 이유가 분명합니다.

\[\Delta\theta = -\eta\,\frac{\hat m}{\sqrt{\hat v} + \epsilon} \;\approx\; -\eta \cdot \frac{\text{기울기}}{\text{기울기의 크기}}\]

기울기의 크기가 약분됩니다. 남는 건 부호와 방향의 일관성뿐이고, 갱신량은 대략 $\pm\eta$ 근처가 됩니다. 그래서 Adam의 학습률은 “손실을 얼마나 줄일지”가 아니라 “파라미터를 스텝당 얼마나 움직일지”에 가까운 의미를 갖습니다. 파라미터의 물리적 단위가 서로 다를 때 이건 엄청난 장점입니다.

그래도 3DGS는 파라미터군마다 학습률을 따로 둡니다. 위치는 아주 작은 학습률에서 출발해 학습이 진행되며 지수적으로 더 줄이고(대략 $10^{-4}$ 에서 $10^{-6}$ 규모), 불투명도는 그보다 두 자릿수 이상 큰 값을 씁니다. Adam이 스케일은 맞춰 주지만, “이 파라미터를 한 스텝에 얼마나 움직여도 되는가”라는 물리적 판단까지 대신해 주지는 않기 때문입니다. 위치를 한 스텝에 1cm씩 옮기면 장면이 매 스텝 요동치지만, 불투명도는 크게 움직여도 기하가 흔들리지 않습니다.


7. 20줄로 직접 만들어 보기

식이 곧 코드입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import numpy as np

class Adam:
    def __init__(self, params, lr=1e-3, b1=0.9, b2=0.999, eps=1e-8):
        self.p, self.lr, self.b1, self.b2, self.eps = params, lr, b1, b2, eps
        self.m = [np.zeros_like(x) for x in params]
        self.v = [np.zeros_like(x) for x in params]
        self.t = 0

    def step(self, grads):
        self.t += 1
        for i, (p, g) in enumerate(zip(self.p, grads)):
            self.m[i] = self.b1 * self.m[i] + (1 - self.b1) * g
            self.v[i] = self.b2 * self.v[i] + (1 - self.b2) * g * g
            mh = self.m[i] / (1 - self.b1 ** self.t)     # 편향 보정
            vh = self.v[i] / (1 - self.b2 ** self.t)
            p -= self.lr * mh / (np.sqrt(vh) + self.eps)  # 제자리 갱신

파라미터군마다 학습률을 다르게 주고 싶다면 Adam 인스턴스를 여러 개 두거나, lr을 파라미터별 배열로 두면 됩니다. 3DGS 구현이 하는 일이 정확히 이것입니다.


8. 자주 나오는 질문들

Q1. Adam이 항상 최선인가?

아닙니다. 이미지 분류처럼 잘 정제된 문제에서는 SGD + 모멘텀이 최종 일반화 성능에서 Adam을 앞서는 경우가 많다는 보고가 꾸준히 있습니다. Adam은 “수렴을 빠르고 안정적으로 만들되, 하이퍼파라미터 튜닝 부담을 줄여 주는” 선택에 가깝습니다.

다만 3DGS·NeRF 같은 문제에서는 사정이 다릅니다. 여기엔 일반화라는 개념이 거의 없습니다. 장면 하나에 과적합시키는 것이 목적이기 때문입니다. 그러면 남는 기준은 “얼마나 빨리, 얼마나 안정적으로 맞추는가”뿐이고, 그건 Adam이 잘하는 일입니다.

Q2. $\epsilon$ 은 0으로 나누는 걸 막는 값일 뿐인가?

그것만은 아닙니다. $\epsilon$ 은 정규화의 하한이기도 합니다. 기울기가 아주 작은 파라미터에서 $\sqrt{\hat v} \ll \epsilon$ 이면 갱신은 $\eta\hat m/\epsilon$ 이 되어 사실상 SGD처럼 동작합니다. 즉 $\epsilon$ 은 “이보다 작은 기울기는 증폭하지 않는다”는 문턱값입니다. 기본값 $10^{-8}$ 이 늘 옳지는 않아서, 문제에 따라 $10^{-15}$ 나 $10^{-4}$ 를 쓰기도 합니다.

Q3. 가중치 감쇠(weight decay)를 Adam에 그냥 붙이면 되나?

안 됩니다. 손실에 $\frac{\lambda}{2}|\theta|^2$ 을 더하는 방식으로 L2 규제를 넣으면, 그 항의 기울기 $\lambda\theta$ 도 $\sqrt{\hat v}$ 로 나눠집니다. 그러면 기울기가 큰 파라미터일수록 규제가 약해지는 이상한 일이 벌어집니다. 이 결합을 끊고 감쇠를 갱신식에 직접 넣은 것이 AdamW 입니다.6

\[\theta_{t+1} = \theta_t - \eta\left(\frac{\hat m_t}{\sqrt{\hat v_t}+\epsilon} + \lambda\,\theta_t\right)\]

L2 규제가 왜 사전분포와 같은 이야기인지는 베이지안 회귀와 MAP에서 다뤘습니다.

Q4. 학습률 스케줄은 꼭 필요한가?

Adam을 써도 필요합니다. Adam은 방향별 스케일을 맞출 뿐, 최적점 근처에서 보폭을 줄여야 한다는 사실은 바꾸지 못합니다. 잡음이 있는 SGD는 최적점 주변을 학습률에 비례하는 반경으로 맴돌기 때문에, 결국 $\eta$ 를 줄여야 그 반경이 줄어듭니다. 3DGS가 위치 학습률을 지수적으로 감쇠시키는 것도 같은 이유입니다 — 초반에는 가우시안이 크게 움직여 자리를 잡아야 하고, 후반에는 미세 조정만 해야 합니다.

Q5. 3DGS는 파라미터 개수가 학습 도중에 변하는데, Adam 상태는 어떻게 하나?

좋은 질문이고, 실제 구현에서 신경 쓰는 부분입니다. 3DGS는 학습 중 가우시안을 복제·분할·제거하므로 파라미터 텐서의 길이가 바뀝니다. 이때 Adam의 $m$, $v$ 도 같이 늘리고 줄여야 합니다. 새로 생긴 가우시안의 모멘트는 보통 0으로 초기화하는데, 그러면 편향 보정 항 $1-\beta^t$ 는 이미 1에 가까운 상태라 새 파라미터는 보정 없이 시작하는 셈이 됩니다. 밀도 조정 직후에 학습이 잠깐 불안정해지는 이유 중 하나입니다.


마치며

  • 경사하강의 근본 문제는 학습률 하나로 조건수를 이길 수 없다는 것이다.
  • 모멘텀은 방향의 일관성을 증폭해 지그재그와 잡음을 줄인다.
  • RMSProp은 파라미터마다 기울기 크기로 나눠 보폭을 정규화한다.
  • Adam은 둘을 합치고, 0에서 출발한 모멘트의 편향을 $1-\beta^t$ 로 보정한다.
  • 결과적으로 갱신량이 기울기 크기와 무관해져서, 단위가 다른 파라미터를 한 옵티마이저로 굴릴 수 있다. 3DGS가 위치·스케일·불투명도·SH를 동시에 학습할 수 있는 실질적인 이유다.

여기까지가 “학습” 쪽 도구입니다. 다음 글부터는 반대편, 그러니까 사진에서 3차원을 얻는 기하 쪽으로 넘어갑니다.


이 시리즈 — 3DGS를 읽기 위한 기초지식

  1. MLP와 역전파
  2. SGD에서 Adam까지 ← 지금 읽는 글
  3. SfM 파이프라인
  4. MVS — 밀집 복원
  5. 래스터화
  6. 알파 블렌딩과 볼륨 렌더링
  7. 구면 조화 함수
  8. 미분 가능 렌더링
  9. NeRF
  10. 3D Gaussian Splatting ← 도착지

참고문헌

  1. Bottou, L. (2010). Large-scale machine learning with stochastic gradient descent. COMPSTAT 2010, 177–186. ↩︎

  2. Polyak, B. T. (1964). Some methods of speeding up the convergence of iteration methods. USSR Computational Mathematics and Mathematical Physics, 4(5), 1–17. ↩︎

  3. Duchi, J., Hazan, E., & Singer, Y. (2011). Adaptive subgradient methods for online learning and stochastic optimization. JMLR, 12, 2121–2159. ↩︎

  4. Tieleman, T., & Hinton, G. (2012). Lecture 6.5 — RMSProp. COURSERA: Neural Networks for Machine Learning. — 논문 없이 강의 슬라이드로 퍼진 드문 사례. ↩︎

  5. Kingma, D. P., & Ba, J. (2015). Adam: A method for stochastic optimization. ICLR. ↩︎

  6. Loshchilov, I., & Hutter, F. (2019). Decoupled weight decay regularization. ICLR. ↩︎

이 기사는 저작권자의 CC BY-NC-ND 4.0 라이센스를 따릅니다.

License

CC BY-NC-ND 4.0

이 포스트는 CC BY-NC-ND 4.0 라이선스로 제공됩니다. 출처 표시는 필요하며, 상업적 이용과 변경은 허용되지 않습니다.