Към съдържанието
3/30Глава 3 от 30

Надолу по склона: gradient descent и двете стъпки, които всички пропускат

Изчислете точния таван на learning rate и вижте как brute-force търсене в 3 600 посоки преоткрива градиента.

На тази страница

Предишната глава завърши с долина.

Не метафорична: истинска крива, loss-ът, нанесен спрямо един параметър, който се спуска надолу и после отново се изкачва. И loss-ът под нея не беше избран, защото е удобен — беше изведен от твърдение за шума в измерванията, а squared error се появи накрая като следствие, не като конвенция.

Значи имаме пейзаж с дъно и причина да вярваме, че дъното е правилното място. Това, което нямаме, е начин да стигнем дотам.

Тази глава изгражда такъв начин и това е алгоритъмът, който обучава всеки model в останалата част на курса — всеки без изключение, включително онези със стотици милиарди параметри. Побира се в около двайсет реда. Двете трудни части не са в тези двайсет реда и те са двете неща, които почти всяко обяснение пропуска:

  • Защо знакът минус. Update-ът изважда градиента. Всеки tutorial го пише; много малко казват защо градиентът е посоката, която води нагоре, а точно това е единственият факт, който прави знака минус нещо различно от акт на вяра.
  • Колко голяма стъпка. „Твърде голяма diverges, твърде малка е бавна“ е вярно и безполезно. Има точно число, то може да се изчисли от loss-а, и тази глава го изчислява два пъти — веднъж за играчкова парабола и веднъж за реалните данни.

Настройката и защо не можете просто да търсите

Връзка към раздела: Настройката и защо не можете просто да търсите

Преразказано така, че тази глава да стои самостоятелно: осемте части от конвейерната лента в Глава 1, но с друг въпрос. Не приеми или отхвърли — това се връща по-късно — а предвиди теглото на частта от нейната ширина.

belt.pyPYTHON
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, и с причина, която ще се върне с лихва преди краят на тази глава. Model-ът е права, y^=ax+b\hat{y} = a x + b, а loss-ът е mean squared error, който предишната глава изведе:

L(a,b)=1ni=1n(axi+byi)2L(a, b) = \frac{1}{n} \sum_{i=1}^{n} \left(a x_i + b - y_i\right)^2

Два параметъра. Защо просто да не пробваме много стойности? Нека наистина го направим — grid от a=0a = 0 до 55 и от b=5b = -5 до 55, със стъпка 0.010.01:

TEXT
grid 501 x 1001 = 501,501 evaluations in 3.67 s
  best found: a = 2.1000, b = -0.0000, L = 24.592450

Половин милион оценки, за да фиксираме две числа до два знака след десетичната запетая — и тази секунда е wall clock на една машина, така че при повторно пускане може да излезе някъде между три и шест; броят оценки и минимумът са частта, която се възпроизвежда. Gradient descent, в края на тази глава, достига четири знака след десетичната запетая за осем стъпки и пълния float64 отговор за трийсет и шест.

Но скоростта не е аргументът, и това е точката, която определя целия курс. Grid search струва kPk^P оценки за PP параметъра при kk стойности за всеки. С хиляда стойности по всяка ос:

modelпараметриgrid оценки
тази права210610^{6}
XOR мрежата от Глава 59102710^{27}
малка multilayer мрежа20,0001060,00010^{60{,}000}

Третият ред не е голямо число, а безсмислено — има приблизително 108010^{80} атома в наблюдаемата Вселена. Търсенето не става по-бавно, когато models растат; то спира да съществува. Всичко, което следва, съществува заради тази таблица.

Производната е измерване, което можете да направите

Връзка към раздела: Производната е измерване, което можете да направите

Фиксирайте b=0b = 0 за момент, така че да има един параметър и една крива — картината, с която ви остави последната глава. Вземете точка върху нея, a=1a = 1, и попитайте: ако побутна aa с малко количество hh, с колко се измества loss-ът на единица побутване?

L(a+h)L(a)h\frac{L(a + h) - L(a)}{h}

Това отношение е rise over run — наклонът на правата линия през две точки на кривата. Когато hh намалява, двете точки се плъзгат една към друга и линията става допирателна. Нейният наклон е производната L(a)L'(a): скоростта, с която loss-ът се променя на единица промяна в aa. Не приближение на нещо и не безкрайно малка величина. Граница на обикновени отношения.

