수학

MATH / 중급 47번

모멘텀·네스테로프·EMA는 같은 점화식이다

옵티마이저의 모멘텀, 가중치 EMA 체크포인트, BatchNorm의 러닝 통계가 전부 v ← βv + (1−β)g 하나입니다. 유효 평균 창이 1/(1−β)인 이유를 등비급수로 유도하고, 좁은 골짜기에서 모멘텀이 반복 수를 κ에서 √κ로 줄이는 것을 실제로 세어 봅니다.

PALDYN Team38 MIN READ

학습 코드를 훑어보면 0.90.9, 0.990.99, 0.9990.999 라는 숫자가 서로 다른 세 자리에 나옵니다. 옵티마이저의 momentum, 가중치 평균 체크포인트의 ema_decay, 그리고 BatchNorm의 momentum입니다. 하는 일이 전혀 달라 보이는데 값의 범위가 똑같습니다.

우연이 아닙니다. 셋 다 같은 점화식이고, β\beta 는 셋 모두에서 "얼마나 긴 과거를 기억하는가"라는 같은 뜻입니다. 지난 글에서 학습률을 최선으로 골라도 반복 수가 조건수 κ\kappa 에 비례한다는 벽을 만났는데, 이 점화식이 그 벽을 κ\sqrt\kappa 로 낮춥니다.

지수이동평균

점화식과 펼친 꼴

관측값 g1,g2,…g_1, g_2, \dots 가 하나씩 들어올 때, 지난 것을 전부 저장하지 않고 평균을 유지하는 방법입니다. 지수이동평균(EMA)은 새 값이 올 때마다 이 한 줄을 돌립니다.

vt=β vt−1+(1−β) gt,v0=0v_t = \beta\,v_{t-1} + (1-\beta)\,g_t, \qquad v_0 = 0

기억해야 할 것이 vv 하나뿐이라 메모리가 상수입니다. 무엇을 계산하고 있는지는 펼쳐 보면 나옵니다.

v1=(1−β)g1v2=β(1−β)g1+(1−β)g2v3=β2(1−β)g1+β(1−β)g2+(1−β)g3\begin{aligned} v_1 &= (1-\beta)g_1 \\ v_2 &= \beta(1-\beta)g_1 + (1-\beta)g_2 \\ v_3 &= \beta^2(1-\beta)g_1 + \beta(1-\beta)g_2 + (1-\beta)g_3 \end{aligned}

규칙이 보입니다. kk 스텝 전의 값에 붙는 가중치가 (1−β)βk(1-\beta)\beta^k 입니다.

vt=(1−β)∑k=0t−1βk gt−kv_t = (1-\beta)\sum_{k=0}^{t-1} \beta^k\, g_{t-k}

EMA는 과거로 갈수록 가중치가 등비수열로 줄어드는 가중평균이다.

β가 클수록 과거의 가중치가 천천히 줄어든다

가중치의 합이 1인지 확인합니다. 등비급수의 부분합을 씁니다.

(1−β)∑k=0t−1βk=(1−β)⋅1−βt1−β=1−βt(1-\beta)\sum_{k=0}^{t-1}\beta^k = (1-\beta)\cdot\frac{1-\beta^t}{1-\beta} = 1 - \beta^t

tt 가 커지면 1로 갑니다.

유효 표본 수

「대략 최근 몇 개의 평균인가」를 재는 셈이 하나 더 있습니다. 가중평균의 유효 표본 수(effective sample size)라 부르는 양으로, 가중치 wkw_k 에 대해 (∑wk)2/∑wk2(\sum w_k)^2 / \sum w_k^2 으로 정의합니다. 모든 가중치가 같으면 정확히 항의 개수가 나오므로, 「몇 개짜리 균등평균과 같은 무게인가」를 재는 자입니다.

∑kwk=1,∑kwk2=(1−β)2∑k≥0β2k=(1−β)21−β2=1−β1+β\sum_k w_k = 1, \qquad \sum_k w_k^2 = (1-\beta)^2 \sum_{k\ge0} \beta^{2k} = \frac{(1-\beta)^2}{1-\beta^2} = \frac{1-\beta}{1+\beta}

ESS=1(1−β)/(1+β)=1+β1−β\text{ESS} = \frac{1}{(1-\beta)/(1+\beta)} = \frac{1+\beta}{1-\beta}

β=0.9\beta = 0.9 에서 19입니다. 곧 볼 1/(1−β)=101/(1-\beta) = 10 의 거의 두 배입니다. 두 수가 다른 것이 오류가 아니고 재는 것이 다릅니다 — 1/(1−β)1/(1-\beta) 는 「가장 큰 가중치 1−β1-\beta 로 무게 1을 채우려면 항이 몇 개 필요한가」이고, ESS는 「가중치를 제곱해 합쳐 잰 실질적인 개수」입니다. 가중치가 고르지 않을수록 두 셈이 벌어지고, 등비수열에서는 그 비가 언제나 1+β1+\beta 입니다.

어느 쪽을 쓸지는 무엇을 물었는지가 정합니다. 「얼마나 오래된 것까지 보고 있나」는 창 1/(1−β)1/(1-\beta) 이고, 「평균을 내서 잡음이 얼마나 줄었나」는 ESS입니다. 뒤쪽은 다음 절에서 수로 확인합니다.

초기 편향과 v₀의 선택

가중치 합 1−βt1-\beta^t 는 초반에 1보다 작습니다. β=0.9\beta = 0.9 이면 t=1t=1 에서 0.10.1, t=5t=5 에서 0.40950.4095 뿐입니다. v0=0v_0 = 0 에서 출발했으니 초반의 vv 는 실제 평균보다 작게 나옵니다. 이것을 초기 편향이라 하고, 1/(1−βt)1/(1-\beta^t) 로 나눠 고치는 방법은 다음 글에서 Adam을 조립하며 유도합니다.

