내리막길: Gradient Descent와 모두가 건너뛰는 두 단계
learning rate의 정확한 상한을 계산하고, 3,600개 방향 brute-force 탐색이 gradient를 스스로 재발견하는 과정을 봅니다.
이 페이지에서
이전 장은 골짜기에서 끝났습니다.
비유적인 골짜기가 아니라 실제 곡선입니다. 하나의 parameter에 대해 loss를 그리면 아래로 내려갔다가 다시 올라오는 곡선이었죠. 그리고 그 아래의 loss는 깔끔해서 고른 것이 아니었습니다. 측정값의 noise에 관한 진술에서 유도했고, squared error는 관례가 아니라 결과로 튀어나왔습니다.
그래서 우리는 바닥이 있는 지형을 갖게 되었고, 그 바닥이 있어야 할 올바른 곳이라고 믿을 이유도 갖게 되었습니다. 아직 없는 것은 거기로 가는 방법입니다.
이 장에서는 그 방법을 만듭니다. 그리고 그것은 이 강좌의 나머지 모든 model을 훈련하는 algorithm입니다. 예외 없이 전부, 수천억 개의 parameter를 가진 model까지 포함해서요. 이 algorithm은 스무 줄 정도에 들어갑니다. 어려운 두 부분은 그 스무 줄 안에 있지 않고, 거의 모든 설명이 건너뛰는 두 가지입니다.
- 왜 마이너스 기호인가. update는 gradient를 뺍니다. 모든 tutorial이 그렇게 쓰지만, gradient가 왜 위로 가는 방향인지 말하는 경우는 드뭅니다. 바로 그 사실만이 마이너스 기호를 믿음의 행위가 아닌 것으로 만듭니다.
- step은 얼마나 커야 하는가. "너무 크면 발산하고, 너무 작으면 느리다"는 말은 맞지만 쓸모없습니다. 정확한 숫자가 있고, loss에서 계산할 수 있으며, 이 장에서는 그것을 두 번 계산합니다. 한 번은 장난감 parabola에서, 한 번은 실제 data에서요.
설정, 그리고 왜 그냥 search할 수 없는가
섹션 링크: 설정, 그리고 왜 그냥 search할 수 없는가이 장만 읽어도 이해되도록 다시 말해 봅시다. 1장의 conveyor belt에서 나온 여덟 개 부품을 가지고, 다른 질문을 던집니다. 합격 또는 불합격이 아닙니다. 그 질문은 나중에 다시 돌아옵니다. 여기서는 부품의 너비로 무게를 예측합니다.
import numpy as np
WIDTH = np.array([18.0, 19.5, 20.2, 21.0, 24.0, 25.5, 23.0, 26.0])
WEIGHT = np.array([47.0, 52.0, 49.0, 55.0, 61.0, 66.0, 70.0, 58.0])
x = WIDTH - WIDTH.mean() # 22.15 mm
y = WEIGHT - WEIGHT.mean() # 57.25 g측정값은 1장과 똑같이 centered 되어 있습니다. 이유가 있고, 이 장이 끝나기 전에 그 이유는 큰 이자를 붙여 돌아옵니다. model은 직선 이고, loss는 이전 장에서 유도한 mean squared error입니다.
parameter가 두 개입니다. 그냥 값을 많이 시도해 보면 안 될까요? 실제로 해 봅시다. 부터 까지, 그리고 부터 까지, 간격의 grid입니다.
grid 501 x 1001 = 501,501 evaluations in 3.67 s
best found: a = 2.1000, b = -0.0000, L = 24.592450두 숫자를 소수 둘째 자리까지 맞추는 데 평가가 50만 번입니다. 그리고 그 초 단위 시간은 한 machine의 wall clock이라, 다시 실행하면 3초에서 6초 사이 어디든 나올 수 있습니다. 재현되는 부분은 평가 횟수와 최솟값입니다. 이 장 끝의 gradient descent는 여덟 step으로 소수 넷째 자리까지 가고, 서른여섯 step으로 전체 float64 답에 도달합니다.
하지만 속도가 논점은 아닙니다. 이 지점이 전체 강좌를 결정합니다. grid search는 개의 parameter 각각에 개의 값을 둘 때 번의 평가가 듭니다. 축마다 천 개의 값을 두면 다음과 같습니다.
| model | parameters | grid evaluations |
|---|---|---|
| 이 직선 | 2 | |
| 5장의 XOR network | 9 | |
| 작은 multilayer network | 20,000 |
세 번째 행은 큰 숫자가 아닙니다. 의미 없는 숫자입니다. 관측 가능한 우주의 원자 수는 대략 개입니다. model이 커질수록 search는 느려지는 것이 아니라 존재를 멈춥니다. 뒤에 나오는 모든 것은 이 표 때문에 존재합니다.
derivative는 측정할 수 있는 양이다
섹션 링크: derivative는 측정할 수 있는 양이다잠시 를 고정해서 parameter 하나와 곡선 하나만 남깁시다. 지난 장이 남긴 그림이 바로 그것이었습니다. 곡선 위의 한 점 를 잡고 묻습니다. 를 작은 양 만큼 살짝 움직이면, 그 움직임 한 단위당 loss는 얼마나 움직일까요?
이 비율은 rise over run입니다. 곡선 위의 두 점을 잇는 직선의 기울기입니다. 가 줄어들수록 두 점은 서로 가까워지고, 그 직선은 tangent가 됩니다. 그 기울기가 derivative 입니다. 가 한 단위 변할 때 loss가 변하는 비율입니다. 어떤 것의 approximation도 아니고, 무한히 작은 양도 아닙니다. 평범한 비율들의 limit입니다.
직접 실행해 볼 가치가 있습니다. 숫자는 정의만으로는 말해 주지 않는 것을 말해 주기 때문입니다.
def loss1(a):
return np.mean((a * x - y) ** 2)
for h in [1.0, 1e-2, 1e-4, 1e-6, 1e-8, 1e-10, 1e-12, 1e-14]:
q = (loss1(1.0 + h) - loss1(1.0)) / h
print(f"h = {h:<8.0e} slope estimate = {q:.10f} error = {abs(q + 16.385):.3e}")h = 1e+00 slope estimate = -8.9400000000 error = 7.445e+00
h = 1e-02 slope estimate = -16.3105500000 error = 7.445e-02
h = 1e-04 slope estimate = -16.3842555001 error = 7.445e-04
h = 1e-06 slope estimate = -16.3849925556 error = 7.444e-06
h = 1e-08 slope estimate = -16.3850003787 error = 3.787e-07
h = 1e-10 slope estimate = -16.3850444324 error = 4.443e-05
h = 1e-12 slope estimate = -16.3851154866 error = 1.155e-04
h = 1e-14 slope estimate = -17.0530256582 error = 6.680e-01여기서 두 가지 일이 일어나고, 둘 다 구조를 지탱합니다.
error는 에 대충 비례하는 것이 아니라 정확히 입니다. 를 100으로 나누면 error도 100으로 나뉩니다. 매번 유효숫자 네 자리까지 그렇습니다. 그 상수는 장식이 아닙니다. loss의 second derivative의 절반이고, 두 section 뒤에 나올 아이디어의 첫 등장입니다. 한 점 근처의 곡선은 직선에 에 비례하는 보정항을 더한 것처럼 보인다는 아이디어입니다.
그리고 나서 pattern이 깨집니다. 아래에서는 추정이 더 나빠지고, 에서는 둘째 자리부터 틀립니다. 수학적으로는 아무 일도 일어나지 않았습니다. 지난 장의 floating-point 상자가 작동했을 뿐입니다. 와 는 처음 열 자리 숫자가 같고, 그것들을 빼면 그 숫자들이 파괴됩니다. 그리고 그 잔해를 아주 작은 수로 나누면 남은 것이 증폭됩니다. 최적인 가 있습니다. 여기서는 근처, 대략 machine epsilon의 제곱근입니다. 더 작게 가는 것은 더 조심스러운 것이 아니라 덜 조심스러운 것입니다. 기억해 두세요. 이 장 끝의 한 함수가 여기에 의존합니다.
측정이 아니라 calculus에서 나온 정확한 기울기는 입니다. 이제 측정을 멈추고 유도를 시작할 수 있습니다.
Composition, 그리고 chain rule
섹션 링크: Composition, 그리고 chain rule강좌의 나머지가 세워질 아이디어를 한 번, 담백하게 말하겠습니다.
두 함수를 compose한다는 것은 하나를 다른 하나에 넣는 것입니다. . 그 이상도 이하도 아닙니다.
deep network는 composition과 비슷한 것이 아닙니다. 그것은 composition 그 자체입니다. layer는 함수입니다. layer를 쌓는 것은 그것들을 compose하는 것입니다. "depth"는 chain 안의 함수 개수입니다. 5장에서 network를 만들 때 우리가 만드는 것은 이고, 그 외에는 아무것도 아닙니다. 따라서 우리의 목적에서 calculus의 가장 중요한 규칙 하나는 composition을 미분하는 규칙입니다.
비율은 곱해집니다. 가 보다 세 배 빠르게 변하고, 가 보다 두 배 빠르게 변한다면, 는 보다 여섯 배 빠르게 변합니다. 내용은 그게 전부입니다. 그리고 그것이 열 개 layer를 거슬러 올라가는 signal이 열 개 숫자와 곱해지는 이유입니다. 그래서 6장은 그 숫자들이 모두 1보다 조금 작을 때 무슨 일이 생기는지에 한 section을 씁니다.
우리의 loss에 적용해 봅시다. residual을 라고 쓰면 입니다. 각 는 inner function 를 통해 에 의존하고, 그 derivative는 입니다. chain rule을 항별로 적용하면 다음과 같습니다.
이 곱슬 기호는 partial derivative를 표시합니다. 한 변수에 대해 미분하고 다른 모든 것은 상수로 취급한다는 뜻입니다. 새로운 일은 일어나지 않습니다. 앞과 같은 limit을 한 축을 따라 취한 것입니다. partials를 vector로 모으면 gradient가 됩니다.
점 에서 그 vector는 입니다. 숫자 두 개입니다. 문제는 그것들이 무엇을 의미하는가이고, 이것이 모두가 건너뛰는 첫 번째 단계입니다.
왜 gradient는 오르막을 가리키는가
섹션 링크: 왜 gradient는 오르막을 가리키는가gradient는 축을 따른 기울기들의 vector입니다. 우리가 증명한 것은 그것뿐입니다. 그것들을 vector로 조립하면 특정한 방향을 가리킨다는 것이 명백하지 않습니다. 명백해서도 안 됩니다.
그러니 실제로 원하는 것을 정의합시다. unit vector , 즉 방향 하나를 고릅니다. directional derivative는 그 방향으로 걸어갈 때 loss가 변하는 비율입니다.
chain rule은 이것을 계산 가능한 것으로 바꿉니다. 를 따라 걸으면 는 의 비율로 변하고, 는 의 비율로 변하며, contribution은 더해집니다.
어떤 방향에서의 변화율도 gradient와 그 방향의 dot product입니다. 이제 punchline은 geometry 한 줄입니다. 두 vector 사이의 angle을 라고 두고 dot product를 쓰면,
의 길이가 1이기 때문입니다. 당신이 제어할 수 있는 것은 뿐입니다. 그것은 에서 가장 크고, 반 바퀴인 도에서 가장 작습니다. 그래서:
- steepest ascent는 자체를 따르고, 그곳의 기울기는 정확히 입니다.
- steepest descent는 를 따르고, 그곳의 기울기는 입니다.
- gradient에 수직인 방향에서는 loss가 전혀 변하지 않습니다. contour map의 선들이 gradient와 직각으로 교차하는 이유입니다.
그것이 마이너스 기호입니다. 관례도 아니고, 누군가 고른 부호 반전도 아닙니다. 가장 빠르게 감소하는 방향은 negative gradient입니다. 왜냐하면 가 반 바퀴에서 최소가 되기 때문이고, 다른 이유는 없습니다.
모든 방향에 대한 주장이라면 모든 방향에 대해 시험해 봅시다. 0.1도마다 하나씩 3,600개를 sample하고, 각각을 살짝 움직여 측정합니다.
theta = np.array([1.0, 4.0])
g = grad(theta)
print("gradient ", g)
print("its length ", np.linalg.norm(g))
print("its angle ", np.degrees(np.arctan2(g[1], g[0])) % 360, "degrees")
best = max(
((loss(theta + 1e-6 * u) - loss(theta - 1e-6 * u)) / 2e-6, np.degrees(ang))
for ang, u in (
(a, np.array([np.cos(a), np.sin(a)])) for a in np.arange(3600) * 2 * np.pi / 3600
)
)
print("steepest slope", best[0], "at", best[1], "degrees")gradient [-16.385 8. ]
its length 18.23371122399386
its angle 153.97598928042032 degrees
steepest slope 18.233709624837502 at 154.0 degreesgradient에 대해 아무것도 모르는 search가 3,600개 방향을 뒤져 154.0도에서 가장 가파른 오르막을 찾습니다. search의 0.1도 resolution 안에서 gradient 자신의 방향입니다. 그리고 그곳에서 찾은 기울기 18.2337은 gradient의 길이와 여섯 자리까지 같습니다. theorem은 gradient가 무엇을 의미하는지에 대한 이야기가 아닙니다. 측정 가능한 사실이고, 이것이 그 측정입니다.
왜 작은 내리막 step은 실제로 도움이 되는가
섹션 링크: 왜 작은 내리막 step은 실제로 도움이 되는가이제 두 번째로 건너뛴 단계입니다. 우리는 어느 쪽이 아래인지 압니다. 하지만 그쪽으로 걸어간다고 loss가 낮아진다는 결론은 따르지 않습니다. "아래"는 infinitesimal한 살짝 움직임에 관한 진술이고, step은 infinitesimal하지 않기 때문입니다.
다리는 linearisation입니다. 한 점 근처에서 smooth function은 tangent에 보정항을 더한 것입니다.
이것이 first-order Taylor expansion입니다. 버려진 가 curvature입니다. slope table의 추정값을 정확히 만큼 틀리게 만든 바로 그 항입니다. 우리가 의도한 step 를 넣어 봅시다.
loss는 만큼 떨어집니다. 그 모든 부분은 non-negative입니다. 그러니 약속은 진짜입니다. 다만 충분히 작은 에 대해서만 그렇습니다. 무시한 항은 처럼 자라서 결국 그것을 먹어 치우기 때문입니다. 이게 이론의 전부입니다. 약속이 지켜졌다가 깨지는 모습을 보겠습니다.
eta = 0.2 promised 66.49364500 delivered -16.01619240 ratio -0.240868
eta = 0.1 promised 33.24682250 delivered 12.61936315 ratio 0.379566
eta = 0.01 promised 3.32468225 delivered 3.11840766 ratio 0.937957
eta = 0.001 promised 0.33246822 delivered 0.33040548 ratio 0.993796
eta = 0.0001 promised 0.03324682 delivered 0.03322620 ratio 0.999380
eta = 1e-05 promised 0.00332468 delivered 0.00332448 ratio 0.999938아래에서부터 읽어 보세요. 가 줄어들수록 실제 drop은 약속한 값으로 수렴합니다. ratio가 0.99938, 이어서 0.99994입니다. Taylor theorem이 맞다는 뜻입니다. 위에서 읽으면 에서 실제 "drop"은 음수 16입니다. step은 내리막으로 갔는데 loss는 올라갔습니다.
따라서 update rule은
이고, 아무도 말하지 않는 조건이 붙습니다. 가 충분히 작아야 한다는 조건입니다. 정확히 무엇에 비해 충분히 작은지가 다음 section입니다.
learning rate에는 상한이 있고, 계산할 수 있다
섹션 링크: learning rate에는 상한이 있고, 계산할 수 있다가장 단순한 골짜기 에서 시작합시다. 여기서 입니다. gradient descent의 한 step은
입니다. 위치는 매 step마다 와 곱해집니다. 이것은 geometric sequence이고, geometric sequence에는 규칙이 정확히 하나 있습니다. multiplier의 절댓값이 1보다 작으면 줄어들고, 그렇지 않으면 커집니다. 그래서 , 즉 입니다.
경계는 정확히 에 있습니다. "대략 1"도 아니고, "1은 보통 너무 크다"도 아닙니다. 에서 multiplier는 이고, 점은 와 사이를 영원히 튕겨 다닙니다. 가까워지지도, 멀어지지도 않습니다. 그 아래에서는 수렴하고, 그 위에서는 발산합니다. interval은 에서 다시 갈라집니다. 그곳에서 multiplier의 부호가 바뀝니다. 그 아래에서는 monotone하게 접근하고, 그 위에서는 점이 overshoot하며 양쪽을 번갈아 오갑니다. 정확히 에서는 multiplier가 0이 되어 단 한 step이 minimum에 착지합니다.
네 줄의 algebra에서 네 가지 regime이 나왔습니다. 직접 경계를 넘어 보세요.
그리고 이제 흥미로운 경우입니다.
이제 같은 논리에서 나오는 일반 규칙입니다. multiplier 는 사실 였고, minimum 근처에서 multi-parameter loss는 방향마다 이런 숫자 하나를 갖습니다. second derivatives의 matrix가 가진 eigenvalues입니다. 모든 방향이 동시에 stable해야 하므로 상한은 가장 큰 값으로 정해집니다.
에 대해 이고 상한은 1입니다. 방금 유도한 그대로입니다. 우리의 belt에서 second-derivative matrix는 를 input의 두 column matrix라고 할 때 이고, 그 eigenvalues는 2와 14.89입니다. 따라서 상한은 입니다. 유효숫자 다섯 자리가 들어간 예측입니다. 시험해 봅시다.
lr=0.1343 -> L = 24.5924
lr=0.13431 -> L = 24.5924
lr=0.13432 -> L = 4707.8 BLEW UP
lr=0.13433 -> L = 4.00452e+16 BLEW UP
lr=0.1344 -> L = 1.18229e+107 BLEW UPlinear algebra 한 줄과 for loop 10만 iteration 사이에 소수 다섯 자리까지 일치합니다.
그리고 여기서 1장이 돌아옵니다. 위의 모든 것은 centered 측정값을 사용했습니다. raw millimetres와 grams에서 동일한 code를 실행하면 eigenvalues는 2와 14.89가 아니라 0.0298과 998.1입니다. 상한은 0.134에서 0.002004로 붕괴합니다. 똑같이 정확하게, lr=0.002003에서는 수렴하고 lr=0.002004에서는 폭발합니다.
상한보다 더 나쁜 것은 eigenvalues 사이의 ratio입니다. condition number는 골짜기가 원형에서 얼마나 멀리 있는지를 측정합니다. 길고 얇은 도랑에서는 가파른 벽에 맞출 만큼 rate를 작게 해야 하고, 그러면 도랑 바닥도 같은 느린 속도로 걸어가야 합니다. centered에서는 7.44였던 값이 raw에서는 33,452가 됩니다. 각 version이 취할 수 있는 최적 rate에서:
| features | condition number | best rate | steps to within 1% of the optimum |
|---|---|---|---|
| centered | 7.44 | 0.1184 | 10 |
| raw millimetres and grams | 33,452 | 0.0020037 | 79,513 |
같은 data, 같은 code, 끝의 같은 답입니다. 그런데 평균을 빼지 않았다는 이유로 작업량이 8천 배가 됩니다. 1장에서 같은 누락은 perceptron에 epoch 6천 배라는 비용을 냈고, 그때의 진단은 geometric했습니다. data가 origin에서 멀리 떠 있었죠. 여기서는 optimisation 옷을 입은 같은 geometry입니다. 그래서 input normalisation은 hygiene advice가 아니라 arithmetic입니다.1
스무 줄
섹션 링크: 스무 줄위의 어떤 것도 library가 필요하지 않았습니다. 전체 optimiser는 다음과 같습니다.
def loss(theta):
a, b = theta
return np.mean((a * x + b - y) ** 2)
def grad(theta):
a, b = theta
residual = a * x + b - y
return np.array([np.mean(2 * residual * x), np.mean(2 * residual)])
def descend(theta, lr, steps):
theta = np.array(theta, dtype=float)
for _ in range(steps):
theta = theta - lr * grad(theta)
return theta
theta = descend([0.0, 0.0], lr=0.05, steps=60)
print(theta, loss(theta))[ 2.10040296e+00 -2.76445533e-15] 24.592448791134984이 여덟 점에 대한 closed-form least-squares 답은 , 이고, loss는 입니다. loop는 closed form이 존재한다는 사실을 모르고도 유효숫자 여덟 자리까지 그것을 찾았습니다. 이것이 중요합니다. 5장 이후로는 closed form이 없을 것이기 때문입니다.
trajectory는 다음과 같습니다. 지켜보는 것이 핵심이니까요.
0 a=0.000000 b=0.000000 L=57.437500
1 a=1.563750 b=0.000000 L=26.736582
2 a=1.963288 b=-0.000000 L=24.732418
5 a=2.098116 b=-0.000000 L=24.592488
10 a=2.100400 b=-0.000000 L=24.592449
60 a=2.100403 b=-0.000000 L=24.592449거리의 대부분은 처음 두 step에서 이동합니다. 바닥에서 가장 멀 때 gradient가 가장 크고, 가까워질수록 작아지기 때문입니다. Gradient descent는 minimum 근처에서 자동으로 느려집니다. 그것은 feature이며, 6장에서는 문제이기도 합니다.
slope가 0인 다른 곳들
섹션 링크: slope가 0인 다른 곳들지금까지의 논의에는 구멍이 있습니다. step은 일 때 멈추고, 우리는 그것을 "minimum"이라고 불러 왔습니다. gradient가 0인 점은 critical point이고, minimum은 critical point가 되는 여러 방식 중 하나일 뿐입니다.
- local minimum: 모든 방향이 오르막이지만, 그런 점들 중 전체에서 가장 낮은 점은 아닐 수 있습니다.
- local maximum: 모든 방향이 내리막입니다.
- saddle point: 어떤 방향은 오르막이고 다른 방향은 내리막입니다. 표면 는 를 가지며, origin에서 0입니다. 그곳에서 함수는 -axis를 따라서는 minimum이고, 동시에 -axis를 따라서는 maximum입니다.
Gradient descent는 이들을 구분할 수 없습니다. gradient만 보기 때문이고, 세 경우 모두에서 gradient는 0이기 때문입니다.
우리의 직선에는 critical point가 하나 있고 그것이 답입니다. linear model 위의 squared-error loss는 convex, 즉 단 하나의 bowl이고, 그 위에서 descent는 global minimum을 찾는 데 실패할 수 없습니다. 이 성질은 이 강좌와 접촉하는 순간 살아남지 못합니다. neural network의 loss는 convex가 아니며, 5장 이후로는 "the minimum"이라는 것이 존재하지 않습니다. 깊이가 다른 minimum이 여럿 있고, 어떤 것을 얻는지는 어디서 시작했는지에 달려 있습니다. 이 문장은 한 문장으로 말하고 한 문장으로 남겨 둡니다. 이론은 크고, 실용적 결과는 작기 때문입니다.
그 결과 전체를 곡선 하나에서 볼 수 있습니다. 깊이가 다른 두 골짜기를 가진 를 봅시다.
x = -1.046681 f(x) = -0.352386 minimum
x = 0.101031 f(x) = 0.005026 maximum
x = 0.945649 f(x) = -0.152639 minimum얕은 골짜기에 착지하면 loss가 56.7% 더 나쁘고, algorithm은 그것을 알 방법이 없습니다. 골짜기 안에서는 모든 방향이 오르막이기 때문입니다. gradient descent 안에는 이것을 고칠 방법이 없고, 앞으로도 나오지 않습니다. 실제로 있는 것은 이것이 이 그림이 암시하는 것보다 훨씬 덜 중요하다는 발견입니다. 실제 network의 매우 높은 차원에서는 대부분의 critical point가 trap이 아니라 saddle로 드러나며,2 5장은 작은 network가 실제로 얼마나 자주 stuck 되는지 측정합니다.
더 싼 steps: stochastic, minibatch, momentum
섹션 링크: 더 싼 steps: stochastic, minibatch, momentum위의 grad에서 마음에 걸려야 할 점이 하나 있습니다. 매 step마다 전체 dataset에 대해 합을 구합니다. 부품 여덟 개는 아무것도 아닙니다. 백만 개는 parameter를 한 번 움직이는 데 gradient computation 백만 번입니다.
탈출구는 gradient가 average라는 사실입니다. average는 sample로 추정할 수 있습니다. 무작위 handful, 즉 minibatch에서 계산하고 그걸로 step을 가면 됩니다. 추정은 noisy합니다. 동시에 unbiased이고, 싸고 noisy한 step 수백 번이 비싼 정확한 step 한 번을 이깁니다. synthetic parts 10만 개에서, step이 아니라 per-example gradients를 세면 다음과 같습니다.
| method | steps to within 0.1% of the optimum | per-example gradients |
|---|---|---|
| full batch | 7 | 700,000 |
| minibatch of 32 | 100 | 3,200 |
| one example at a time | 17,580 | 17,580 |
같은 곳에 도달하는 데 산술이 219배 적게 듭니다. 그리고 극단, 즉 한 번에 example 하나씩 하는 Robbins와 Monro3의 original stochastic approximation은 승자가 아닙니다. batch 32보다 다섯 배 나쁩니다. matrix를 곱하는 hardware에서는 example 32개가 1개보다 거의 더 비싸지 않은 반면, noise는 batch size의 제곱근에 따라 줄어들기 때문입니다. 이 trade-off 때문에 앞으로 보게 될 모든 training script에는 batch_size가 들어 있습니다.
Momentum은 또 다른 싼 fix이고, 정확히 도랑을 겨냥합니다. badly conditioned valley에서는 step이 좁은 방향을 가로질러 지그재그 치면서 긴 방향으로는 기어갑니다. Momentum은 과거 gradients의 running average를 유지합니다. 그래서 oscillating components는 상쇄되고, consistent한 component는 누적됩니다.4
추가되는 것은 두 줄입니다. 우리가 가진 최악의 경우인 raw uncentred belt, condition number 33,452에서 plain descent가 취할 수 있는 best rate를 쓰면:
momentum beta=0.0 -> 79,513 steps to 1%
momentum beta=0.9 -> 1,609 steps to 1%
momentum beta=0.99 -> 461 steps to 1%code 두 줄로 172배입니다. 6장은 이것을 Adam으로 바꿉니다. mechanism은 이미 여기 있습니다.
5장에서 필요할 check
섹션 링크: 5장에서 필요할 check이 장의 모든 gradient는 손으로 유도했으므로 틀렸을 수 있습니다. 해결책은 시작 부분의 slope table입니다. derivative를 수치적으로 측정하고 비교하세요. leading error term을 cancel해 같은 에서 훨씬 정확한 central difference 를 사용합니다.
def numeric_grad(f, theta, h=1e-5):
theta = np.asarray(theta, dtype=float)
out = np.zeros_like(theta)
for i in range(theta.size):
bump = np.zeros_like(theta)
bump[i] = h
out[i] = (f(theta + bump) - f(theta - bump)) / (2 * h)
return out
def gradcheck(f, df, theta, h=1e-5):
analytic = np.asarray(df(theta), dtype=float)
numeric = numeric_grad(f, theta, h)
return np.max(np.abs(analytic - numeric) / np.maximum(1e-8, np.abs(analytic) + np.abs(numeric)))비교의 relative form이 중요합니다. 의 absolute difference는 크기가 인 gradient에서는 재앙이고, 크기가 인 gradient에서는 무의미합니다.
relative error: 1.8929136036763527e-11
with 2 dropped: 0.33333333331650744첫 줄은 위에서 손으로 유도한 gradient입니다. 둘째 줄은 한 component에서 factor 2가 빠진 같은 함수입니다. 단 한 글자의 typo이고, check가 즉시 잡아냅니다. 대략 아래는 agreement입니다. 위는 bug입니다. 이 함수를 보관하세요. 5장에서 automatic differentiation engine을 debug하는 데 사용하며, 잘못된 gradient를 찾아낼 수 있는 유일한 이유입니다.
다음에는 어디로 가는가
섹션 링크: 다음에는 어디로 가는가이 장의 모든 것은 말하지 않았던 하나의 가정 위에 놓여 있었습니다. 를 쓸 수 있다는 가정입니다.
parameter 두 개짜리 직선에서는 그것이 algebra 한 줄이었습니다. 거의 즉시 그렇지 않게 됩니다. symbolic algebra system에 network의 loss를 하나의 first-layer weight에 대해, 하나의 example에 대해 미분하라고 시키고, 답에 들어간 산술을 세어 보세요.
| network | operations in one partial derivative |
|---|---|
| hidden unit 네 개, layer 하나 | 40 |
| hidden unit 네 개, layer 두 개 | 301 |
| hidden unit 네 개, layer 세 개 | 1,717 |
세 번째 행은 parameter 57개짜리 network입니다. 6장에서는 footnote가 될 만큼 작은 network입니다. 그 gradient를 손으로 모두 쓰면 training example 하나에 대해 약 97,869 operations가 필요합니다. 이것을 구해 줄 notation은 없습니다. 구해 주는 것은 chain rule을 composition에 적용하면 거대한 structure가 생긴다는 관찰입니다. 같은 intermediate quantities가 반복해서 나타나고, 올바른 순서로 계산하면 forward pass 하나 정도의 비용으로 모든 derivatives를 얻습니다. 그것이 5장입니다.
하지만 먼저 더 작은 문제가 있고, 바로 앞에서 기다리고 있습니다.
이제 우리는 어떤 differentiable loss 위에서도 내리막으로 굴러가는 machine을 갖고 있습니다. 그것을 belt의 원래 질문, 즉 accept or reject, target이 1 또는 0인 문제에 겨냥해 봅시다. output에 sigmoid를 붙여 probability를 예측하게 하고, squared error를 minimise합니다. 실행은 됩니다. 하지만 가장 크게 틀렸을 때 거의 움직이지 않을 것이고, gradient는 그 이유를 말해 줍니다.
| output | prediction | truth | gradient with squared error | gradient with cross-entropy |
|---|---|---|---|---|
| 0.5000 | 1 | |||
| 0.1192 | 1 | |||
| 0.0025 | 1 | |||
| 1 |
model이 확신에 차서, 재앙적으로 틀렸습니다. 답이 1인데 0.0000454를 예측합니다. 그런데 squared-error gradient는 입니다. 자신이 곤란한 상황이라는 것을 전혀 모릅니다. 아직 유도하지 않은 loss에서 나온 다른 column은 1.0을 보고합니다. 최대 긴급도이고, 정확히 그래야 할 자리입니다.
그래서 다음 장이 여는 질문이 생깁니다. 지난 장은 loss가 noise에 관한 assumption이고, squared error는 Gaussian noise를 assumption한다고 말했습니다. yes-or-no answer에는 어떤 noise model이 있을까요? 그리고 같은 유도를 그것에 적용하면 어떤 loss가 나올까요?
Sources and method
섹션 링크: Sources and method이 방법은 이들 모두보다 오래되었습니다. Cauchy는 1847년 Académie des Sciences에 보낸 note에서, squared residuals의 합 위를 내리막으로 걸어 equations system을 푸는 방법으로 이를 설명했습니다. 이 장과 함께 읽을 만한 자료도 있습니다. Sebastian Ruder의 An overview of gradient descent optimization algorithms (arXiv:1609.04747)는 momentum부터 Adam까지를 읽기 쉬운 14쪽으로 다룹니다. Nocedal and Wright의 Numerical Optimization 3장(2nd ed., Springer, 2006)의 theorem 3.3은 quadratic에서 steepest descent의 convergence rate를 condition number로 제시합니다. conditioning이 step count를 결정하는 이유 뒤의 이론입니다. 다만 위에서 측정한 fixed-step 상한이 아니라 line search를 다룹니다. 또는 Deisenroth, Faisal and Ong의 Mathematics for Machine Learning §5.8과 §7.1은 같은 영역을 더 적은 장비로 다룹니다. Prince의 Understanding Deep Learning §6.1과 Goodfellow, Bengio and Courville의 Deep Learning §4.3도 함께 볼 만합니다. Dive into Deep Learning §12.1–12.3은 여기 담을 공간보다 더 많은 measurement로 minibatch analysis를 보여 줍니다. Géron의 Hands-On Machine Learning 4장(3rd ed.)은 learning rate를 유도하는 대상이라기보다 tune하는 대상으로 다루는 가장 실용적인 설명입니다. MIT 6.390 notes는 이 강좌처럼 classification 앞에 gradient descent를 놓는데, 이유도 같습니다.
-
LeCun, Y., Bottou, L., Orr, G. B. and Müller, K.-R. Efficient BackProp, in Neural Networks: Tricks of the Trade (Springer, 1998), pp. 9–50. Section 4.3은 권고를, section 5.1은 위 detail box에서 사용한 논증을 제공합니다. input을 centering하고 scaling하면 second-derivative matrix의 eigenvalues가 바뀌며, 따라서 단순한 numerical comfort가 아니라 step 수가 바뀝니다. ↩
-
Dauphin, Y. N., Pascanu, R., Gulcehre, C., Cho, K., Ganguli, S. and Bengio, Y. Identifying and attacking the saddle point problem in high-dimensional non-convex optimization, arXiv:1406.2572 (2014). 고차원에서는 minimum이 되려면 수천 개 direction이 모두 동시에 위로 휘어야 하므로 critical points가 local minima보다 압도적으로 saddles라는 논증입니다. ↩
-
Robbins, H. and Monro, S. A Stochastic Approximation Method. Annals of Mathematical Statistics 22(3), pp. 400–407 (1951). step size가 올바른 방식으로 줄어든다면 gradient의 noisy estimate만으로 충분하다는 것을 확립한 논문입니다. ↩
-
Polyak, B. T. Some methods of speeding up the convergence of iteration methods. USSR Computational Mathematics and Mathematical Physics 4(5), pp. 1–17 (1964). 위의 momentum update인 heavy-ball method입니다. backpropagation이 이 분야에 도달하기 22년 전의 일입니다. ↩