Струва си да го пуснем, защото числата казват нещо, което дефиницията не казва:

slope.pyPYTHON
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}")
TEXT
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

Тук се случват две неща и и двете носят тежестта на аргумента.

Грешката не е смътно пропорционална на hh — тя е точно 7.445h7.445\,h. Разделете hh на сто, грешката се дели на сто, до четири значещи цифри всеки път. Тази константа не е украса: тя е половината от втората производна на loss-а и е първата поява на идея, която идва след две секции — че една крива близо до точка изглежда като права плюс корекция, пропорционална на h2h^2.

А после моделът се чупи. Под h=108h = 10^{-8} оценката става по-лоша, а при 101410^{-14} е грешна още във втората цифра. Нищо математическо не се случи; случи се floating-point кутията от миналата глава. L(a+h)L(a+h) и L(a)L(a) съвпадат в първите си десет цифри, изваждането им унищожава тези цифри, а деленето на останките на миниатюрно число усилва това, което е останало. Има най-добро hh — тук около 10810^{-8}, приблизително квадратният корен на machine epsilon — и да отидете по-малко не е по-внимателно, а по-невнимателно. Запомнете това; функция в края на тази глава зависи от него.

Точният наклон, от calculus, а не от измерване, е 16.385-16.385. Така че можем да спрем да измерваме и да започнем да извеждаме.

Ето идеята, върху която е построен останалият курс, казана веднъж, ясно.

Да композирате две функции означава да подадете едната в другата: (fg)(x)=f(g(x))(f \circ g)(x) = f(g(x)). Нищо повече.

Дълбоката мрежа не е като композиция. Тя е композиция. Layer е функция; подреждането на layers е композирането им; „дълбочина“ е броят функции във веригата. Когато Глава 5 изгражда мрежа, тя изгражда f4f3f2f1f_4 \circ f_3 \circ f_2 \circ f_1 и нищо друго. Което означава, че единственото най-важно правило на calculus, за нашите цели, е онова, което диференцира композиция:

ddxf(g(x))=f(g(x))g(x)\frac{d}{dx} f(g(x)) = f'(g(x)) \cdot g'(x)

Скоростите се умножават. Ако gg се променя три пъти по-бързо от xx, а ff се променя два пъти по-бързо от gg, тогава ff се променя шест пъти по-бързо от xx. Това е цялото съдържание и затова сигнал, който минава назад през десет layers, се умножава по десет числа — поради което Глава 6 отделя секция на това какво се случва, когато всички тези числа са малко по-малки от едно.

Използвайте го върху нашия loss. Запишете residual-а ri=axi+byir_i = a x_i + b - y_i, така че L=1nri2L = \frac{1}{n}\sum r_i^2. Всяко rir_i зависи от aa чрез вътрешната функция axia x_i, чиято производна е xix_i. Chain rule, член по член:

La=1ni2rixi,Lb=1ni2ri1\frac{\partial L}{\partial a} = \frac{1}{n}\sum_i 2 r_i \cdot x_i, \qquad \frac{\partial L}{\partial b} = \frac{1}{n}\sum_i 2 r_i \cdot 1

Тези къдрави символи \partial означават частна производна: диференцирате спрямо една променлива и третирате всяка друга като константа. Нищо ново не се случва — това е същата граница като преди, взета по една ос. Съберете частните производни във вектор и имате градиента:

L=(La, Lb)\nabla L = \left( \frac{\partial L}{\partial a},\ \frac{\partial L}{\partial b} \right)

В точката (a,b)=(1,4)(a, b) = (1, 4) този вектор е (16.385, 8.0)(-16.385,\ 8.0). Две числа. Въпросът е какво означават, и това е първата стъпка, която всички пропускат.

Градиентът е вектор от наклони по осите. Това е всичко, което сме доказали. Не е очевидно — и не бива да е очевидно — че сглобяването им във вектор произвежда нещо, което сочи в някаква конкретна посока.

Затова дефинирайте нещото, което всъщност искаме. Изберете unit vector u\mathbf{u}, посока. Directional derivative е скоростта, с която loss-ът се променя, докато вървите натам:

