GPU를 여덟 장으로 늘리고 배치 크기를 32에서 256으로 올립니다. 학습률은 어떻게 해야 할까요. 그대로 두면 데이터를 여덟 배 빨리 넘기는데 파라미터는 8분의 1만큼만 움직입니다. 여덟 배로 올리면 첫 스텝에서 손실이 발산하는 일이 잦습니다. 실제 설정 파일에는 대개 이런 줄들이 함께 들어 있습니다.
batch_size: 256
lr: 3.0e-4
warmup_steps: 1000
lr_schedule: cosine
지난 글에서 옵티마이저 안쪽은 다 열어 봤습니다. 남은 것은 밖에서 들어오는 값 자체입니다. 그것은 데이터 전체의 그래디언트가 아니라 배치 하나로 잰 추정값이고, 위 네 줄은 전부 그 추정의 오차를 다루는 장치입니다. 이 글은 오차의 크기를 먼저 계산하고 거기서 네 줄을 하나씩 끌어냅니다. 그리고 옵티마이저가 SGD냐 Adam이냐에 따라 같은 질문의 답이 어떻게 갈리는지도 봅니다.
미니배치 그래디언트의 분산
불편 추정량
데이터 개짜리 손실은 표본별 손실의 평균입니다.
배치 는 이 중 개를 무작위로 고른 것이고, 우리가 쓰는 값은 그 평균입니다.
각 가 균등하게 뽑히므로 이고, 따라서
입니다. 평균적으로는 정확하다는 뜻이고, 이렇게 기댓값이 참값과 같은 추정량을 불편 추정량이라고 부릅니다 — 치우침(편향)이 없다는 말입니다. 틀리는 것은 한 번 한 번의 값이 참값 둘레에 흩어지는 폭뿐이고, 이 글의 나머지는 전부 그 폭의 이야기입니다.
벡터의 분산
파라미터가 개면 그래디언트는 차원 벡터이고, 분산도 좌표마다 하나씩 있습니다. 좌표 에서 표본별 그래디언트의 분산을 이라 하면, 이 글에서 쓰는 은 그 합입니다.
오른쪽 식이 말하듯 이 합은 표본 하나의 그래디언트가 참값에서 떨어진 거리의 제곱을 평균한 것이라, 벡터를 한 숫자로 다뤄도 잃는 것이 없습니다. 평균을 내면 분산이 이 된다는 중급 21번 · 분산과 표본 오차의 결과는 좌표마다 성립하고, 좌표를 모두 더해도 그대로 성립합니다.
배치를 4배로 키우면 흩어짐이 절반이 됩니다. 참 그래디언트의 크기와 흩어짐의 크기를 나눈 값을 신호 대 잡음비라고 부르면
이고, 배치를 키워서 얻는 것은 뿐입니다. 계산량은 에 비례해 늘어나는데 정확도는 그 제곱근만큼만 좋아진다는 이 어긋남이 아래 모든 이야기의 뿌리입니다.
유한모집단 보정
위 식은 배치 안의 표본이 서로 독립이라는 가정에 기대고 있습니다. 실제 데이터로더는 한 에폭 안에서 같은 표본을 두 번 뽑지 않는데, 이렇게 복원 없이 뽑으면 표본끼리 약하게 음의 상관이 생겨 분산이 조금 줄어듭니다. 줄어드는 비율이 유한모집단 보정이고 값은 입니다.
데이터 5만 개에 배치 256이면 이 비율이 0.995라서 차이가 0.5%입니다. 극단적으로 데이터 1,000개에서 256개를 뽑으면 0.745까지 내려가고, 실제로 4만 번 뽑아 재 보면 보정 없는 식보다 이 보정을 곱한 값에 맞습니다. 배치가 데이터 전체의 몇 퍼센트도 안 되는 보통의 학습에서는 무시해도 되는 항이고, 이 글도 이후로는 빼고 씁니다. 배치가 데이터 전체가 되면 라 분산이 0이 되는데, 그때는 추정이 아니라 전체 그래디언트 그 자체이기 때문입니다.
분산의 측정
은 이론 속의 기호가 아니라 잴 수 있는 값입니다. 파라미터를 한 자리에 고정해 두고 배치를 여러 번 뽑아 그래디언트를 각각 계산한 뒤, 그 벡터들이 평균에서 떨어진 거리의 제곱을 평균하면 가 나옵니다. 여기에 를 곱하면 입니다. 표본별 그래디언트를 따로 뽑을 수 있는 환경이라면 배치를 로 둔 것과 같으니 더 직접적입니다.
다만 이 값은 파라미터가 어디 있느냐에 따라 달라집니다. 같은 자리에서 잰 여러 번의 그래디언트라야 흩어짐만 담기고, 학습을 돌리면서 스텝마다 뽑은 그래디언트를 모으면 위치가 움직인 효과까지 섞입니다. 이 흩어짐을 대규모 학습 도중에 싸게 재는 방법은 아래 「임계 배치 크기」 절에서 다시 나옵니다.
선형 스케일링 법칙
평균과 분산의 일치
같은 양의 데이터를 볼 때 배치를 배로 하면 스텝 수는 배가 됩니다. 배치 로 스텝 걸은 것과 배치 로 한 스텝 걸은 것이 같아지려면 학습률이 얼마여야 하는지를 직접 맞춰 보면 됩니다.
작은 배치로 스텝 동안 움직인 총량은 — 그 사이 이 거의 변하지 않는다고 두면 —
이고, 평균은 , 분산은 독립인 개가 더해지므로 입니다.
큰 배치 로 한 스텝을 학습률 로 걸으면 평균은 , 분산은 입니다. 평균이 맞으려면 여야 하고, 그때 분산은
로 분산까지 저절로 맞습니다. 하나를 맞추면 다른 하나가 따라오는 것이 이 규칙의 핵심이고, 배치를 배로 하면 학습률도 배로 한다는 이 규칙을 선형 스케일링 법칙이라고 부릅니다.
숫자로 확인해 보겠습니다. 1차원 이차함수에서 배치 32로 8스텝 간 것과 배치 256으로 학습률만 8배 올려 한 스텝 간 것을 20만 번씩 돌렸습니다.
import numpy as np
L, sig, eta, B, k, N = 1.0, 3.0, 0.02, 32, 8, 200000
r = np.random.default_rng(0)
x = np.full(N, 1.0) # 배치 32 로 8 스텝
for _ in range(k):
x -= eta * (L * x + r.normal(0, sig / np.sqrt(B), N))
print(f"배치 {B:<4} × {k}스텝, η={eta:<5} 평균 {x.mean():.4f} 표준편차 {x.std():.4f}")
y = np.full(N, 1.0) # 배치 256 으로 1 스텝, 학습률도 8 배
y -= (k * eta) * (L * y + r.normal(0, sig / np.sqrt(k * B), N))
print(f"배치 {k*B:<4} × 1스텝, η={k*eta:<5} 평균 {y.mean():.4f} 표준편차 {y.std():.4f}")
z = np.full(N, 1.0) # 배치만 키우고 학습률은 그대로
z -= eta * (L * z + r.normal(0, sig / np.sqrt(k * B), N))
print(f"배치 {k*B:<4} × 1스텝, η={eta:<5} 평균 {z.mean():.4f} 표준편차 {z.std():.4f}")
# 배치 32 × 8스텝, η=0.02 평균 0.8507 표준편차 0.0280
# 배치 256 × 1스텝, η=0.16 평균 0.8399 표준편차 0.0300
# 배치 256 × 1스텝, η=0.02 평균 0.9800 표준편차 0.0038
학습률을 함께 올린 두 번째 줄은 첫 줄과 평균도 흩어짐도 거의 같습니다. 학습률을 그대로 둔 세 번째 줄은 로 8분의 1만 움직였습니다 — 계산은 여덟 배를 하고 진전은 8분의 1인 셈입니다.
궤적과 분포
위 실험에서 맞은 것은 20만 번 돌린 결과의 평균과 표준편차였습니다. 한 번의 실행을 놓고 보면 두 방식은 전혀 다른 길을 갑니다. 작은 배치는 걸음마다 새 배치로 그래디언트를 다시 재므로 여덟 번 꺾이며 가고, 큰 배치는 출발점에서 잰 방향 하나로 곧게 한 번 갑니다. 두 번째 파라미터를 하나 더해 평면에 그리면 이 차이가 보입니다.
선형 스케일링 법칙이 맞추는 것은 이 도착점들의 분포, 곧 같은 데이터를 본 뒤 파라미터가 어디쯤에 얼마나 흩어져 있느냐입니다. 매 스텝의 위치를 맞추는 규칙이 아니므로 두 방식을 같은 시드로 돌려 스텝별 파라미터를 비교하면 맞지 않는 것이 정상입니다. 학습의 결과가 결국 이 분포에서 뽑힌 한 점이라는 점에서, 분포가 같으면 두 설정은 같은 학습입니다.
곡률 오차와 상한
이 법칙이 깨지는 자리는 둘입니다. 첫째, 위 출력에서 평균이 0.8507과 0.8399로 완전히 같지는 않습니다. 작은 배치 쪽은 스텝마다 위치가 바뀌면서 그래디언트를 다시 재므로 이고, 큰 배치 쪽은 처음 위치의 그래디언트만 쓰므로 입니다. 유도할 때 " 스텝 동안 이 그대로"라고 둔 자리가 여기서 값을 냅니다. 그림에서 두 구름의 중심이 어긋난 폭이 이것이고, 곡률이 크거나 가 크면 벌어집니다.
둘째, 학습률에는 애초에 상한이 있습니다. 경사하강법의 학습률 상한에서 본 대로 곡률 상수가 이면 을 넘는 순간 발산합니다. 배치를 아무리 키워도 를 그 위로 올릴 수는 없으므로, 선형 스케일링은 언젠가 반드시 멈춥니다.
점진적 워밍업
이 규칙을 대규모로 처음 검증한 것이 Goyal 등(2017)입니다. 모멘텀 SGD로 ResNet-50을 ImageNet에서 학습하면서 배치 256에 학습률 0.1을 기준으로 두고 배치에 비례해 학습률을 올렸고, 배치 8,192까지는 작은 배치와 같은 정확도를 냈습니다. 그보다 큰 배치에서는 정확도가 떨어졌다고 적었는데, 위 두 번째 자리가 실제로 드러난 것입니다.
그들이 함께 짚은 것이 첫 번째 자리입니다. 학습 초반에는 파라미터가 빠르게 바뀌어 " 스텝 동안 그래디언트가 그대로"라는 전제가 가장 크게 어긋납니다. 그래서 처음 5에폭 동안은 학습률을 기준값 에서 까지 매 스텝 같은 폭으로 올렸습니다. 학습률을 처음에 낮게 두었다가 몇 스텝에 걸쳐 목표값까지 올리는 방식을 워밍업이라 하고, 그들은 이렇게 선형으로 올리는 것을 점진적 워밍업이라 불렀습니다. 워밍업이 필요한 다른 이유는 아래 「워밍업」 절에서 이어서 봅니다.
제곱근 스케일링
크기가 약분되는 갱신
선형 스케일링의 유도는 SGD의 갱신 에 기대고 있었습니다. 걸음이 그래디언트에 그대로 비례하니 평균도 분산도 를 따라갔습니다. Adam은 다릅니다. 모멘텀을 잠시 빼고(지난 글의 , RMSprop과 같은 꼴) 좌표 하나만 보면 갱신은
이고, 분모가 그래디언트의 크기를 재서 나누므로 그래디언트 전체를 몇 배 해도 걸음이 변하지 않습니다. 지난 글에서 스케일 불변이라고 부른 성질입니다. 그러면 배치를 키워 잡음이 줄 때 걸음에 무슨 일이 생기는지도 SGD와 달라집니다.
이 좌표에서 표본 하나의 분산을 이라 두면 입니다. 잡음이 신호보다 훨씬 큰 구간, 곧 인 구간에서는 뒷항이 이기므로
입니다. 배치가 커질수록 분모가 작아지고, 같은 에서도 걸음이 에 비례해 커집니다. SGD에서는 배치를 키워도 걸음의 평균이 그대로였는데, Adam에서는 배치만 키워도 평균 걸음이 저절로 늘어납니다.
√k의 유도
선형 스케일링과 같은 방식으로 맞춰 봅니다. 한 걸음의 평균은 , 분산은 입니다. 분산이 배치와 무관하게 인 것이 SGD와 가장 크게 다른 점입니다.
- 작은 배치 로 스텝: 평균 , 분산
- 큰 배치 로 학습률 한 스텝: 평균 , 분산
평균을 맞추면 , 곧 이고, 그때 분산 도 왼쪽과 같아집니다. 이번에도 하나를 맞추면 다른 하나가 따라옵니다. 분자의 평균이 에 비례해 커지는 몫이 이미 배를 채워 주고, 학습률은 나머지 배만 채우면 됩니다. 배치를 배로 할 때 학습률을 배로 하는 이 규칙이 제곱근 스케일링 규칙입니다.
Malladi 등(2022)은 Adam과 RMSprop의 학습 과정을 잡음이 섞인 연속 시간 방정식으로 근사해서 같은 규칙을 끌어냈습니다. 그들의 규칙은 배치를 배로 할 때 학습률을 배로 하는 데 더해 과 를 배, 을 배로 바꿉니다. 이동 평균의 창은 스텝 수로 재므로, 스텝이 로 줄면 창도 그만큼 좁혀야 같은 양의 데이터를 평균합니다. 은 가 배로 작아진 만큼 함께 줄여야 분모에서 차지하는 몫이 그대로입니다.
규칙의 선택
시뮬레이션으로 확인해 봅니다. 위와 같은 1차원 이차함수에 RMSprop을 돌리되, 작은 배치는 학습률 로 스텝, 큰 배치는 스텝을 걷게 해서 같은 양의 데이터를 본 자리에서 비교합니다. 큰 배치 쪽은 를 8배로 했고, 출발점을 두 곳으로 달리했습니다.
import numpy as np
L, sig, B, k, C = 1.0, 3.0, 32, 8, 20000
def rmsprop(eta, batch, steps, beta2, x0, seed=0):
r = np.random.default_rng(seed)
x, v = np.full(C, x0), np.zeros(C)
for t in range(1, steps + 1):
g = L * x + r.normal(0, sig / np.sqrt(batch), C)
v = beta2 * v + (1 - beta2) * g * g
x -= eta * g / np.sqrt(v / (1 - beta2**t))
return x.mean(), x.std()
eta, b2 = 0.001, 0.999
for x0 in (0.1, 1.0):
for T in (100, 1000):
rows = [("배치 32, η", rmsprop(eta, B, k * T, b2, x0)),
("배치 256, √8η", rmsprop(eta * np.sqrt(k), k * B, T, 1 - k * (1 - b2), x0)),
("배치 256, 8η", rmsprop(eta * k, k * B, T, 1 - k * (1 - b2), x0))]
print(f"x0={x0} 큰 배치 {T}스텝:", " ".join(f"{n} {m:+.4f}±{s:.4f}" for n, (m, s) in rows))
# x0=0.1 큰 배치 100스텝: 배치 32, η +0.0224±0.0158 배치 256, √8η +0.0246±0.0151 배치 256, 8η +0.0013±0.0271
# x0=0.1 큰 배치 1000스텝: 배치 32, η +0.0002±0.0164 배치 256, √8η -0.0001±0.0163 배치 256, 8η -0.0001±0.0276
# x0=1.0 큰 배치 100스텝: 배치 32, η +0.4427±0.0105 배치 256, √8η +0.7409±0.0046 배치 256, 8η +0.3774±0.0126
# x0=1.0 큰 배치 1000스텝: 배치 32, η +0.0002±0.0164 배치 256, √8η -0.0001±0.0161 배치 256, 8η -0.0001±0.0275
에서 출발하면 참 그래디언트가 0.1이고 배치 256의 잡음이 라 처음부터 잡음이 이깁니다. 여기서는 배가 작은 배치와 평균도 흩어짐도 맞고, SGD처럼 8배를 하면 끝에 앉는 흩어짐이 0.0276으로 1.7배 커집니다. 에서 출발하면 초반에는 신호 1이 잡음 0.19를 이겨 가 되고, 갱신이 부호만 남긴 에 가까워집니다. 그러면 걸음이 배치와 무관해져 오히려 8배 쪽이 작은 배치(0.44)에 더 가깝고 배는 0.74로 뒤처집니다. 둘 다 최적점 근처에 앉은 1,000스텝 뒤에는 다시 배가 맞습니다.
그래서 어느 규칙을 쓸지는 외울 것이 아니라 옵티마이저의 갱신 식을 보고 정합니다. 걸음이 그래디언트에 비례하면(SGD, 모멘텀 SGD) 선형이고, 그래디언트의 크기를 재서 나누면(Adam, RMSprop) 제곱근입니다. 그리고 둘 다 잡음이 신호보다 클 때의 규칙이라는 전제가 같습니다. 그 전제가 언제 깨지는지를 재는 값이 다음 절의 주제입니다.
임계 배치 크기
진전의 포화
선형 스케일링이 멈추는 지점을 계산으로 찾을 수 있습니다. 한 스텝이 손실을 얼마나 줄이는지를 2차 근사로 적으면
이고, 입니다(평균의 제곱에 분산을 더한 것). 우변을 에 대한 이차식으로 보고 최소로 만드는 를 구하면
분모에 있는 을 이라 쓰면 스텝당 진전이 에 비례합니다. 이 값을 임계 배치 크기라고 부릅니다.
- : 진전이 에 거의 비례한다. 배치를 두 배로 하면 스텝당 두 배 나아가므로 총 스텝 수가 절반이 된다. 선형 스케일링이 통하는 구간이 여기다.
- : 진전이 에 붙어 더 안 는다. 배치를 두 배로 해도 스텝 수는 그대로이고 계산량만 두 배가 된다.
- : 한계값의 정확히 절반. 계산 효율과 스텝 수를 맞바꾸는 지점이 여기다.
을 풀어 쓰면 , 곧 앞 절에서 두 규칙의 전제로 둔 "잡음이 신호보다 크다"와 같은 말입니다. 앞 절의 시뮬레이션에 대 보면 에서는 이라 배치 256이 그 아래이고, 에서는 라 배치 32부터 이미 위였습니다.
McCandlish 등(2018)은 이 양을 이라 부릅니다. 곡률을 숫자 하나 로 둔 판이고, 곡률을 행렬로 두어 방향마다 다르게 센 의 간단한 근사입니다. 그들은 여러 과제에서 배치를 키워도 빨라지지 않기 시작하는 지점이 이 잡음 크기로 대략 예측된다고 보고했습니다.
두 배치로 재기
을 재려면 과 을 따로 알아야 하는데, 둘 다 직접 볼 수 없는 값입니다. 볼 수 있는 것은 미니배치 그래디언트의 노름 제곱 이고, 그 기댓값이 위에서 쓴 식입니다. 배치 크기를 두 가지로 재면 미지수 둘에 식이 둘이 됩니다.
두 식을 빼면 이, 를 곱해 빼면 이 나옵니다. 예를 들어 배치 32에서 0.665, 배치 512에서 0.0790625가 나왔다면 , 이고 입니다. 이 자리에서 배치를 512로 쓰면 스텝당 진전이 한계값의 배입니다.
McCandlish 등이 제안한 방법이 이것이고, 데이터 병렬 학습에서는 두 배치가 공짜로 생깁니다. 장치 하나가 계산한 그래디언트가 작은 배치이고, 모든 장치의 것을 평균한 값이 큰 배치입니다. 한 스텝의 값은 흔들리므로 여러 스텝에 걸쳐 두 노름 제곱을 각각 평균한 뒤 식에 넣습니다.
학습 중 변화
은 잡음이 신호보다 몇 배 큰가입니다. 학습 초반에는 이 커서 이 작고, 후반으로 갈수록 이 0에 가까워지므로 이 커집니다. McCandlish 등도 학습이 진행되며 손실이 내려갈수록 잡음 크기가 커지는 것을 관찰했습니다.
그러면 한 학습 안에서도 알맞은 배치가 변합니다. 처음부터 큰 배치를 쓰면 초반에는 이라 계산을 버리고, 끝까지 작은 배치를 쓰면 후반에는 이라 스텝을 버립니다. 큰 모델 학습에서 작은 배치로 시작해 도중에 배치를 키워 가는 관행이 여기서 나옵니다. 배치를 키우는 시점을 정할 때 위 두 배치 측정값이 그대로 기준이 됩니다.
그래디언트 누적
메모리가 모자라 배치 256이 한 번에 안 올라가면 배치 32를 여덟 번 돌려 그래디언트를 더해 두었다가 한 번만 갱신합니다. 이것을 그래디언트 누적이라 합니다. 여덟 개의 배치 평균을 다시 평균하면
로 배치 256의 그래디언트와 정확히 같은 값입니다. 여덟 번 모두 같은 파라미터에서 계산하므로 곡률 오차도 끼지 않습니다. 그러니 누적은 같은 를 만드는 다른 방법일 뿐이고, 이 글의 계산에서는 배치 256과 구별되지 않습니다. 학습률도 배치 256에 맞춰 잡습니다.
달라지는 것은 비용뿐입니다. 메모리는 배치 32만큼 쓰는 대신 한 번의 갱신에 여덟 번의 순전파·역전파를 차례로 돌리므로 시간이 걸리고, 데이터 병렬이면 장치 사이의 그래디언트 평균을 여덟 번이 아니라 한 번만 하므로 통신량은 줄어듭니다. 예외가 하나 있는데, 배치 안의 통계를 쓰는 배치 정규화는 그 통계를 32개씩 따로 내므로 256개로 잰 것과 달라집니다.
워밍업
초반의 곡률
워밍업은 대개 를 0에서 까지 몇백~몇천 스텝에 걸쳐 올리는 것입니다. 가장 흔한 꼴은 선형입니다.
이유가 둘 있고 둘 다 앞에서 계산한 것입니다. 첫째는 곡률입니다. 초기화 근처는 곡률이 크고 그 값을 모릅니다. 상한 의 은 우리가 미리 알 수 없는 값이고, 학습이 진행되며 대개 작아집니다. 초반의 큰 에 맞춘 를 끝까지 쓰면 너무 작고, 후반에 맞춘 를 처음부터 쓰면 발산합니다. 워밍업은 "작게 시작해서 버티는 만큼 올린다"를 스케줄로 박아 둔 것이고, 선형 스케일링으로 올린 큰 학습률일수록 이 구간이 중요합니다. Goyal 등이 워밍업을 넣은 것도 초반의 이 불안정 때문이었습니다.
분모의 흩어짐
둘째는 Adam의 가 아직 믿을 만하지 않다는 것입니다. 지난 글에서 편향 보정이 의 치우침은 없애 준다는 것을 봤지만, 보정이 없애는 것은 평균의 치우침뿐이고 추정 자체의 흩어짐은 그대로입니다. 첫 스텝에서는 , 이라 갱신이 정확히 가 됩니다. 그래디언트가 크든 작든 모든 파라미터가 학습률만큼 한꺼번에 움직입니다.
그래디언트가 순수한 잡음인 좌표를 10만 개 두고 , 로 재 보면 이렇습니다.
| 스텝 | 의 크기(제곱평균) | 가 가장 작은 1% 좌표 | 그 좌표의 걸음 배수 |
|---|---|---|---|
| 1 | 1.00 | 참값의 0.01배 | — |
| 10 | 0.33 | 0.51배 | 1.98배 |
| 100 | 0.23 | 0.84배 | 1.20배 |
| 1,000 | 0.23 | 0.95배 | 1.06배 |
자리를 잡은 뒤의 갱신 크기는 0.23인데 첫 스텝은 1.00으로 4배가 넘습니다. 둘째 열이 먼저 가라앉는 것은 분자의 창(, 10스텝)이 빨리 차기 때문이고, 분모는 더 느립니다. 10스텝째에도 100개 중 1개 좌표는 가 참값의 절반쯤으로 우연히 작게 나와 그 방향의 걸음이 두 배가 됩니다. 나눗셈이 분모에 있는 값의 잡음을 증폭하는 자리라서, 표본이 쌓일 때까지 를 낮춰 두는 것이 안전합니다.
워밍업 길이
마지막 열이 1에 붙는 속도는 의 창이 정합니다. 의 유효 창은 스텝이고, 위 표에서도 1,000스텝쯤 지나야 분모의 흩어짐이 몇 퍼센트로 줄었습니다. 워밍업을 흔히 1,000스텝 안팎으로 잡는 것은 이 창과 길이를 맞춘 감각이라 볼 수 있습니다. 를 바꾸면 워밍업 길이도 같이 옮기는 것이 자연스럽습니다.
올리는 모양도 둘로 갈립니다. 선형은 에서 정확히 에 닿고 끝이 분명합니다. 지수형 는 처음 기울기가 인 선형과 같지만 끝으로 갈수록 완만해져서, 에서 63%, 에서 95%에 닿습니다. 분모의 흩어짐이 줄어드는 모양 자체가 지수에 가까워 그 곡선을 그대로 따라가는 셈이고, 대신 언제 에 다다랐다고 볼지가 흐려집니다.
워밍업 생략
워밍업 없이도 버티는 설정이 있습니다. 를 0.99로 낮추면 창이 100스텝이라 분모가 열 배 빨리 자리를 잡습니다. 대가는 그 뒤로도 분모가 매 스텝 더 크게 흔들린다는 것이고, 그래디언트가 가끔 크게 튀는 과제에서는 그 흔들림이 그대로 걸음에 실립니다. 을 키우는 길도 있습니다. 지난 글에서 본 대로 걸음의 상한이 이라 분모가 우연히 작아도 걸음이 그 위로 못 나갑니다. 대가는 이 보다 커지는 좌표에서 Adam이 모멘텀 SGD 쪽으로 미끄러져 좌표마다 걸음을 맞추는 이점을 잃는다는 것입니다. 워밍업은 이 두 대가를 치르지 않고 초반만 조심하는 방법이라 널리 쓰입니다.
잡음 바닥과 감쇠
잡음 바닥
학습 후반에 학습률을 줄이는 이유는 "이제 다 왔으니 조심스럽게"가 아니라 계산할 수 있는 값입니다. 1차원 이차함수 에서 미니배치 그래디언트가 , 라 하면
입니다. 양변의 분산을 잡고 정상 상태 를 두면
이고, 손실로 옮기면 입니다. 이 작을 때 이 값은 대략 로 에 비례합니다.
최적점에 도착해도 손실이 0으로 가지 않고 이 값에서 멈춥니다. 그래디언트의 잡음이 파라미터를 계속 흔들기 때문이고, 이 높이를 잡음 바닥이라고 부릅시다.
import numpy as np
L, sig, B = 1.0, 3.0, 32
def run(eta_fn, steps=4000, x0=5.0, seed=0):
r = np.random.default_rng(seed)
x, tail = x0, []
for t in range(1, steps + 1):
g = L * x + r.normal(0, sig / np.sqrt(B)) # 미니배치 그래디언트
x -= eta_fn(t) * g
if t > steps * 0.9:
tail.append(0.5 * L * x * x)
return float(np.mean(tail))
for eta in (0.2, 0.1, 0.05, 0.025):
m = np.mean([run(lambda t, e=eta: e, seed=s) for s in range(40)])
pred = eta * sig**2 / (2 * B * (2 - eta * L))
print(f"상수 η={eta:<6} 잡음 바닥 {m:.5f} 식이 주는 값 {pred:.5f}")
T = 4000
m = np.mean([run(lambda t: 0.1 * (1 + np.cos(np.pi * t / T)), seed=s) for s in range(40)])
print(f"코사인 0.2 → 0 잡음 바닥 {m:.5f}")
# 상수 η=0.2 잡음 바닥 0.01595 식이 주는 값 0.01562
# 상수 η=0.1 잡음 바닥 0.00755 식이 주는 값 0.00740
# 상수 η=0.05 잡음 바닥 0.00368 식이 주는 값 0.00361
# 상수 η=0.025 잡음 바닥 0.00185 식이 주는 값 0.00178
# 코사인 0.2 → 0 잡음 바닥 0.00039
학습률을 반으로 줄일 때마다 바닥이 거의 정확히 반이 되고, 측정값은 식이 주는 값과 몇 퍼센트 안에서 맞습니다. 그리고 에서 시작해 코사인 곡선의 반 주기를 따라 까지 내려오는 코사인 감쇠는 상수 보다 41배 낮은 바닥에 도착합니다. 처음부터 로 갔다면 바닥은 낮지만 거기까지 가는 데 훨씬 오래 걸립니다 — 스케줄은 "빨리 가는 구간"과 "낮게 앉는 구간"을 한 번의 학습 안에 이어 붙이는 장치입니다.
η/B 비율
바닥의 근사식 에서 와 는 따로 나오지 않고 라는 비율로만 나옵니다. 바닥을 내리는 방법이 를 키우거나 를 줄이는 둘뿐인데, 식 안에서 둘은 같은 손잡이의 두 이름입니다. 배치를 키우는 것은 비싸고 학습률을 줄이는 것은 공짜라서 감쇠가 기본이 되었을 뿐입니다.
거꾸로 배치와 학습률을 함께 두 배로 하면 비율이 그대로이니 바닥도 그대로여야 합니다. 정확한 식에 넣어 보면 , 에서 0.00361이고, , 에서 0.00370입니다. 2.6% 차이는 분모의 에서 오고, 이 작을수록 사라집니다. 이것은 선형 스케일링 법칙을 끝 쪽에서 다시 본 것이기도 합니다. 스텝치 이동의 분산이 맞았으니, 오래 돌려 앉는 자리의 흩어짐도 맞는 것이 당연합니다.
코사인과 역제곱근
첫 식의 코사인 감쇠는 총 스텝 수 를 미리 알아야 합니다. 대신 초반에는 천천히 떨어지고 중반에 가장 가파르며 끝에서 다시 완만해져서, 앞의 절반을 큰 학습률로 벌고 마지막에 바닥까지 앉는 모양이 됩니다. 를 정해 두고 도는 학습에는 이쪽이 맞습니다.
둘째 식의 역제곱근 감쇠는 를 몰라도 됩니다. 언제 멈춰도 그 시점까지의 스케줄이 말이 되므로 학습을 이어 붙이거나 도중에 끊을 수 있고, 트랜스포머 원논문도 워밍업에 이어 이 식을 썼습니다. 다만 후반에 잘 안 내려가서 — 가 100배가 되어야 가 10분의 1입니다 — 마지막 바닥은 코사인보다 높습니다.
상수 후 감쇠
두 식의 장단을 합친 것이 워밍업 뒤 학습률을 상수로 오래 두고 끝의 짧은 구간에서만 내리는 스케줄입니다. 상수 구간에서는 가 필요 없고, 멈추기로 정한 뒤에야 마지막 감쇠 구간의 길이를 정합니다. 잡음 바닥의 식으로 보면 이해가 쉽습니다. 상수 구간은 높은 바닥에 앉은 채 빨리 이동하는 구간이고, 감쇠 구간이 그 바닥을 에 비례해 끌어내립니다. 위 실험에서 코사인이 41배 낮은 바닥에 닿은 것도 대부분 끝부분에서 가 0으로 가는 동안 생긴 일입니다.
이 모양의 이점은 도중의 체크포인트에서 감쇠를 여러 갈래로 떼어 낼 수 있다는 것입니다. 상수 구간의 한 지점에서 감쇠를 붙여 끝낸 모델과, 같은 지점에서 상수를 더 이어 간 뒤 감쇠한 모델을 한 번의 학습에서 모두 얻습니다. 코사인은 를 바꾸면 처음부터 곡선 전체가 바뀌므로 이것이 안 됩니다. 다만 상수 구간의 파라미터는 높은 바닥에서 흔들리고 있으므로, 그 구간의 손실만 보고 모델을 비교하면 감쇠 뒤의 순서와 다를 수 있습니다.
정리
- 미니배치 그래디언트는 불편 추정량이고 그 분산은 다. 은 좌표별 분산의 합이고, 같은 자리에서 배치를 여러 번 뽑아 잴 수 있다. 신호 대 잡음비는 로, 계산을 배 해서 정확도는 배만 얻는다.
- SGD에서 배치를 배로 하고 학습률도 배로 하면 스텝치 이동의 평균과 분산이 동시에 맞는다 — 선형 스케일링 법칙. 맞추는 것은 도착점의 분포이지 매 스텝의 위치가 아니고, 곡률이 크면 와 의 차이만큼 어긋난다.
- Adam은 그래디언트의 크기를 나누어 없애므로 큰 배치의 걸음이 이미 배 커져 있다. 학습률은 배만 올린다 — 제곱근 스케일링 규칙. 두 규칙 모두 잡음이 신호보다 큰 구간의 것이다.
- 학습률에는 이라는 상한이 있으므로 스케일링은 반드시 멈춘다. 멈추는 지점이 임계 배치 크기 이고, 두 배치 크기의 로 잴 수 있으며 학습이 진행될수록 커진다. 그래디언트 누적은 같은 를 만드는 다른 방법일 뿐이다.
- 워밍업은 초기화 근처의 큰 곡률과, 표본이 몇 개 안 쌓인 의 흩어짐을 지나가는 구간이다. 분모가 자리를 잡는 속도는 의 창이 정한다.
- 감쇠는 잡음 바닥 를 끌어내린다. 바닥은 로 정해져 학습률을 반으로 줄이면 바닥도 반이 된다. 코사인은 총 스텝 수를 알아야 하고, 역제곱근은 몰라도 되는 대신 후반이 덜 내려가며, 상수 뒤 끝에서만 내리는 스케줄은 그 문제를 피한다.
여기까지가 학습을 어떻게 굴릴 것인가입니다. 그런데 워밍업이 필요한 이유 중 하나로 "초기화 근처는 곡률이 크다"를 그냥 두고 왔습니다. 애초에 그 출발점의 값을 무엇으로 놓느냐는 따로 계산할 수 있는 문제입니다. 다음 글에서 층을 하나 통과할 때 활성값의 분산이 어떻게 변하는지 유도하고, 그 분산이 유지되는 조건에서 Xavier와 He 초기화를 끌어냅니다.
읽어주셔서 감사합니다. 😊