같은 편향이 세 자리에서 문제가 되는 정도는 전혀 다릅니다. 갈리는 것은 βt\beta^t 하나이므로, β\beta 와 실제 스텝 수를 함께 봐야 합니다.

쓰이는 자리 β\beta 관심 있는 tt 1−βt1-\beta^t 보정이 필요한가
Adam의 2차 모멘트 0.999 1 0.001 반드시 — 1000배 작다
BatchNorm 러닝 통계 0.9 50 0.995 첫 에폭 안에 사라진다
EMA 체크포인트 0.999 5000 0.993 저절로 사라진다

수천 스텝을 도는 EMA 체크포인트는 βt\beta^t 가 0에 붙어 버려 편향이 따로 손대지 않아도 없어집니다. Adam은 반대입니다 — 보정이 필요한 자리가 바로 첫 몇 스텝이고, 그 몇 스텝에서 걸음이 터무니없이 커지면 학습이 시작부터 어긋납니다.

보정 대신 출발점을 바꾸는 길도 있습니다. v0=0v_0 = 0 이 아니라 첫 관측값을 그대로 넣어 v1=g1v_1 = g_1 으로 두면, 가중치의 합이 처음부터 정확히 1이 되어 편향이 아예 생기지 않습니다. 첫 관측의 가중치가 βt−1\beta^{t-1} 이고 나머지 항의 합이 1−βt−11-\beta^{t-1} 이라 둘을 더하면 1이기 때문입니다.

대신 치르는 값이 있습니다. 그 g1g_1 의 가중치가 원래 받을 (1−β)βt−1(1-\beta)\beta^{t-1} 의 1/(1−β)1/(1-\beta) 배 — β=0.9\beta = 0.9 에서 10배 — 라, 관측 하나가 초반을 지배합니다. 그래디언트처럼 한 표본이 크게 튀는 값에서는 초반이 오히려 더 흔들립니다. 편향 보정은 「전부 작게 나오는 것」을 고치고 v1=g1v_1 = g_1 은 「첫 값에 휘둘리는 것」을 대가로 받는 맞바꿈입니다.

유효 평균 창 1/(1−β)

두 가지 셈

"β=0.99\beta = 0.99 는 대략 최근 100개의 평균"이라는 말을 자주 씁니다. 이 100이 어디서 나오는지를 두 가지로 셈해 봅니다.

첫째, 가중치의 합으로 세기. 무한히 이어지는 항의 가중치 합은 (1−β)∑k≥0βk=1(1-\beta)\sum_{k\ge0}\beta^k = 1 입니다. 항 하나의 가중치가 최대 1−β1-\beta 이므로, 같은 무게를 균등한 평균으로 만들려면 항이 1/(1−β)1/(1-\beta) 개 필요합니다.

둘째, 평균 지연으로 세기. 지금 vtv_t 가 실제로 어느 시점을 대표하는지는 가중치로 잰 kk 의 기댓값입니다.

E[k]=∑k=0∞k (1−β)βk=(1−β)⋅β(1−β)2=β1−β\mathbb{E}[k] = \sum_{k=0}^{\infty} k\,(1-\beta)\beta^k = (1-\beta)\cdot\frac{\beta}{(1-\beta)^2} = \frac{\beta}{1-\beta}

β=0.9\beta = 0.9 이면 9입니다. 즉 vtv_t 는 평균적으로 9스텝 전의 값을 대표합니다. 두 셈이 정확히 1만큼 차이 나는 같은 이야기입니다.

가중치의 절반이 얼마나 최근에 몰려 있는지도 물어볼 수 있습니다. βk=12\beta^k = \tfrac12 에서 반감 스텝은 ln⁡2/ln⁡(1/β)\ln 2 / \ln(1/\beta) 입니다.

β\beta 창 1/(1−β)1/(1-\beta) 유효 표본 수 평균 지연 무게 절반이 차는 스텝
0.9 10 19 9.0 6.6
0.99 100 199 99.0 69.0
0.999 1000 1999 999.0 692.8

같은 점화식이 쓰이는 세 자리

BatchNorm의 러닝 평균, EMA 체크포인트, 옵티마이저 모멘텀에서 β\beta 를 고른다는 것은 이 표에서 창 길이를 고르는 일입니다. 세 자리 모두 "얼마나 긴 과거로 지금을 부드럽게 만들 것인가"를 묻고 있기 때문입니다. (다만 PyTorch의 BatchNorm은 인자 이름이 momentum이면서 값의 뜻이 반대인 1−β1-\beta 라, momentum=0.1이 β=0.9\beta = 0.9 입니다.)

평활이 잡음을 줄이는 크기

「부드러워진다」를 수로 바꿔 봅니다. 들어오는 값이 서로 독립이고 각각 분산 σ2\sigma^2 으로 흔들린다면, 가중평균의 분산은 가중치의 제곱합이 곱해진 값입니다.

Var(vt)=σ2∑kwk2=σ2⋅1−β1+β\mathrm{Var}(v_t) = \sigma^2 \sum_k w_k^2 = \sigma^2 \cdot \frac{1-\beta}{1+\beta}

앞 절에서 구해 둔 제곱합이 그대로 쓰입니다. 곧 분산이 ESS로 나뉜다는 말이고, 균등평균에서 분산이 표본 수로 나뉘는 것과 같은 모양입니다.