DuL=limh0L(θ+hu)L(θ)hD_{\mathbf{u}} L = \lim_{h \to 0} \frac{L(\boldsymbol{\theta} + h\mathbf{u}) - L(\boldsymbol{\theta})}{h}

Chain rule превръща това в нещо изчислимо. Ходенето по u\mathbf{u} променя aa със скорост u1u_1 и bb със скорост u2u_2, а приносите се събират:

DuL=Lau1+Lbu2=LuD_{\mathbf{u}} L = \frac{\partial L}{\partial a} u_1 + \frac{\partial L}{\partial b} u_2 = \nabla L \cdot \mathbf{u}

Скоростта на промяна в която и да е посока е dot product на градиента с тази посока. А сега punchline-ът, който е един ред геометрия. Записвайки dot product-а с ъгъла ϕ\phi между векторите,

Lu=Lucosϕ=Lcosϕ\nabla L \cdot \mathbf{u} = \lVert \nabla L \rVert \, \lVert \mathbf{u} \rVert \cos\phi = \lVert \nabla L \rVert \cos\phi

понеже u\mathbf{u} има дължина 1. Единственото, което контролирате, е cosϕ\cos\phi, което е най-голямо при ϕ=0\phi = 0 и най-малко при половин оборот, ϕ=180\phi = 180 градуса. Значи:

  • Най-стръмното изкачване е по самия L\nabla L, а наклонът там е точно L\lVert \nabla L \rVert.
  • Най-стръмното спускане е по L-\nabla L, а наклонът там е L-\lVert \nabla L \rVert.
  • Перпендикулярно на градиента loss-ът изобщо не се променя. Затова линиите на contour map пресичат градиента под прав ъгъл.

Това е знакът минус. Не конвенция, не обръщане на знак, което някой е избрал: посоката на най-бързо намаляване е отрицателният градиент, защото cosϕ\cos\phi се минимизира при половин оборот, и по никаква друга причина.

Тъй като това е твърдение за всички посоки, тествайте го срещу всички посоки. Sample-нете 3,600 от тях, по една на всяка десета от градуса, и измерете всяка чрез побутване:

directions.pyPYTHON
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")
TEXT
gradient       [-16.385   8.   ]
its length     18.23371122399386
its angle      153.97598928042032 degrees
steepest slope 18.233709624837502 at 154.0 degrees

Търсене, което не знае нищо за градиенти, сред 3,600 посоки намира най-стръмното си изкачване при 154.0 градуса — собствената посока на градиента, в рамките на 0.1-градусовата resolution на търсенето. А наклонът, който намира там, 18.2337, е дължината на градиента до шест цифри. Теоремата не е история за това какво означават градиентите; тя е измерим факт, и това е измерването.

Защо малка стъпка надолу наистина помага

Връзка към раздела: Защо малка стъпка надолу наистина помага

Сега втората пропусната стъпка. Знаем накъде е надолу. От това не следва, че ходенето натам намалява loss-а, защото „надолу“ е твърдение за безкрайно малко побутване, а стъпката не е безкрайно малка.

Мостът е линеаризация. Близо до точка гладка функция е нейната допирателна плюс корекция:

L(θ+δ)=L(θ)+Lδ+O(δ2)L(\boldsymbol{\theta} + \boldsymbol{\delta}) = L(\boldsymbol{\theta}) + \nabla L \cdot \boldsymbol{\delta} + O(\lVert\boldsymbol{\delta}\rVert^2)

Това е Taylor expansion от първи ред. Изхвърленото O(δ2)O(\lVert\boldsymbol{\delta}\rVert^2) е кривината — същият член, който направи оценката в таблицата с наклони грешна с точно 7.445h7.445\,h. Поставете стъпката, която възнамеряваме да направим, δ=ηL\boldsymbol{\delta} = -\eta \nabla L:

L(θηL)L(θ)ηL2L(\boldsymbol{\theta} - \eta \nabla L) \approx L(\boldsymbol{\theta}) - \eta \lVert \nabla L \rVert^2

Loss-ът пада с ηL2\eta \lVert \nabla L \rVert^2. Всяка част от това е неотрицателна, така че обещанието е реално — за достатъчно малко η\eta, защото пренебрегнатият член расте като η2\eta^2 и накрая го изяжда. Това е цялата теория. Ето как обещанието се изпълнява, а после се нарушава:

