미니배치는 왜 더 시끄러운데 더 빨리 도착하나
2편의 경사하강법은 데이터 전부를 보고 기울기를 구했다. 데이터가 네 개였으니 그래도 됐다. 백만 개라면 한 걸음마다 백만 개를 훑어야 한다.
미니배치는 그 자리에서 타협한다. 일부만 보고 기울기를 어림한 뒤 바로 걷는다. 어림이니 방향이 틀린다. 그런데 결과는 더 빨리 도착한다. 왜 그런지 재 보자.
재료
점 2048개짜리 직선 맞추기다. x 의 평균을 0이 아닌 곳에 두면 2편에서 본
길쭉한 등고선이 나온다.
import numpy as np
rng = np.random.default_rng(0)
n = 2048
x = rng.normal(2.0, 1.0, n)
y = 1.9 * x + 0.05 + 0.3 * rng.standard_normal(n)
X = np.stack([x, np.ones(n)], 1) # 파라미터는 w 와 b 둘
loss = lambda p: np.mean((X @ p - y) ** 2)
grad = lambda p, i: 2 * X[i].T @ (X[i] @ p - y[i]) / len(i)
손실이 2차식이라 헤세 행렬이 상수다. 2편에서 한 대로 고윳값을 보면 문제의 모양과 학습률 상한이 한 번에 나온다.
헤세 고윳값 0.3507 과 11.4379 조건수 32.6
학습률 상한 2 / 11.4379 = 0.1749
최소 손실 0.0885 (노이즈 0.3^2 = 0.09)
노이즈를 0.3 으로 얹었으니 아무리 잘 맞춰도 손실이 0.09 근처 아래로는
못 간다. 실제 최소가 0.0885 다. 이 선을 기억해 두자.
어림이 얼마나 틀리나
배치로 구한 기울기가 전체로 구한 기울기와 얼마나 다른지 잰다. 시작점에서 20000번 뽑아 평균한 상대오차다.
배치 8 상대오차 0.2302
배치 32 상대오차 0.1140
배치 256 상대오차 0.0380
배치 2048 상대오차 0.0000 (전체이므로 정의상 0)
배치를 키우면 오차가 그 제곱근에 반비례해 줄어든다. 정확한 법칙은
sqrt((1/B)(n-B)/(n-1)) 이고, 흔히 쓰는 1/sqrt(B) 는 배치가 전체에 비해
아주 작을 때의 근사다. 여기서는 배치 256이 전체의 8분의 1이라 그 차이가 보인다.
비 8/32 측정 2.020 예측 2.012
비 32/256 측정 2.997 예측 3.000 1/sqrt(B) 만 쓰면 2.828
두 번째 줄이 요점이다. 정확한 법칙은 3.000 을 부르고 측정이 2.997 인데,
1/sqrt(B) 근사는 2.828 을 부른다. 배치 2048의 오차가 0인 것도 같은 식의
끝점이다 - 전체를 뽑으면 1/n - 1/n = 0 이다.
여기까지만 보면 미니배치는 그냥 나쁜 근사다.
같은 데이터를 한 번 보는 동안
관건은 무엇을 기준으로 비교하느냐다. 걸음 수로 비교하면 전체 배치가 이긴다. 매 걸음이 정확하니까. 그런데 걸음 하나의 값이 다르다. 전체 배치는 한 걸음에 2048개를 읽고, 배치 32는 32개를 읽는다.
같은 값을 치르는 단위, 즉 데이터를 한 번 다 보는 에포크로 맞춰서 비교한다.
전체 배치에는 유리한 학습률을 따로 줬다. 0.005 부터 상한까지 훑어 12에포크
손실이 가장 낮은 값을 골랐고, 0.14462 가 나왔다. 상한의 83%다. 작은 배치에 쓴
0.02 를 그대로 주면 불공정한 비교가 된다.
설정 1에포크 3에포크 12에포크 에포크당 걸음
전체 배치 lr 0.02 10.8950 3.9688 0.2026 1
전체 배치 lr 0.14462 7.8806 1.5682 0.1159 1
배치 256 lr 0.02 0.4545 0.1555 0.1128 8
배치 32 lr 0.02 0.1288 0.0951 0.0887 64
배치 8 lr 0.02 0.0914 0.0886 0.0886 256
최소 0.0885
배치 8은 한 에포크만에 0.0914 다. 최소가 0.0885 이니 사실상 도착했다.
데이터를 딱 한 번 읽는 동안이다.
전체 배치는 학습률을 최적으로 골라 줘도 한 에포크 뒤 7.8806 이고, 열두
에포크를 다 써도 0.1159 다. 같은 데이터를 열두 번 읽는 동안 배치 8이 한 번
읽고 도달한 곳에 못 온다.
부정확한 걸음 256번이 정확한 걸음 한 번을 이긴다. 노이즈가 걸음 수를 사는 값이었던 셈이다.
왜 노이즈가 치명적이지 않은가
기울기 어림이 틀리는 방식이 중요하다. 배치를 무작위로 뽑으면 그 기울기의 기댓값이 전체 기울기와 같다. 틀리되 한쪽으로 치우쳐 틀리지 않는다는 뜻이다. 걸음마다 방향이 흔들려도 평균적으로는 내려가는 쪽을 향한다.
그리고 오차는 걸음이 쌓이면서 서로 상쇄된다. 같은 방향의 편향이 아니라 매번
독립적인 흔들림이라, 예순네 걸음의 누적 오차는 예순네 배가 아니라 대략
sqrt(64) = 8 배로 자란다. 반면 전진은 예순네 걸음만큼 그대로 쌓인다.
대가
공짜는 아니다. 위 셈은 내려가는 동안에만 성립한다. 최소에서 멀 때는 진짜 기울기가 크니 노이즈보다 신호가 이기지만, 최소에 가까워지면 신호는 0으로 줄고 노이즈는 그대로 남는다.
오래 돌려 보면 그게 숫자로 나온다.
12에포크 300에포크 최소와의 차(300에포크)
배치 32 0.0887 0.0891 0.0007
배치 8 0.0886 0.0904 0.0019
더 돌렸는데 나빠졌다. 배치 32는 0.0887 에서 0.0891 로, 배치 8은
0.0886 에서 0.0904 로 올라간다. 최소 근처에 도착한 뒤로는 노이즈가 만든
진동만 남아서, 걸음을 더 걸을수록 그 진동 위 어딘가에 머문다.
그리고 배치가 작을수록 그 바닥이 높다. 300에포크에서 배치 8이 0.0904,
배치 32가 0.0891 이다. 빨리 도착하는 대가로 더 멀리서 진동한다. 학습률을
뒤에서 줄이는 스케줄이 필요한 이유가 이것이다.
남는 대가도 둘 더 있다.
- 배치를 키우면 걸음 수가 준다. 큰 배치로 같은 결과를 얻으려면 학습률을 올려야 하고, 2편의 상한이 그것을 막는다. 위 표의 전체 배치가 그 벽이다
- 하드웨어가 기준을 흔든다. 위 비교는 데이터를 읽는 개수를 값으로 쳤지만, GPU 에서는 배치 256이 배치 32의 8배 시간이 걸리지 않는다. 장치가 놀고 있는 구간에서는 배치를 키워도 걸음 시간이 거의 안 늘어서 샘플당 비용이 떨어진다. 그래서 실제로는 이론상 최적보다 큰 배치를 쓴다. 물론 장치가 포화하면 그 이득은 끝나고 시간이 배치에 비례하기 시작한다
그래서
- 미니배치 기울기는 편향 없이 흔들린다. 방향이 틀려도 평균은 맞는다
- 오차는
sqrt((1/B)(n-B)/(n-1))을 따른다. 측정2.997대 예측3.000이고,1/sqrt(B)근사만 쓰면2.828로 어긋난다 - 비교 단위를 걸음이 아니라 에포크로 잡으면 부정확한 여러 걸음이 정확한 한
걸음을 이긴다. 배치 8은 1에포크에
0.0914, 전체 배치는 12에포크에도0.1159 - 노이즈는 대가를 남긴다. 300에포크까지 돌리면 배치 8이
0.0904로 나빠지고, 배치가 작을수록 그 바닥이 높다 - 배치 크기는 정확도와 걸음 수를 맞바꾸는 손잡이지, 클수록 좋은 값이 아니다
다음 편은 4편으로 돌아간다. 초기값을 맞추느라 애썼는데, 층마다 한 줄을 더하면 그 고생이 아예 없어진다. 정규화가 초기값을 어떻게 지우는지 재 본다.
Comments