β\beta 분산 배율 표준편차 배율 몇 배로 조용해지나
0.9 0.0526 0.229 4.4배
0.99 0.0050 0.071 14.1배
0.999 0.0005 0.022 44.7배

β=0.9\beta = 0.9 로 평균을 내면 흔들림의 폭이 4.4배 줄어듭니다. 그리고 세 번째 열이 (1−β)/(1+β)\sqrt{(1-\beta)/(1+\beta)} 이므로, β\beta 를 1에 붙이는 데 드는 값이 제곱근으로만 돌아옵니다 — 창을 100배 늘려도 잡음은 10배밖에 안 줄어듭니다.

지연과 잡음은 같은 손잡이의 양 끝

그런데 잡음을 줄이는 대가가 바로 옆 칸에 적혀 있습니다. 같은 β\beta 를 올리면 평균 지연 β/(1−β)\beta/(1-\beta) 도 함께 커집니다. 둘이 같은 손잡이의 양 끝입니다.

  • β\beta 를 올리면 — 잡음은 (1−β)/(1+β)\sqrt{(1-\beta)/(1+\beta)} 로 줄고, 지연은 β/(1−β)\beta/(1-\beta) 로 늘어난다.
  • β\beta 를 내리면 — 반응이 빨라지고, 그만큼 시끄러워진다.

입력이 계단처럼 한 번 뛰는 상황에서 그려 보면 지연이 눈에 보입니다.

β를 올리면 계단 입력을 따라잡는 데 그만큼 오래 걸린다

세 곡선이 모두 t=1/(1−β)t = 1/(1-\beta) 에서 63% 를 지납니다. 1−βt1-\beta^t 에 t=1/(1−β)t = 1/(1-\beta) 를 넣으면 β\beta 가 1에 가까울 때 1−e−1=0.6321 - e^{-1} = 0.632 로 가기 때문이고, 그래서 창 길이는 「과거를 얼마나 보나」이면서 동시에 「새 사실에 63% 반응하는 데 걸리는 스텝」입니다.

고를 때 물을 것은 「지금 재려는 값이 얼마나 빨리 바뀌는가」입니다. BatchNorm의 러닝 통계는 학습 중 분포가 계속 움직이므로 지연이 짧아야 해서 β=0.9\beta = 0.9 를 쓰고, 마지막에 가중치를 평균 내는 EMA 체크포인트는 잡음만 없애면 되고 지연이 문제가 안 되므로 0.9990.999 를 씁니다.

EMA된 그래디언트로 걷기

두 형태가 같은 것인 자리

경사하강은 매 스텝 그 순간의 그래디언트만 봅니다. 그래디언트를 EMA로 부드럽게 만든 뒤 그 방향으로 걸으면 모멘텀입니다.

vt=β vt−1+(1−β) ∇f(xt),xt+1=xt−η′ vtv_t = \beta\,v_{t-1} + (1-\beta)\,\nabla f(x_t), \qquad x_{t+1} = x_t - \eta'\,v_t

교과서와 PyTorch가 쓰는 형태는 조금 다릅니다. (1−β)(1-\beta) 를 떼고 씁니다.

vt=β vt−1+∇f(xt),xt+1=xt−η vtv_t = \beta\,v_{t-1} + \nabla f(x_t), \qquad x_{t+1} = x_t - \eta\,v_t

두 형태는 같은 것입니다. 둘째 식의 vv 는 첫째 식의 vv 를 (1−β)(1-\beta) 로 나눈 값이므로, η=η′(1−β)\eta = \eta'(1-\beta) 로 두면 갱신이 글자 그대로 일치합니다. 그래서 β\beta 를 0.90.9 에서 0.990.99 로 올릴 때 학습률을 함께 내려야 하는 것이고, 그 비율이 정확히 (1−β)(1-\beta) 입니다.

뒤에서 이득을 계산할 때는 어느 형태로 적고 있는지를 매번 확인해야 합니다. 같은 「vv 가 몇 배로 자란다」가 형태에 따라 다른 수가 되기 때문입니다.

뒤집히는 성분과 일정한 성분

왜 이득인지는 지난 글에서 본 길쭉한 골짜기에서 드러납니다. 그런 지형에서 그래디언트는 가파른 방향으로는 매 스텝 부호가 뒤집히고, 완만한 방향으로는 계속 같은 부호입니다. 이 둘이 EMA를 지나면 어떻게 되는지를 「상쇄된다」 대신 수로 적어 봅니다. 아래는 (1−β)(1-\beta) 를 뗀 형태이고, 들어오는 값이 상수 cc 인 성분과 (−1)ta(-1)^t a 로 뒤집히는 성분입니다.

c  ⟼  c∑k≥0βk=c1−β,(−1)ta  ⟼  (−1)ta∑k≥0(−β)k=(−1)ta1+βc \;\longmapsto\; c\sum_{k\ge0}\beta^k = \frac{c}{1-\beta}, \qquad (-1)^t a \;\longmapsto\; (-1)^t a\sum_{k\ge0}(-\beta)^k = \frac{(-1)^t a}{1+\beta}

부호가 번갈아 들어오면 등비급수의 비가 β\beta 가 아니라 −β-\beta 가 되어 분모가 1+β1+\beta 로 바뀝니다. 그것이 상쇄의 정확한 크기입니다. 두 이득의 비를 보면

1/(1−β)1/(1+β)=1+β1−β\frac{1/(1-\beta)}{1/(1+\beta)} = \frac{1+\beta}{1-\beta}