TEXT
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

Четете отдолу. Когато η\eta намалява, доставеният спад converges към обещания — ratio 0.99938, после 0.99994 — което е Taylor's theorem в действие. Четете отгоре и при η=0.2\eta = 0.2 доставеният „спад“ е минус шестнайсет. Стъпката тръгна надолу, а loss-ът се качи.

Значи правилото за update е

θθηL(θ)\boldsymbol{\theta} \leftarrow \boldsymbol{\theta} - \eta \nabla L(\boldsymbol{\theta})

и идва с условие, което никой не казва: η\eta трябва да е достатъчно малко. Достатъчно малко спрямо какво точно — това е следващата секция.

Learning rate има таван и той може да се изчисли

Връзка към раздела: Learning rate има таван и той може да се изчисли

Започнете с най-простата възможна долина, f(x)=x2f(x) = x^2, където f(x)=2xf'(x) = 2x. Една стъпка gradient descent е

xxη2x=x(12η)x \leftarrow x - \eta \cdot 2x = x\,(1 - 2\eta)

Позицията се умножава по (12η)(1 - 2\eta) на всяка стъпка. Това е геометрична редица, а геометричните редици имат точно едно правило: свиват се, когато множителят е по-малък от 1 по абсолютна стойност, и растат иначе. Значи 12η<1\lvert 1 - 2\eta \rvert < 1, което е 0<η<10 < \eta < 1.

Границата е точно при η=1\eta = 1. Не „около 1“, не „1 обикновено е твърде голямо“. При η=1\eta = 1 множителят е 1-1 и точката подскача между xx и x-x завинаги, без да се приближава и без да избяга. Под него — converge; над него — diverge. Интервалът се разделя отново при η=0.5\eta = 0.5, където множителят сменя знак: под това приближаването е монотонно, над него точката overshoots и редува страни, а точно при 0.50.5 множителят е 0 и една-единствена стъпка попада в минимума.

Четири режима от четири реда алгебра. Отидете и прекосете границите сами:

14 стъпки, край при x = -0.0836.

Виж данните като таблица
Стъпкаxf(x)
0⁨-1.9000⁩⁨3.6100⁩
1⁨-1.5200⁩⁨2.3104⁩
2⁨-1.2160⁩⁨1.4787⁩
3⁨-0.9728⁩⁨0.9463⁩
4⁨-0.7782⁩⁨0.6057⁩
5⁨-0.6226⁩⁨0.3876⁩
6⁨-0.4981⁩⁨0.2481⁩
7⁨-0.3985⁩⁨0.1588⁩
8⁨-0.3188⁩⁨0.1016⁩
9⁨-0.2550⁩⁨0.0650⁩
10⁨-0.2040⁩⁨0.0416⁩
11⁨-0.1632⁩⁨0.0266⁩
12⁨-0.1306⁩⁨0.0170⁩
13⁨-0.1045⁩⁨0.0109⁩
14⁨-0.0836⁩⁨0.0070⁩
Градиентен спуск, интерактивно

Четиринайсет стъпки със скорост 0.1, от x=1.9x = -1.9, завършващи при 0.0836-0.0836. Вдигнете скоростта до 0.5 и още първата стъпка попада на дъното. Вдигнете я до 0.9 и тя завършва при същото 0.0836-0.0836 като 0.1 — същото разстояние, противоположен стил, защото 12η\lvert 1 - 2\eta \rvert е 0.8 и за двете — но стига дотам със зигзаг през долината, вместо да върви надолу по едната страна.

А сега интересният случай:

14 стъпки, край при x = -1.9000.

Виж данните като таблица
Стъпкаxf(x)
0⁨-1.9000⁩⁨3.6100⁩
1⁨1.9000⁩⁨3.6100⁩
2⁨-1.9000⁩⁨3.6100⁩
3⁨1.9000⁩⁨3.6100⁩
4⁨-1.9000⁩⁨3.6100⁩
5⁨1.9000⁩⁨3.6100⁩
6⁨-1.9000⁩⁨3.6100⁩
7⁨1.9000⁩⁨3.6100⁩
8⁨-1.9000⁩⁨3.6100⁩
9⁨1.9000⁩⁨3.6100⁩
10⁨-1.9000⁩⁨3.6100⁩
11⁨1.9000⁩⁨3.6100⁩
12⁨-1.9000⁩⁨3.6100⁩
13⁨1.9000⁩⁨3.6100⁩
14⁨-1.9000⁩⁨3.6100⁩
Градиентен спуск, интерактивно

