학습률에는 넘으면 안 되는 선이 있다
학습률은 보통 이렇게 배운다. 너무 작으면 느리고, 너무 크면 발산한다. 맞는 말인데 쓸모가 없다. 얼마부터 큰지를 안 알려주기 때문이다.
파라미터가 두 개뿐인 문제에서는 그 경계를 정확한 숫자로 구할 수 있다. 한 번 구해 놓으면, 파라미터가 몇 억 개가 되어도 왜 학습률을 못 올리는지가 같은 이유로 설명된다.
손실 지형을 만든다
점 네 개를 놓고 직선 하나를 맞춘다. 파라미터는 기울기 w 와 절편 b 두 개다.
import numpy as np
x = np.array([1., 2., 3., 4.])
y = np.array([2., 4., 5., 8.])
def loss(w, b):
return np.mean((w * x + b - y) ** 2)
def grad(w, b):
e = w * x + b - y
return 2 * np.mean(e * x), 2 * np.mean(e)
정답은 미리 알 수 있다. 최소제곱해는 w = 1.9, b = 0 이고 그때 손실은
0.175 다. 데이터에 노이즈가 있으니 손실이 0 이 되지는 않는다.
파라미터가 둘이니 손실은 평면 위의 지형이 된다. 이 문제의 손실은 정확히 이차식이라 등고선은 타원인데, 동그란 타원이 아니라 한쪽으로 7배 넘게 늘어난 타원이다. 그 길쭉함이 아래 두 절의 내용을 전부 결정한다.
한 걸음
경사하강법은 한 줄이다. 기울기 반대 방향으로 학습률만큼 간다.
w = b = 0.0
lr = 0.05
for step in range(5):
gw, gb = grad(w, b)
w, b = w - lr * gw, b - lr * gb
print(f"step {step+1}: w={w:.4f} b={b:.4f} loss={loss(w, b):.4f}")
step 1: w=1.4250 b=0.4750 loss=0.9647
step 2: w=1.6625 b=0.5463 loss=0.2478
step 3: w=1.7041 b=0.5510 loss=0.2267
step 4: w=1.7133 b=0.5449 loss=0.2247
step 5: w=1.7171 b=0.5371 loss=0.2232
첫 걸음이 압도적으로 크다. 손실이 27.25 에서 0.96 으로 떨어진다. 그리고 그 다음부터는 거의 멈춘 것처럼 보인다. 다섯 걸음 동안 손실이 0.2478 에서 0.2232 로, 1% 남짓 움직인다.
멈춘 게 아니다. 같은 학습률로 200 걸음까지 가면 w=1.8904, b=0.0284, 손실
0.1751 로 최소에 거의 닿는다. 골짜기 방향으로는 빠르고 골짜기를 따라가는 방향으로는
느리다. 등고선이 동그라미가 아니라 길쭉한 타원이라는 사실이 그대로 걸음에
나타난다.
경계는 계산으로 나온다
학습률을 올려 보자.
lr=0.11 200걸음 뒤 손실 0.175
lr=0.119 200걸음 뒤 손실 0.343
lr=0.121 200걸음 뒤 손실 100803
lr=0.13 200걸음 뒤 손실 7.3e+28
0.119 와 0.121 사이에서 무언가가 끊어진다. 그 무언가는 손실의 곡률이다.
이차 손실에서 곡률은 헤세 행렬 하나로 다 적힌다.
H = 2 * np.array([[np.mean(x*x), np.mean(x)],
[np.mean(x), 1.0 ]])
print(H) # [[15. 5.] [ 5. 2.]]
print(np.linalg.eigvalsh(H)) # [ 0.2994 16.7006]
print(2 / 16.7006) # 0.11976
고윳값이 두 개 나온다. 큰 쪽 16.70 이 가장 가파른 방향의 곡률이고, 작은 쪽
0.30 이 골짜기를 따라가는 완만한 방향이다. 이 지형에서 경사하강법이 발산하지
않을 조건은 딱 하나다.
lr < 2 / 가장 큰 고윳값 = 2 / 16.7006 = 0.1198
위에서 0.119 는 살아남고 0.121 은 폭발한 이유가 이것이다. 감이 아니라 경계선이 있었고, 소수점 셋째 자리까지 맞는다.
왜 하필 2/λ 인가는 한 방향만 떼어 놓고 보면 바로 보인다. 그 방향의 손실이
λ/2 · d² 이면 기울기는 λd 이고, 한 걸음 뒤 거리는 d(1 - lr·λ) 가 된다.
|1 - lr·λ| < 1, 즉 lr < 2/λ 여야 거리가 줄어든다. lr 이 그 값을 넘으면
매 걸음 반대편으로 더 멀리 튕겨 나간다. lr=0.13 의 자취를 찍어 보면 딱 그
모양이다. (0, 0) 에서 (3.7, 1.24) 로 건너뛰고, 다음 걸음에 (-0.62, -0.26)
으로 되돌아오고, 그다음은 (4.46, 1.44) 로 더 멀리 간다. 위 그림에서 그 학습률의
손실이 곧게 위로 뻗는 것이 이 왕복이다.
이게 왜 큰 모델에서도 같은 이야기인가
파라미터가 억 단위가 되면 헤세 행렬을 통째로 적어둘 수 없다. 원소가 파라미터 수의 제곱만큼 필요하기 때문이다. 그래도 헤세와 벡터의 곱은 역전파 두 번이면 구할 수 있고, 거기에 거듭제곱법을 태우면 가장 큰 고윳값은 실제로 뽑는다. 전부는 못 봐도 안정성을 정하는 그 하나는 볼 수 있다는 뜻이고, 구조는 그대로다.
- 안정성을 정하는 건 가장 가파른 방향 하나다. 나머지 방향이 아무리 완만해도 그 하나가 발산하면 전부 같이 망가진다.
- 학습이 느린 이유는 대개 반대쪽 끝, 가장 완만한 방향이다. 가파른 방향에 맞춰 학습률을 낮춰야 하니, 완만한 방향은 그 낮은 학습률로 기어간다.
- 두 고윳값의 비
16.7 / 0.30 = 56이 이 문제의 조건수다. 이 값이 클수록 타원이 길쭉하고, 경사하강법은 더 답답해진다.
입력을 정규화하면 학습이 잘 된다는 말도 여기서 정확해진다. 흔히 “크기를 줄이면 학습률을 올릴 수 있다” 고 설명하는데, 절반만 맞다. 이 데이터로 확인해 보자.
x 그대로 : 고윳값 [0.299, 16.70] 조건수 55.8 lr 상한 0.120
x 를 0.1배 : 고윳값 [0.024, 2.13] 조건수 90.4 lr 상한 0.940
x 에서 평균 뺌: 고윳값 [2.00, 2.50] 조건수 1.2 lr 상한 0.800
평균 빼고 표준화: 고윳값 [2.00, 2.00] 조건수 1.0 lr 상한 1.000
크기만 줄이면 상한은 8배로 올라가지만 조건수는 오히려 나빠진다(55.8 → 90.4). 지형이 더 길쭉해진 것이라, 학습률을 올릴 수 있는 만큼 이득을 다시 반납한다.
실제로 듣는 것은 평균을 빼는 쪽이다. x 를 중심에 놓으면 mean(x) 가 0 이
되어 헤세 행렬의 비대각 항이 사라진다. w 와 b 가 서로 얽혀 있던 것이 풀리고,
조건수가 55.8 에서 1.2 로 떨어진다. 표준화까지 하면 1.0, 즉 완전한 동그라미가
된다. 정규화의 값어치는 스케일이 아니라 축을 세우는 데 있다.
모멘텀과 Adam 도 같은 문제를 다루지만 방식이 다르다. 지형을 바꾸는 게 아니라 걸음을 바꾼다. 모멘텀은 방향이 일정한 축의 걸음을 누적해 완만한 방향에서 더 멀리 가고, Adam 은 축마다 최근 기울기 크기로 걸음을 나눠 축 사이의 불균형을 줄인다. 다만 Adam 이 조건수를 없애 주는 것은 아니다 - 대각 성분만 보정하므로, 축이 좌표축과 어긋나 있으면 남는다.
그래서
- 손실 지형은 그릴 수 있고, 파라미터 두 개면 진짜로 그려진다
- 학습률 상한은
2 / λ_max이고, 실험값과 소수점 셋째 자리까지 맞는다 - 잘 안 되는 학습의 상당수는 알고리즘이 아니라 지형의 길쭉함 문제다
다음 편은 기울기 그 자체다. 위에서 grad 를 손으로 적었지만, 층이 쌓이면 그럴
수 없다. 역전파가 그 계산을 어떻게 장부 적듯 처리하는지, 노드 세 개짜리
그래프에서 손으로 따라간 뒤 수치미분과 대조해 본다.
Comments