이고, β=0.9\beta = 0.9 에서 19배입니다. 일정한 성분은 10배로 자라고 뒤집히는 성분은 0.526배로 남아, 같은 크기로 들어온 두 성분이 19배로 벌어집니다. 정규화한 형태 v←βv+(1−β)gv \leftarrow \beta v + (1-\beta)g 로 적으면 앞의 것이 cc 그대로이고 뒤의 것이 (1−β)/(1+β)=0.0526(1-\beta)/(1+\beta) = 0.0526 배로 눌리는데, 비는 여전히 19입니다. 형태가 바꾸는 것은 두 수의 절대 크기이고 벌어지는 배율은 아닙니다.

지그재그는 상쇄되고 일정한 성분만 쌓인다

유효 학습률 η/(1−β)

일정한 성분이 1/(1−β)1/(1-\beta) 배로 자란다는 것은 걸음의 크기에 곧바로 걸립니다. (1−β)(1-\beta) 를 뗀 형태에서 그래디언트가 gg 로 일정하면 vv 가 g/(1−β)g/(1-\beta) 에 앉고, 걸음은

η v=η1−β g\eta\,v = \frac{\eta}{1-\beta}\,g

가 됩니다. 즉 이 형태의 모멘텀은 유효 학습률 η/(1−β)\eta/(1-\beta) 로 걷는 경사하강처럼 움직입니다. β=0.9\beta = 0.9 에서 10배, 0.990.99 에서 100배입니다.

이것이 실무에서 두 가지로 나타납니다. 하나는 앞에서 본 「β\beta 를 올리면 학습률을 내려야 한다」는 규칙이고, 다른 하나는 잡음이 섞였을 때의 바닥 높이입니다. 그래디언트에 잡음이 섞이면 파라미터가 최솟값 주위에서 떨리다 멈추는데, 그 떨림의 크기가 학습률에 따라 정해집니다. 모멘텀을 쓰면 그 바닥이 η\eta 가 아니라 η/(1−β)\eta/(1-\beta) 짜리 경사하강과 비슷한 자리에 섭니다 — 뒤의 코드에서 실제로 재 봅니다.

네스테로프의 미리보기

미리보기 지점

네스테로프 가속 경사법은 한 군데만 바꿉니다. 그래디언트를 지금 서 있는 xtx_t 가 아니라, 모멘텀이 어차피 데려갈 자리에서 잽니다.

vt=β vt−1+∇f(xt−ηβ vt−1⏟미리보기 지점),xt+1=xt−η vtv_t = \beta\,v_{t-1} + \nabla f\big(\underbrace{x_t - \eta\beta\,v_{t-1}}_{\text{미리보기 지점}}\big), \qquad x_{t+1} = x_t - \eta\,v_t

xt−ηβvt−1x_t - \eta\beta v_{t-1} 은 그래디언트를 더하지 않고 관성만으로 갈 자리입니다. 거기서 잰 기울기로 보정하니, 골짜기 벽을 향해 달려가고 있으면 부딪히기 전에 그 사실이 갱신에 들어옵니다. 기존 모멘텀은 벽에 닿은 다음에야 알아차립니다.

같은 η와 β에서 네스테로프는 가파른 방향의 진동을 먼저 죽인다

좌표를 옮겨 적은 형태

식을 그대로 구현하면 문제가 하나 생깁니다. 파라미터 xtx_t 와 미리보기 지점이 서로 다른 점이라, 둘을 따로 들고 있어야 하는 것처럼 보입니다. 파라미터가 수억 개인 모델에서 버퍼 하나를 더 두는 것은 비싼 일입니다.

그래서 쓰는 요령이 좌표를 미리보기 지점으로 옮겨 적는 것입니다. 우리가 들고 있는 변수가 처음부터 미리보기 지점이라고 두면 갱신이 이렇게 됩니다.

vt=β vt−1+gt,xt+1=xt−η (gt+β vt)v_t = \beta\,v_{t-1} + g_t, \qquad x_{t+1} = x_t - \eta\,(g_t + \beta\, v_t)

여기서 gtg_t 는 지금 들고 있는 점에서 잰 그래디언트입니다. 새로 저장할 것이 없고 갱신 방향만 gt+βvtg_t + \beta v_t 로 바뀝니다. PyTorch의 nesterov=True가 쓰는 것이 이 형태입니다.

# torch.optim.SGD 의 nesterov 분기와 같은 모양
buf = momentum * buf + grad
d   = grad + momentum * buf      # 여기 한 줄만 다르다
p   = p - lr * d

주의할 점이 있습니다. 정착한 뒤의 걸음은 두 방법이 같습니다 — 그래디언트가 상수 gg 로 일정하면 v→g/(1−β)v \to g/(1-\beta) 이고 g+βv=g/(1−β)g + \beta v = g/(1-\beta) 라, 유효 학습률이 양쪽 다 η/(1−β)\eta/(1-\beta) 입니다. 다른 것은 지금 막 들어온 그래디언트에 붙는 계수입니다. 위 식을 이차함수에 대입하면 xx 의 갱신에서 ηλ\eta\lambda 자리에 ηλ(1+β)\eta\lambda(1+\beta) 가 들어가고, 그만큼 안정 범위가 좁아집니다. κ=100\kappa = 100 짜리 골짜기에서 재면 안정 상한이 η=0.0334\eta = 0.0334 에서 0.01430.0143 으로 절반 이하입니다 — 그래서 폴랴크의 최적 η⋆=0.0331\eta^\star = 0.0331 을 네스테로프에 그대로 넣으면 발산합니다.

폴랴크와 나란히 놓기

이차함수에서는 두 방법의 최적 설정과 수렴률을 끝까지 계산할 수 있습니다. 뒤 절에서 유도하는 폴랴크의 값과 나란히 적습니다.