Точно на границата. Четиринайсет стъпки със скорост 1 и завършва при 1.9-1.9: точно там, откъдето е започнала, без да е направила нищо друго освен да подскача. Едно побутване нагоре и подскачането расте, вместо да се задържа; при 1.2 излита извън графиката за четири стъпки. Скорост, която е твърде голяма, не converges бавно. Тя не converges.

Сега общото правило, което изпада от същия аргумент. Множителят 12η1 - 2\eta всъщност беше 1ηf1 - \eta f'', а близо до минимум multi-parameter loss има по едно такова число за всяка посока — eigenvalues на матрицата на вторите производни. Всяка посока трябва да е стабилна едновременно, така че таванът се задава от най-голямата:

η<2λmax\eta < \frac{2}{\lambda_{\max}}

За f(x)=x2f(x) = x^2, f=2f'' = 2, таван 1, което е точно това, което изведохме. За нашата лента матрицата на вторите производни е 2nAA\frac{2}{n} A^{\top} A с AA като двуколонната матрица на inputs, а eigenvalues са 2 и 14.89, така че таванът е 2/14.89=0.134322 / 14.89 = 0.13432. Това е предсказание с пет значещи цифри. Тествайте го:

TEXT
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 UP

Пет знака след десетичната запетая съвпадение между един ред linear algebra и сто хиляди iterations на for loop.

И ето къде Глава 1 се връща. Всичко по-горе използваше центрираните измервания. Пуснете идентичния code върху сурови милиметри и грамове и eigenvalues са 0.0298 и 998.1 вместо 2 и 14.89. Таванът се срива от 0.134 до 0.002004 — също толкова точно, converging при lr=0.002003 и взривявайки се при lr=0.002004.

По-лошо от тавана е отношението между eigenvalues. Condition number измерва колко далеч от кръгла е долината: дълъг тънък ров налага скорост, достатъчно малка за стръмните стени, а после дъното на рова се изминава със същото пълзене. Нашият отива от 7.44 центриран до 33,452 суров. С най-добрата скорост, която всяка версия може да поеме:

featurescondition numberнай-добра скоростстъпки до 1% от optimum
центрирани7.440.118410
сурови милиметри и грамове33,4520.002003779,513

Същите данни, същият code, същият отговор накрая — и осем хиляди пъти повече работа, защото никой не е извадил средната стойност. В Глава 1 същият пропуск струва на perceptron фактор шест хиляди в epochs, а диагнозата там беше геометрична: данните плаваха далеч от началото. Това е същата геометрия тук, преоблечена като оптимизация, и затова input normalisation не е хигиенен съвет, а аритметика.1

Нищо по-горе не се нуждаеше от library. Ето целия optimiser.

descent.pyPYTHON
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))
TEXT
[ 2.10040296e+00 -2.76445533e-15] 24.592448791134984

Closed-form least-squares отговорът за тези осем точки е a=2.100403a = 2.100403, b=0b = 0, с loss 24.59244924.592449. Loop-ът го намери до осем значещи цифри, без да знае, че closed form съществува — което има значение, защото от Глава 5 нататък такъв няма да има.

Траекторията, понеже смисълът е да я гледаме:

TEXT
   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

По-голямата част от разстоянието се покрива в първите две стъпки, защото градиентът е най-голям, когато сте най-далеч от дъното, и намалява, когато се приближавате. Gradient descent се забавя автоматично близо до минимум. Това е feature и също така, в Глава 6, проблем.

Аргументът дотук има дупка. Стъпката спира, когато L=0\nabla L = \mathbf{0}, а ние наричахме това „минимума“. Точка с нулев градиент е критична точка, а да бъде минимум е само един от начините да бъде такава:

  • локален минимум: нагоре във всяка посока, но възможно не най-ниската такава точка изобщо;
  • локален максимум: надолу във всяка посока;
  • седлова точка: нагоре в някои посоки и надолу в други. Повърхността f(x,y)=x2y2f(x,y) = x^2 - y^2 има f=(2x,2y)\nabla f = (2x, -2y), което е нула в началото, където функцията е минимум по оста xx и максимум по оста yy едновременно.