폴랴크 (기존 모멘텀) 네스테로프
최적 η\eta 4/(λmax⁡+λmin⁡)24/(\sqrt{\lambda_{\max}}+\sqrt{\lambda_{\min}})^2 1/λmax⁡1/\lambda_{\max}
최적 β\beta ((κ−1)/(κ+1))2\big((\sqrt\kappa-1)/(\sqrt\kappa+1)\big)^2 (κ−1)/(κ+1)(\sqrt\kappa-1)/(\sqrt\kappa+1)
수렴률 ρ\rho (κ−1)/(κ+1)(\sqrt\kappa-1)/(\sqrt\kappa+1) 1−1/κ1 - 1/\sqrt\kappa
κ=100\kappa = 100 에서 0.8182 0.9
일반 볼록·매끄러움 보장 없음 O(1/k2)O(1/k^2)

읽는 순서가 중요합니다. 이차함수에서는 폴랴크가 더 빠릅니다 — κ=100\kappa=100 에서 0.818 대 0.9이고, 수렴률이 작을수록 빠릅니다. 그런데 이차함수를 벗어나면 폴랴크에는 수렴 보장이 아예 없습니다. 강볼록이고 매끄러운 함수인데도 폴랴크가 수렴하지 않는 반례가 알려져 있습니다.

네스테로프 쪽은 반대입니다. 이차함수에서는 조금 느린데, 볼록하고 매끄러운 함수 전체에서 O(1/k2)O(1/k^2) 를 보장합니다. 경사하강의 O(1/k)O(1/k) 와 비교하면 차수가 하나 오르는 것이고, 이 등급에서 더 좋아질 수 없다는 하한도 함께 증명되어 있습니다.

그래서 고르는 기준은 속도가 아니라 지형입니다. 손실이 이차함수에 가까운 구간에서 마지막 수렴을 짜내려면 폴랴크의 최적값이 낫고, 지형을 모르는 채 돌려야 하는 딥러닝에서는 보장이 있는 쪽이 안전합니다.

조건수와 √κ 가속

행렬 거듭제곱으로 적기

이득을 정확히 계산할 수 있습니다. 이차함수 f(x)=12λx2f(x) = \tfrac12\lambda x^2 에서 모멘텀 갱신을 상태 (xt, vt−1)(x_t,\ v_{t-1}) 의 선형 점화식으로 적습니다.

vt=βvt−1+λxtxt+1=xt−ηvt=(1−ηλ) xt−ηβ vt−1\begin{aligned} v_t &= \beta v_{t-1} + \lambda x_t \\ x_{t+1} &= x_t - \eta v_t = (1-\eta\lambda)\,x_t - \eta\beta\,v_{t-1} \end{aligned}

[xt+1vt]=[1−ηλ−ηβλβ]⏟M[xtvt−1]\begin{bmatrix} x_{t+1} \\ v_t \end{bmatrix} = \underbrace{\begin{bmatrix} 1-\eta\lambda & -\eta\beta \\ \lambda & \beta \end{bmatrix}}_{M}\begin{bmatrix} x_t \\ v_{t-1} \end{bmatrix}

경사하강에서 곱셈 하나였던 것이 행렬 거듭제곱이 되었습니다. 그래서 수렴 속도는 스펙트럼 반지름 ρ(M)\rho(M) 이 정합니다. 여기에 β\beta 가 들어 있는 것이 요점입니다 — 경사하강은 ∣1−ηλ∣|1-\eta\lambda| 하나로 묶여 있어 λmax⁡\lambda_{\max} 와 λmin⁡\lambda_{\min} 을 동시에 만족시킬 수가 없었는데, 손잡이가 둘이 되면서 여지가 생깁니다.

최적 η와 β

η\eta 와 β\beta 를 함께 최적화한 값이 폴랴크의 고전적인 결과입니다.

η⋆=4(λmax⁡+λmin⁡)2,β⋆=(κ−1κ+1)2\eta^\star = \frac{4}{\big(\sqrt{\lambda_{\max}} + \sqrt{\lambda_{\min}}\big)^2}, \qquad \beta^\star = \left(\frac{\sqrt\kappa - 1}{\sqrt\kappa + 1}\right)^2

그때의 수렴률. ρ⋆=κ−1κ+1\rho^\star = \dfrac{\sqrt\kappa - 1}{\sqrt\kappa + 1}

경사하강의 κ−1κ+1\dfrac{\kappa-1}{\kappa+1} 에서 κ\kappa 가 κ\sqrt\kappa 로 바뀌었습니다.

반복 수는 log⁡(1/ε)/log⁡(1/ρ)\log(1/\varepsilon)/\log(1/\rho) 이고 κ\kappa 가 크면 경사하강이 κ/2\kappa/2 에, 모멘텀이 κ/2\sqrt\kappa/2 에 비례합니다. 손실을 1000분의 1로 줄이는 데 걸리는 스텝으로 비교합니다.

κ\kappa 경사하강 ρ\rho 스텝 모멘텀 ρ\rho 스텝 배율 κ\sqrt\kappa
10 0.8182 34 0.5195 11 3.3배 3.2
100 0.9802 345 0.8182 34 10.0배 10.0
1000 0.9980 3454 0.9387 109 31.6배 31.6

배율이 κ\sqrt\kappa 와 소수점까지 맞습니다. 조건수 100짜리 문제에서 모멘텀은 정확히 10배 빠릅니다. 그리고 κ\kappa 가 나쁠수록 이득이 커집니다 — 딥러닝의 손실 지형이 대체로 아주 길쭉하기 때문에 모멘텀이 사실상 기본값인 이유가 이것입니다.

조건수가 나쁠수록 모멘텀의 이득이 커진다

κ=100\kappa = 100 일 때 β⋆=(9/11)2=0.6694\beta^\star = \big(9/11\big)^2 = 0.6694 입니다. 실무의 0.90.9 와 크게 다르지 않고, 조건수가 더 나쁠수록 1에 가까워집니다 — κ=1000\kappa = 1000 이면 0.88110.8811 입니다.

고윳값이 복소수가 되는 문턱

β\beta 를 올리다 보면 MM 의 성격이 한 번 바뀌는 자리가 있습니다. 특성방정식을 적으면 보입니다. tr M=1+β−ηλ\mathrm{tr}\,M = 1+\beta-\eta\lambda 이고 det⁡M=β\det M = \beta 이므로

z2−(1+β−ηλ) z+β=0,Δ=(1+β−ηλ)2−4βz^2 - (1+\beta-\eta\lambda)\,z + \beta = 0, \qquad \Delta = (1+\beta-\eta\lambda)^2 - 4\beta

이고, Δ<0\Delta < 0 이면 두 고윳값이 서로 켤레인 복소수가 됩니다. 조건을 β\beta 에 대해 풀면

β  >  (1−ηλ )2\beta \;>\; \big(1 - \sqrt{\eta\lambda}\,\big)^2

입니다. 이 문턱을 넘으면 두 고윳값의 크기가 같아지고, 곱이 det⁡M=β\det M = \beta 이므로 각각의 크기는

ρ=β\rho = \sqrt{\beta}

입니다. 여기서 두 가지가 따라옵니다. 첫째, 문턱 위에서는 수렴률이 β\beta 하나로만 정해집니다 — η\eta 와 λ\lambda 는 사라지고, 그래서 어느 방향이든 같은 속도로 줄어듭니다. 둘째, 복소수 고윳값은 회전을 뜻하므로 궤적이 곧게 붙지 않고 진동하며 붙습니다.

최적값이 어디 있는지를 이 문턱으로 다시 읽으면 왜 그 값인지가 드러납니다. κ=100\kappa = 100 에서 η⋆=4/121=0.03306\eta^\star = 4/121 = 0.03306 이고, 두 고윳값 각각에 문턱을 계산하면

λ\lambda η⋆λ\eta^\star\lambda 문턱 (1−η⋆λ)2(1-\sqrt{\eta^\star\lambda})^2
100 3.3058 0.66942
1 0.03306 0.66942

둘이 같고, 그 값이 정확히 β⋆=0.66942\beta^\star = 0.66942 입니다. 최적 β\beta 는 두 방향의 문턱이 만나는 자리이고, 그래서 두 방향의 ρ\rho 가 0.66942=0.8182\sqrt{0.66942} = 0.8182 로 같아집니다. 가장 빠른 방향과 가장 느린 방향을 같은 속도로 맞추는 것이 최적화가 한 일의 전부입니다.

β\beta 를 그보다 더 올리면 두 방향 모두 문턱 위에 있으므로 ρ=β\rho = \sqrt\beta 이고, β\beta 가 커질수록 느려집니다. 최적값이 문턱 위에 정확히 앉아 있는 이유가 여기 있습니다 — 그 아래는 느린 방향에 발목이 잡히고, 그 위는 β\sqrt\beta 가 커집니다.

코드로 확인하기

세 방법의 스텝 수

H=diag(100,1)H = \mathrm{diag}(100, 1), 즉 κ=100\kappa = 100 인 골짜기에서 셋을 나란히 돌립니다. 각자 자기 최적 파라미터를 씁니다.

import numpy as np, math

H = np.diag([100.0, 1.0]); kappa = 100.0; sk = math.sqrt(kappa)

eta_gd  = 2 / (kappa + 1)              # 0.0198
eta_mom = 4 / (sk + 1) ** 2            # 0.0331
beta_mom = ((sk - 1) / (sk + 1)) ** 2  # 0.6694
eta_nag  = 1 / 100.0                   # 0.0100  = 1/λmax
beta_nag = (sk - 1) / (sk + 1)         # 0.8182

def run(kind, n=4000):
    x, v, out = np.array([1.0, 1.0]), np.zeros(2), []
    for _ in range(n):
        g = H @ x
        if kind == "gd":
            x = x - eta_gd * g
        elif kind == "mom":
            v = beta_mom * v + g                  # EMA (스케일을 뗀 형태)
            x = x - eta_mom * v
        else:
            v = beta_nag * v + g
            x = x - eta_nag * (g + beta_nag * v)  # 좌표를 옮겨 적은 네스테로프
        out.append(np.linalg.norm(x))
    return out

seq = {k: run(k) for k in ("gd", "mom", "nag")}

def first_below(s, th):
    return next(i + 1 for i, v in enumerate(s) if v < th)

for th in (1e-3, 1e-6, 1e-9):
    a, b, c = (first_below(seq[k], th) for k in ("gd", "mom", "nag"))
    print(f"‖x‖ < {th:g}:  경사하강 {a:4d}   모멘텀 {b:3d}   네스테로프 {c:3d}")

# ‖x‖ < 0.001:  경사하강  363   모멘텀  58   네스테로프  87
# ‖x‖ < 1e-06:  경사하강  709   모멘텀  95   네스테로프 157
# ‖x‖ < 1e-09:  경사하강 1054   모멘텀 131   네스테로프 226

세 줄이 앞의 표와 맞습니다. 모멘텀 대 경사하강의 배율이 6.3 → 7.5 → 8.0으로 올라가는데, 표의 10배는 점근적인 값이고 여기에는 초반의 과도 구간이 섞여 있어 아직 못 미칩니다. 목표를 낮출수록 점근값에 가까워지는 것이 그 증거입니다.