Gradient descent не може да ги различи, защото винаги гледа само градиента, а градиентът е нула и при трите.

Нашата права има една критична точка и тя е отговорът — squared-error loss върху линеен model е convex, една-единствена купа, и descent върху него не може да не намери глобалния минимум. Това свойство не оцелява при срещата с този курс. Loss-ът на neural network не е convex, и от Глава 5 нататък „минимумът“ не е нещо, което съществува: има много, с различни дълбочини, и кой ще получите зависи от това откъде сте започнали. Това е едно изречение и остава едно изречение, защото теорията е голяма, а практическото следствие е малко.

Можете да видите цялото следствие върху една крива. Вземете f(x)=x44x22+x10f(x) = \tfrac{x^4}{4} - \tfrac{x^2}{2} + \tfrac{x}{10}, която има две долини с различна дълбочина:

TEXT
   x =  -1.046681   f(x) =  -0.352386   minimum
   x =   0.101031   f(x) =   0.005026   maximum
   x =   0.945649   f(x) =  -0.152639   minimum

40 стъпки, край при x = 0.9456.

Виж данните като таблица
Стъпкаxf(x)
0⁨0.1100⁩⁨0.0050⁩
1⁨0.1122⁩⁨0.0050⁩
2⁨0.1149⁩⁨0.0049⁩
3⁨0.1182⁩⁨0.0049⁩
4⁨0.1223⁩⁨0.0048⁩
5⁨0.1275⁩⁨0.0047⁩
6⁨0.1338⁩⁨0.0045⁩
7⁨0.1416⁩⁨0.0042⁩
8⁨0.1513⁩⁨0.0038⁩
9⁨0.1633⁩⁨0.0032⁩
10⁨0.1781⁩⁨0.0022⁩
11⁨0.1962⁩⁨0.0007⁩
12⁨0.2183⁩⁨-0.0014⁩
13⁨0.2453⁩⁨-0.0046⁩
14⁨0.2779⁩⁨-0.0093⁩
15⁨0.3170⁩⁨-0.0160⁩
16⁨0.3633⁩⁨-0.0253⁩
17⁨0.4172⁩⁨-0.0377⁩
18⁨0.4783⁩⁨-0.0535⁩
19⁨0.5455⁩⁨-0.0721⁩
20⁨0.6163⁩⁨-0.0922⁩
21⁨0.6869⁩⁨-0.1116⁩
22⁨0.7526⁩⁨-0.1277⁩
23⁨0.8092⁩⁨-0.1393⁩
24⁨0.8540⁩⁨-0.1463⁩
25⁨0.8868⁩⁨-0.1499⁩
26⁨0.9091⁩⁨-0.1516⁩
27⁨0.9236⁩⁨-0.1522⁩
28⁨0.9325⁩⁨-0.1525⁩
29⁨0.9379⁩⁨-0.1526⁩
30⁨0.9411⁩⁨-0.1526⁩
31⁨0.9430⁩⁨-0.1526⁩
32⁨0.9441⁩⁨-0.1526⁩
33⁨0.9448⁩⁨-0.1526⁩
34⁨0.9451⁩⁨-0.1526⁩
35⁨0.9454⁩⁨-0.1526⁩
36⁨0.9455⁩⁨-0.1526⁩
37⁨0.9455⁩⁨-0.1526⁩
38⁨0.9456⁩⁨-0.1526⁩
39⁨0.9456⁩⁨-0.1526⁩
40⁨0.9456⁩⁨-0.1526⁩
Градиентен спуск, интерактивно

Четирийсет стъпки от x=0.11x = 0.11, установявайки се при 0.94560.9456 — по-плитката от двете долини. Сега преместете началната точка една идея наляво, до 0.100.10. Същата скорост, същите четирийсет стъпки, и се установява при 1.0461-1.0461 вместо това, където loss-ът е 0.199747 по-нисък. Водоразделът е гърбицата при 0.1010310.101031, и цялата разлика между двата отговора е от коя нейна страна случайно сте започнали.

Попадането в плитката долина е с 56.7% по-лошо като loss, а алгоритъмът няма как да знае, защото отвътре на долина всяка посока е нагоре. Няма поправка за това в gradient descent и такава няма да дойде. Това, което има на практика, е откритието, че то има далеч по-малко значение, отколкото тази картина подсказва — във много високите измерения на реална мрежа повечето критични точки се оказват седла, а не капани,2 и Глава 5 измерва колко често една малка мрежа наистина засяда.

Едно нещо за grad по-горе трябва да ви притесни: то сумира върху целия dataset за всяка стъпка. Осем части са нищо. Един милион е един милион gradient computations, за да преместите параметрите веднъж.

Изходът е, че градиентът е средна стойност, а средна стойност може да се оцени от sample. Изчислете го върху случайна шепа — minibatch — и направете стъпка по него. Оценката е шумна; тя е и unbiased, а стотици евтини шумни стъпки побеждават една скъпа точна. Върху сто хиляди synthetic части, броейки per-example gradients вместо стъпки:

methodстъпки до 0.1% от optimumper-example gradients
full batch7700,000
minibatch от 321003,200
по един пример наведнъж17,58017,580

Двеста и деветнайсет пъти по-малко аритметика, за да стигнете до същото място. А крайността — по един пример наведнъж, оригиналната stochastic approximation на Robbins и Monro3не е победителят: тя е пет пъти по-лоша от batch-ове по 32, защото 32 примера струват почти нищо повече от един върху hardware, който умножава матрици, докато шумът спада с квадратния корен от batch size. Този trade-off е причината всеки training script, който някога ще прочетете, да има batch_size в него.

Momentum е другата евтина поправка и е насочена право към рова. В зле conditioned долина стъпките правят зигзаг през тясната посока, докато пълзят по дългата. Momentum държи running average на минали градиенти, така че осцилиращите компоненти се cancel-ват, а постоянната се натрупва:4

vβv+L(θ),θθηv\mathbf{v} \leftarrow \beta \mathbf{v} + \nabla L(\boldsymbol{\theta}), \qquad \boldsymbol{\theta} \leftarrow \boldsymbol{\theta} - \eta \mathbf{v}

Два допълнителни реда. Върху суровата нецентрирана лента — condition number 33,452, най-лошият случай, който имаме — при най-добрата скорост, която plain descent може да поеме:

TEXT
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%

Фактор 172 за два реда code. Глава 6 превръща това в Adam; механизмът вече е тук.

Всеки градиент в тази глава беше изведен на ръка и следователно може да е грешен. Поправката е таблицата с наклони от началото: измерете производната числено и сравнете. Използвайте central difference, L(θ+h)L(θh)2h\frac{L(\theta+h) - L(\theta-h)}{2h}, която cancel-ва водещия член на грешката и е много по-точна за същото hh.

gradcheck.pyPYTHON
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)))

Относителната форма на сравнението има значение: абсолютна разлика от 10410^{-4} е катастрофа при градиент с размер 10310^{-3} и е irrelevant при такъв с размер 10610^{6}.

TEXT
relative error: 1.8929136036763527e-11
with 2 dropped: 0.33333333331650744

Първият ред е ръчно изведеният градиент по-горе. Вторият е същата функция с изпуснат фактор 2 в един компонент — typo от един-единствен символ — и проверката го хваща веднага. Всичко под около 10710^{-7} е съвпадение; всичко над 10410^{-4} е bug. Запазете тази функция: Глава 5 я използва, за да debug-ва automatic differentiation engine, и тя е единствената причина грешен градиент изобщо да може да бъде намерен.

Всичко в тази глава се опираше на едно предположение, което никога не беше изречено: че можете да запишете L/θ\partial L / \partial \theta.

За права с два параметъра това беше един ред алгебра. Почти веднага спира да бъде такъв. Попитайте symbolic algebra system за производната на loss-а на мрежа спрямо един-единствен first-layer weight, за един-единствен пример, и пребройте аритметиката в отговора:

networkоперации в една частна производна
четири hidden units, един layer40
четири hidden units, два layers301
четири hidden units, три layers1,717