네스테로프가 이차함수에서 폴랴크보다 느린 것도 표대로입니다. ρ\rho 가 0.9 대 0.818이므로 스텝 수의 비가 log⁡0.818/log⁡0.9=1.9\log 0.818/\log 0.9 = 1.9 쯤 되어야 하고, 실제로 226 대 131이 그 비율입니다. eta_nag에 eta_mom을 넣어 보면 발산합니다 — 앞에서 적은 안정 상한 0.0143을 두 배 넘게 넘긴 값이기 때문입니다.

행렬의 스펙트럼 반지름도 직접 확인해 둡니다.

for lam in (100.0, 1.0):
    M = np.array([[1 - eta_mom * lam, -eta_mom * beta_mom],
                  [lam,                beta_mom          ]])
    ev = np.linalg.eigvals(M)
    print(f"λ={lam:5.1f}  문턱 {(1-math.sqrt(eta_mom*lam))**2:.5f}"
          f"   ρ(M) = {max(abs(ev)):.4f}")

# λ=100.0  문턱 0.66942   ρ(M) = 0.8182
# λ=  1.0  문턱 0.66942   ρ(M) = 0.8182

문턱 둘이 같고 그 값이 beta_mom과 같으며, 두 방향의 ρ\rho 가 0.66942\sqrt{0.66942} 로 일치합니다. 그리고 그 공통값 0.81820.8182 는 조건수 10짜리 문제를 경사하강으로 푸는 속도와 같습니다. 100=10\sqrt{100} = 10 이 그대로 나타난 자리입니다.

잡음이 섞이면

앞에서 유효 학습률이 η/(1−β)\eta/(1-\beta) 라고 적었습니다. 잡음이 있을 때 파라미터가 떠는 폭으로 재 봅니다. f=12x2f = \tfrac12 x^2 에 표준편차 1의 잡음을 섞고, 정착한 뒤의 제곱평균제곱근을 셋에서 비교합니다.

rng = np.random.default_rng(0)

def noisy(eta, beta, n=400_000, burn=20_000):
    x, v, s = 0.0, 0.0, 0.0
    for t in range(n):
        g = x + rng.normal()
        v = beta * v + g
        x = x - eta * v
        if t >= burn:
            s += x * x
    return math.sqrt(s / (n - burn))

print(round(noisy(0.01, 0.9), 4))   # 0.2236  모멘텀, 유효 학습률 0.1
print(round(noisy(0.10, 0.0), 4))   # 0.2300  경사하강, 학습률 0.1
print(round(noisy(0.01, 0.0), 4))   # 0.0698  경사하강, 학습률 0.01

첫 줄과 둘째 줄이 0.224와 0.230으로 거의 같고, 셋째 줄만 3배 조용합니다. 모멘텀의 잡음 바닥은 자기 η\eta 가 아니라 η/(1−β)\eta/(1-\beta) 짜리 경사하강 쪽에 섭니다. 그래디언트를 평균 내어 잡음을 줄였는데 왜 조용해지지 않는가 — 줄인 만큼 걸음이 커져 상쇄되기 때문입니다. 모멘텀은 잡음을 없애 주는 장치가 아니라 방향을 골라 주는 장치입니다.

β를 최적값 위로 올리면

마지막으로 문턱 위쪽을 봅니다. η\eta 를 η⋆\eta^\star 로 고정하고 β\beta 만 올려 400스텝 뒤의 크기와, 완만한 좌표의 부호가 몇 번 뒤집혔는지를 셉니다.

def sweep(beta, n=400):
    x, v, flips, prev = np.array([1.0, 1.0]), np.zeros(2), 0, 1.0
    for _ in range(n):
        v = beta * v + H @ x
        x = x - eta_mom * v
        if x[1] * prev < 0:
            flips += 1
        prev = x[1]
    return np.linalg.norm(x), flips

for b in (0.66942, 0.80, 0.90):
    nrm, f = sweep(b)
    print(f"β={b:.5f}  √β={math.sqrt(b):.4f}  ‖x‖={nrm:.2e}  부호 뒤집힘 {f:2d}회")

# β=0.66942  √β=0.8182  ‖x‖=1.12e-32  부호 뒤집힘  0회
# β=0.80000  √β=0.8944  ‖x‖=1.18e-19  부호 뒤집힘 20회
# β=0.90000  √β=0.9487  ‖x‖=8.97e-10  부호 뒤집힘 23회

읽을 것이 둘입니다. 첫째, β\beta 를 올리자 수렴이 느려졌습니다 — 400스텝 뒤의 크기가 10−3210^{-32} 에서 10−1010^{-10} 으로 올라갔습니다. 실측 비율을 재면 (1.18×10−19)1/400=0.897(1.18\times10^{-19})^{1/400} = 0.897 로 0.8=0.894\sqrt{0.8} = 0.894 와 맞고, β=0.9\beta = 0.9 에서는 0.9490.949 로 0.9=0.949\sqrt{0.9} = 0.949 와 맞습니다. 문턱 위에서 ρ=β\rho = \sqrt\beta 라는 계산이 그대로 나옵니다.

둘째, 최적값에서는 부호가 한 번도 안 뒤집히는데 위로 올리면 20번 넘게 뒤집힙니다. 복소수 고윳값이 뜻하는 회전이 궤적에 그대로 나타난 것이고, 「모멘텀을 올렸는데 손실이 출렁인다」는 관찰의 정체가 이것입니다. β\beta 가 문턱을 넘어 완만한 방향까지 복소 구간으로 들어간 것입니다.

정리

  • 지수이동평균 vt=βvt−1+(1−β)gtv_t = \beta v_{t-1} + (1-\beta)g_t 를 펼치면 kk 스텝 전 값의 가중치가 (1−β)βk(1-\beta)\beta^k 인 가중평균이다. 가중치의 합은 1−βt1-\beta^t 이라 초반에는 1에 못 미친다.
  • 창은 1/(1−β)1/(1-\beta), 유효 표본 수는 (1+β)/(1−β)(1+\beta)/(1-\beta), 평균 지연은 β/(1−β)\beta/(1-\beta) 다. 두 셈이 두 배 차이 나는 것은 재는 것이 다르기 때문이다 — 앞은 「얼마나 오래된 것까지 보나」, 뒤는 「잡음이 얼마나 줄었나」다.
  • 잡음의 분산은 ESS로 나뉘어 σ2(1−β)/(1+β)\sigma^2(1-\beta)/(1+\beta) 가 된다. 표준편차는 제곱근으로만 줄어서, 창을 100배 늘려도 잡음은 10배밖에 안 준다. 평활의 이득과 지연의 비용이 같은 손잡이의 양 끝이다.
  • 초기 편향 βt\beta^t 는 세 자리에서 문제가 되는 정도가 다르다. 수천 스텝 도는 EMA 체크포인트는 저절로 사라지고 Adam의 첫 몇 스텝은 반드시 보정해야 한다. v1=g1v_1 = g_1 으로 두면 편향은 없어지지만 첫 관측의 가중치가 1/(1−β)1/(1-\beta) 배가 되어 초반이 흔들린다.
  • 모멘텀은 EMA된 그래디언트로 걷는 것이다. 일정한 성분은 1/(1−β)1/(1-\beta) 배, 뒤집히는 성분은 1/(1+β)1/(1+\beta) 배가 되어 둘이 (1+β)/(1−β)(1+\beta)/(1-\beta) 배로 벌어진다 — β=0.9\beta=0.9 에서 19배다. 걸음은 η/(1−β)\eta/(1-\beta) 짜리 경사하강과 같아지고, 잡음 바닥도 그쪽에 선다.
  • 네스테로프는 그래디언트를 관성이 데려갈 자리에서 잰다. 좌표를 옮겨 적으면 버퍼를 더 두지 않고 방향만 g+βvg + \beta v 로 바뀐다. 이차함수에서는 폴랴크가 빠르지만, 일반 볼록·매끄러움에서 O(1/k2)O(1/k^2) 를 보장하는 것은 네스테로프뿐이다.
  • 이차함수에서 모멘텀은 상태 (x,v)(x, v) 의 2×22\times2 행렬 거듭제곱이고, 최적 (η,β)(\eta,\beta) 에서 수렴률이 κ−1κ+1\dfrac{\sqrt\kappa-1}{\sqrt\kappa+1} 이다. 반복 수가 κ\kappa 에서 κ\sqrt\kappa 로 떨어진다 — κ=100\kappa = 100 이면 10배, κ=1000\kappa = 1000 이면 31.6배다.
  • β>(1−ηλ)2\beta > (1-\sqrt{\eta\lambda})^2 이면 고윳값이 복소수가 되어 ρ=β\rho = \sqrt\beta 이고 궤적이 진동한다. 최적 β⋆\beta^\star 는 두 방향의 이 문턱이 만나는 자리라, 그보다 올리면 느려지면서 출렁인다.

여기까지가 손잡이 둘짜리 옵티마이저입니다. 남은 문제는 η\eta 가 모든 파라미터에 하나라는 것입니다 — 어떤 방향은 곡률이 크고 어떤 방향은 작은데 걸음의 크기는 같습니다. 다음 글에서 그래디언트의 1차·2차 모멘트를 각각 EMA로 추정해 방향마다 걸음을 다르게 주는 Adam을 처음부터 조립하고, 여기서 미뤄 둔 편향 보정 1/(1−βt)1/(1-\beta^t) 도 그때 유도합니다.


읽어주셔서 감사합니다. 😊

LATEST

수학의 최신 글

수학2026.09.07

양자화 오차: 격자 사상, 오차 분산, 이상치 채널

실수를 2^b개 격자에 사상할 때 오차의 분산이 왜 Δ²/12인지 유도하고, 그것이 비트당 6.02dB라는 SNR로 번역되는 과정을 실측과 대조했습니다. 이상치 하나가 나머지 값의 유효 비트를 어떻게 먹는지, 그리고 int4에서 성능이 무너지는 지점을 오차 예산으로 미리 계산하는 법까지.

중급18 MIN
수학2026.09.07

수치적으로 안정한 계산 패턴 모음

최댓값 빼기, 로그 공간, log1p·expm1, 분산의 두 공식, 정규화의 ε, fp32 누산, 역행렬 대신 solve — 프레임워크가 몰래 해 주는 일곱 가지를 하나씩 꺼내 각각 어떤 고장을 막는지 직접 재 봤습니다. 수식을 그대로 옮긴 코드가 왜 라이브러리보다 나쁜지에 대한 목록입니다.

중급22 MIN
수학2026.09.07

부동소수점은 어디서 새는가: 반올림, 상쇄, 더하는 순서

0.1 + 0.2가 0.3이 아닌 이유부터 시작해 머신 엡실론을 유도하고, 같은 16비트인데 fp16과 bf16이 서로 다른 지점에서 터지는 이유, 비슷한 수를 뺄 때 유효자리가 사라지는 파괴적 상쇄, 그리고 1,000만 개를 순서만 바꿔 더했을 때 오차가 백만 배 갈리는 실험까지 직접 재 봤습니다.

중급23 MIN