Третият ред е мрежа с 57 параметъра — мрежа толкова малка, че би била бележка под линия в Глава 6 — а изписването на нейния градиент на ръка означава около 97,869 операции за един training example. Няма notation, която да спаси това. Спасението е наблюдението, че chain rule, приложен към композиция, има огромна структура, че едни и същи междинни величини се появяват отново и отново, и че изчисляването им в правилния ред дава всички производни за приблизително цената на един forward pass. Това е Глава 5.

Но първо има по-малък проблем и той чака веднага.

Сега имаме машина, която ще се търкаля надолу по всеки differentiable loss. Насочете я към първоначалния въпрос за лентата — приеми или отхвърли, target, който е 1 или 0 — сложете sigmoid на output-а, за да предсказва вероятност, и минимизирайте squared error. Тя ще тръгне. Но и почти няма да се движи, когато греши най-много, и градиентът казва защо:

output zzpredictiontruthgradient със squared errorgradient с cross-entropy
000.500012.5×1012.5 \times 10^{-1}5.0×1015.0 \times 10^{-1}
2-20.119211.850×1011.850 \times 10^{-1}8.808×1018.808 \times 10^{-1}
6-60.002514.921×1034.921 \times 10^{-3}9.975×1019.975 \times 10^{-1}
10-104.54×1054.54 \times 10^{-5}19.079×1059.079 \times 10^{-5}1.0001.000

Model, който е уверено, катастрофално грешен — предсказва 0.0000454, когато отговорът е 1 — произвежда squared-error gradient от 9×1059 \times 10^{-5}. Той няма представа, че е в беда. Другата колона, от loss, който още не сме извели, отчита 1.0: максимална спешност, точно там, където е заслужена.

Което повдига въпроса, с който започва следващата глава. Последната глава каза, че loss е предположение за шума, а squared error предполага Gaussian noise. Какъв noise model има отговор „да“ или „не“ — и какъв loss излиза, когато приложите същото извеждане върху него?


Методът е по-стар от всички тях: Cauchy го описва в бележка до Académie des Sciences през 1847 г. като начин за решаване на системи от уравнения чрез ходене надолу по сумата от квадратите на residual-ите им. Също си струва да прочетете заедно с тази глава: An overview of gradient descent optimization algorithms на Sebastian Ruder (arXiv:1609.04747), който покрива momentum до Adam в четиринайсет четивни страници; глава 3 от Numerical Optimization на Nocedal и Wright (2nd ed., Springer, 2006), чиято theorem 3.3 дава convergence rate на steepest descent върху quadratic чрез condition number — това е теорията зад причината conditioning да определя броя стъпки, макар да разглежда line search, а не fixed-step тавана 2/λmax2/\lambda_{\max}, измерен по-горе; или §5.8 и §7.1 от Mathematics for Machine Learning на Deisenroth, Faisal и Ong за същата територия с по-малко machinery; §6.1 от Understanding Deep Learning на Prince и §4.3 от Deep Learning на Goodfellow, Bengio и Courville; Dive into Deep Learning §12.1–12.3, където има minibatch analysis с повече измервания, отколкото има място тук; и глава 4 от Hands-On Machine Learning на Géron (3rd ed.), най-практичното разглеждане на learning rate като нещо, което tune-вате, вместо да извеждате. Бележките на MIT 6.390 поставят gradient descent преди classification, както прави този курс, и по същата причина.

  1. 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 по-горе: centring и scaling на inputs променя eigenvalues на матрицата на вторите производни и следователно броя стъпки, не просто числения комфорт.

  2. 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). Аргументът, че във високи измерения критичните точки са преобладаващо седла, а не локални минимуми, тъй като минимум изисква всяка една от хиляди посоки да се извива нагоре едновременно.

  3. Robbins, H. and Monro, S. A Stochastic Approximation Method. Annals of Mathematical Statistics 22(3), pp. 400–407 (1951). Статията, която установява, че шумна оценка на градиент е достатъчна, при step size, който намалява по правилния начин.

  4. 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). Heavy-ball методът, който е momentum update-ът по-горе, двайсет и две години преди backpropagation да достигне това поле.

Готови ли сте LIA да избира вместо вас?

Създавайте с всички AI модели на едно място — започнете безплатно още днес.