Przejdź do treści
3/30Rozdział 3 z 30

W dół: Gradient Descent i dwa kroki, które wszyscy pomijają

Wylicz dokładny pułap współczynnika uczenia, a potem zobacz, jak brute-force po 3 600 kierunkach odkrywa gradient bez podpowiedzi.

Na tej stronie

Poprzedni rozdział skończył się doliną.

Nie metaforyczną: prawdziwą krzywą, stratą wykreśloną względem jednego parametru, opadającą w dół i wracającą w górę. A leżąca pod nią strata nie została wybrana dlatego, że była wygodna — została wyprowadzona z założenia o szumie w pomiarach, a błąd kwadratowy pojawił się na końcu jako konsekwencja, nie konwencja.

Mamy więc krajobraz z dnem i powód, by wierzyć, że dno jest właściwym miejscem. Nie mamy za to sposobu, żeby tam dotrzeć.

Ten rozdział taki sposób buduje, a jest nim algorytm, który trenuje każdy model w dalszej części kursu — każdy bez wyjątku, aż po te z setkami miliardów parametrów. Mieści się w około dwudziestu liniach. Dwie trudne części nie znajdują się w tych dwudziestu liniach i są dokładnie tymi dwiema rzeczami, które pomija niemal każde wyjaśnienie:

  • Dlaczego znak minus. Aktualizacja odejmuje gradient. Każdy tutorial to zapisuje; bardzo niewiele mówi, dlaczego gradient jest kierunkiem prowadzącym w górę, a tylko ten fakt sprawia, że znak minus jest czymś więcej niż aktem wiary.
  • Jak duży krok. „Zbyt duży powoduje rozbieżność, zbyt mały jest wolny” — to prawda i zarazem bezużyteczna rada. Istnieje dokładna liczba, da się ją obliczyć ze straty, a ten rozdział oblicza ją dwa razy — raz dla zabawkowej paraboli i raz dla rzeczywistych danych.

Układ problemu i dlaczego nie możesz po prostu szukać

Link do sekcji: Układ problemu i dlaczego nie możesz po prostu szukać

Przypomnijmy tak, żeby ten rozdział był samodzielny: osiem części z taśmy produkcyjnej z Rozdziału 1, ale z innym pytaniem. Nie zaakceptować czy odrzucić — to wróci później — tylko przewidzieć wagę części na podstawie jej szerokości.

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

Pomiary są wycentrowane, dokładnie tak jak w Rozdziale 1 i z powodu, który zwróci się z odsetkami przed końcem tego rozdziału. Model jest prostą, y^=ax+b\hat{y} = a x + b, a strata to średni błąd kwadratowy wyprowadzony w poprzednim rozdziale:

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

Dwa parametry. Dlaczego nie spróbować po prostu wielu wartości? Zróbmy to naprawdę — siatka od a=0a = 0 do 55 i od b=5b = -5 do 55, krokiem 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

Pół miliona obliczeń straty, żeby ustalić dwie liczby z dokładnością do dwóch miejsc po przecinku — a ta sekunda to czas zegarowy na jednej maszynie, więc powtórka wyląduje gdzieś między trzema a sześcioma; liczba ewaluacji i minimum to część, która się odtwarza. Gradient descent, na końcu tego rozdziału, dostaje cztery miejsca po przecinku w ośmiu krokach i pełną odpowiedź float64 w trzydziestu sześciu.

Ale szybkość nie jest argumentem — i to jest punkt, który rozstrzyga o całym kursie. Grid search kosztuje kPk^P ewaluacji dla PP parametrów przy kk wartościach na każdy. Przy tysiącu wartości na oś:

modelparametryewaluacje siatki
ta prosta210610^{6}
sieć XOR z Rozdziału 59102710^{27}
mała sieć wielowarstwowa20,0001060,00010^{60{,}000}

Trzeci wiersz nie jest dużą liczbą, tylko liczbą pozbawioną sensu — w obserwowalnym wszechświecie jest mniej więcej 108010^{80} atomów. Wraz ze wzrostem modeli przeszukiwanie nie staje się wolniejsze; ono przestaje istnieć. Wszystko, co następuje, istnieje przez tę tabelę.

Pochodna jest pomiarem, który możesz wykonać

Link do sekcji: Pochodna jest pomiarem, który możesz wykonać

Na chwilę ustal b=0b = 0, żeby został jeden parametr i jedna krzywa — obraz, z którym zostawił cię poprzedni rozdział. Weź na niej punkt, a=1a = 1, i zapytaj: jeśli przesunę aa o małą wartość hh, o ile przesunie się strata na jednostkę tego przesunięcia?

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

Ten iloraz to przyrost pionowy do poziomego — nachylenie prostej przechodzącej przez dwa punkty krzywej. Gdy hh maleje, dwa punkty zsuwają się ku sobie, a prosta staje się styczną. Jej nachylenie to pochodna L(a)L'(a): tempo, w jakim strata zmienia się na jednostkę zmiany aa. Nie przybliżenie czegokolwiek i nie nieskończenie mała wielkość. Granica zwykłych ilorazów.

Warto to uruchomić, bo liczby mówią coś, czego sama definicja nie mówi:

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

Dzieją się tu dwie rzeczy i obie podtrzymują całą konstrukcję.

Błąd nie jest mgliście proporcjonalny do hh — jest dokładnie 7.445h7.445\,h. Podziel hh przez sto, błąd dzieli się przez sto, za każdym razem do czterech cyfr znaczących. Ta stała nie jest ozdobą: to połowa drugiej pochodnej straty i pierwsze pojawienie się idei z dwóch sekcji dalej — że krzywa w pobliżu punktu wygląda jak prosta plus poprawka proporcjonalna do h2h^2.

A potem wzorzec się psuje. Poniżej h=108h = 10^{-8} estymata staje się gorsza, a przy 101410^{-14} myli się już na drugiej cyfrze. Nie wydarzyło się nic matematycznego; zadziałało pudełko zmiennoprzecinkowe z poprzedniego rozdziału. L(a+h)L(a+h) i L(a)L(a) zgadzają się w pierwszych dziesięciu cyfrach, ich odjęcie niszczy te cyfry, a podzielenie szczątków przez maleńką liczbę wzmacnia to, co zostało. Istnieje najlepsze hh — tutaj około 10810^{-8}, mniej więcej pierwiastek z epsilonu maszynowego — i zejście niżej nie jest ostrożniejsze, tylko mniej ostrożne. Zapamiętaj to; funkcja na końcu tego rozdziału od tego zależy.

Dokładne nachylenie, z rachunku różniczkowego zamiast pomiaru, wynosi 16.385-16.385. Możemy więc przestać mierzyć i zacząć wyprowadzać.

Oto idea, na której opiera się reszta kursu, wypowiedziana raz i prosto.

Złożyć dwie funkcje to podać jedną do drugiej: (fg)(x)=f(g(x))(f \circ g)(x) = f(g(x)). Nic więcej.

Głęboka sieć nie jest jak złożenie. Ona jest złożeniem. Warstwa jest funkcją; układanie warstw to ich składanie; „głębokość” to liczba funkcji w łańcuchu. Gdy Rozdział 5 buduje sieć, buduje f4f3f2f1f_4 \circ f_3 \circ f_2 \circ f_1 i nic innego. Co oznacza, że dla naszych celów najważniejsza pojedyncza reguła rachunku różniczkowego to ta, która różniczkuje złożenie:

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

Tempa się mnożą. Jeśli gg zmienia się trzy razy szybciej niż xx, a ff zmienia się dwa razy szybciej niż gg, to ff zmienia się sześć razy szybciej niż xx. To cała treść i dlatego sygnał przechodzący wstecz przez dziesięć warstw zostaje pomnożony przez dziesięć liczb — właśnie dlatego Rozdział 6 poświęca sekcję temu, co się dzieje, gdy wszystkie te liczby są odrobinę mniejsze od jedności.

Użyjmy tego na naszej stracie. Zapisz residuum ri=axi+byir_i = a x_i + b - y_i, tak że L=1nri2L = \frac{1}{n}\sum r_i^2. Każde rir_i zależy od aa przez funkcję wewnętrzną axia x_i, której pochodna wynosi xix_i. Reguła łańcuchowa, wyraz po wyrazie:

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

Te kręcone symbole \partial oznaczają pochodną cząstkową: różniczkujesz względem jednej zmiennej, a wszystkie pozostałe traktujesz jako stałe. Nie dzieje się nic nowego — to ta sama granica co wcześniej, tylko wzięta wzdłuż jednej osi. Zbierz pochodne cząstkowe w wektor i masz gradient:

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

W punkcie (a,b)=(1,4)(a, b) = (1, 4) ten wektor wynosi (16.385, 8.0)(-16.385,\ 8.0). Dwie liczby. Pytanie brzmi, co znaczą — i to jest pierwszy krok, który wszyscy pomijają.

Dlaczego gradient wskazuje pod górę

Link do sekcji: Dlaczego gradient wskazuje pod górę

Gradient jest wektorem nachyleń wzdłuż osi. Tyle udowodniliśmy. Nie jest oczywiste — i nie powinno być oczywiste — że złożenie ich w wektor tworzy coś, co wskazuje jakikolwiek konkretny kierunek.

Zdefiniujmy więc to, czego naprawdę chcemy. Wybierz wektor jednostkowy u\mathbf{u}, czyli kierunek. Pochodna kierunkowa to tempo zmiany straty, gdy idziesz w tę stronę:

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

Reguła łańcuchowa zamienia to w coś obliczalnego. Marsz wzdłuż u\mathbf{u} zmienia aa z tempem u1u_1 i bb z tempem u2u_2, a wkłady się sumują:

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}

Tempo zmiany w dowolnym kierunku jest iloczynem skalarnym gradientu z tym kierunkiem. A teraz puenta, jedna linia geometrii. Zapisując iloczyn skalarny przez kąt ϕ\phi między wektorami,

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

ponieważ u\mathbf{u} ma długość 1. Jedyną rzeczą, którą kontrolujesz, jest cosϕ\cos\phi, największy przy ϕ=0\phi = 0 i najmniejszy po półobrocie, przy ϕ=180\phi = 180 stopniach. Zatem:

  • Najstromsze wznoszenie jest wzdłuż samego L\nabla L, a nachylenie wynosi tam dokładnie L\lVert \nabla L \rVert.
  • Najstromsze opadanie jest wzdłuż L-\nabla L, a nachylenie wynosi tam L-\lVert \nabla L \rVert.
  • Prostopadle do gradientu strata w ogóle się nie zmienia. Dlatego linie na mapie konturowej przecinają gradient pod kątem prostym.

To jest znak minus. Nie konwencja, nie odwrócenie znaku wybrane przez kogoś: kierunek najszybszego spadku to ujemny gradient, ponieważ cosϕ\cos\phi jest minimalizowane po półobrocie, i z żadnego innego powodu.

Skoro to twierdzenie dotyczy wszystkich kierunków, przetestujmy je na wszystkich kierunkach. Wylosuj 3 600 z nich, jeden co jedną dziesiątą stopnia, i zmierz każdy przez małe przesunięcie:

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

Przeszukiwanie, które nic nie wie o gradientach, po 3 600 kierunkach znajduje najstromsze podejście przy 154,0 stopniach — we własnym kierunku gradientu, z dokładnością do rozdzielczości przeszukiwania 0,1 stopnia. A nachylenie, które tam znajduje, 18,2337, to długość gradientu do sześciu cyfr. Twierdzenie nie jest opowieścią o tym, co znaczą gradienty; jest mierzalnym faktem, a to jest pomiar.

Dlaczego mały krok w dół naprawdę pomaga

Link do sekcji: Dlaczego mały krok w dół naprawdę pomaga

Teraz drugi pomijany krok. Wiemy, w którą stronę jest w dół. Nie wynika z tego, że pójście w tę stronę obniży stratę, bo „w dół” jest stwierdzeniem o nieskończenie małym przesunięciu, a krok nie jest nieskończenie mały.

Mostem jest linearyzacja. W pobliżu punktu gładka funkcja jest swoją styczną plus poprawka:

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

To rozwinięcie Taylora pierwszego rzędu. Odrzucony składnik O(δ2)O(\lVert\boldsymbol{\delta}\rVert^2) to krzywizna — ten sam składnik, który sprawił, że estymata w tabeli nachyleń myliła się dokładnie o 7.445h7.445\,h. Wstawmy krok, który zamierzamy wykonać, δ=η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

Strata spada o ηL2\eta \lVert \nabla L \rVert^2. Każda część tego wyrażenia jest nieujemna, więc obietnica jest prawdziwa — dla dostatecznie małego η\eta, bo pominięty składnik rośnie jak η2\eta^2 i w końcu go pożera. To cała teoria. Oto obietnica dotrzymana, a potem złamana:

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

Czytaj od dołu. Gdy η\eta maleje, rzeczywisty spadek zbiega do obiecanego — stosunek 0,99938, potem 0,99994 — czyli twierdzenie Taylora ma rację. Czytaj od góry i przy η=0.2\eta = 0.2 dostarczony „spadek” wynosi minus szesnaście. Krok poszedł w dół, a strata poszła w górę.

Reguła aktualizacji brzmi więc

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

i ma warunek, którego nikt nie wypowiada: η\eta musi być dostatecznie małe. Dostatecznie małe względem czego, dokładnie, to następna sekcja.

Współczynnik uczenia ma pułap i da się go obliczyć

Link do sekcji: Współczynnik uczenia ma pułap i da się go obliczyć

Zacznij od najprostszej doliny, f(x)=x2f(x) = x^2, gdzie f(x)=2xf'(x) = 2x. Jeden krok gradient descent to

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

Położenie jest w każdym kroku mnożone przez (12η)(1 - 2\eta). To ciąg geometryczny, a ciągi geometryczne mają dokładnie jedną regułę: maleją, gdy mnożnik ma wartość bezwzględną mniejszą niż 1, i rosną w przeciwnym razie. Zatem 12η<1\lvert 1 - 2\eta \rvert < 1, czyli 0<η<10 < \eta < 1.

Granica jest dokładnie przy η=1\eta = 1. Nie „około 1”, nie „1 zwykle jest za duże”. Przy η=1\eta = 1 mnożnik wynosi 1-1, a punkt odbija się między xx i x-x na zawsze, ani się nie zbliżając, ani nie uciekając. Poniżej — zbieżność; powyżej — rozbieżność. Przedział dzieli się jeszcze raz przy η=0.5\eta = 0.5, gdzie mnożnik zmienia znak: poniżej podejście jest monotoniczne, powyżej punkt przestrzeliwuje i zmienia strony, a dokładnie przy 0.50.5 mnożnik wynosi 0 i jeden jedyny krok ląduje w minimum.

Cztery reżimy z czterech linijek algebry. Przekrocz granice samodzielnie:

Liczba kroków: 14, koniec przy x = -0.0836.

Zobacz dane w tabeli
Krokxf(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⁩
Interaktywny spadek gradientowy

Czternaście kroków przy współczynniku 0,1, od x=1.9x = -1.9, kończących się w 0.0836-0.0836. Podnieś współczynnik do 0,5, a pierwszy krok ląduje na dnie. Podnieś go do 0,9, a skończy w tym samym 0.0836-0.0836 co 0,1 — ta sama odległość, przeciwny styl, bo 12η\lvert 1 - 2\eta \rvert wynosi 0,8 w obu przypadkach — ale dociera tam, zygzakując przez dolinę zamiast schodzić jedną stroną.

A teraz ciekawy przypadek:

Liczba kroków: 14, koniec przy x = -1.9000.

Zobacz dane w tabeli
Krokxf(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⁩
Interaktywny spadek gradientowy

Dokładnie na granicy. Czternaście kroków przy współczynniku 1 i kończy w 1.9-1.9: dokładnie tam, gdzie zaczęło, nie robiąc nic poza odbijaniem. Jedno minimalne zwiększenie i odbicia rosną zamiast się utrzymywać; przy 1,2 znika z wykresu w czterech krokach. Współczynnik, który jest za duży, nie zbiega wolno. On nie zbiega.

Teraz reguła ogólna, która wypada z tego samego argumentu. Mnożnik 12η1 - 2\eta był tak naprawdę 1ηf1 - \eta f'', a blisko minimum strata z wieloma parametrami ma po jednej takiej liczbie na kierunek — wartości własne macierzy drugich pochodnych. Każdy kierunek musi być stabilny jednocześnie, więc pułap wyznacza największa:

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

Dla f(x)=x2f(x) = x^2, f=2f'' = 2, pułap 1, czyli dokładnie to, co właśnie wyprowadziliśmy. Dla naszej taśmy macierz drugich pochodnych to 2nAA\frac{2}{n} A^{\top} A, gdzie AA jest dwukolumnową macierzą wejść, a jej wartości własne to 2 i 14,89, więc pułap wynosi 2/14.89=0.134322 / 14.89 = 0.13432. To przewidywanie z pięcioma cyframi znaczącymi. Sprawdźmy je:

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

Pięć miejsc po przecinku zgodności między linijką algebry liniowej a stu tysiącami iteracji pętli for.

I tutaj wraca Rozdział 1. Wszystko powyżej używało wycentrowanych pomiarów. Uruchom identyczny kod na surowych milimetrach i gramach, a wartości własne wyniosą 0,0298 i 998,1 zamiast 2 i 14,89. Pułap zapada się z 0,134 do 0,002004 — równie dokładnie, zbiegając przy lr=0.002003 i eksplodując przy lr=0.002004.

Gorszy od samego pułapu jest stosunek między wartościami własnymi. Liczba uwarunkowania mierzy, jak daleka od okrągłej jest dolina: długa, cienka rynna wymusza współczynnik dość mały dla stromych ścian, a potem dnem tej rynny idzie się tym samym ślimaczym tempem. U nas rośnie z 7,44 po wycentrowaniu do 33 452 na surowych danych. Przy najlepszym współczynniku, jaki może przyjąć każda wersja:

cechyliczba uwarunkowanianajlepszy współczynnikkroki do 1% od optimum
wycentrowane7.440.118410
surowe milimetry i gramy33,4520.002003779,513

Te same dane, ten sam kod, ta sama odpowiedź na końcu — i osiem tysięcy razy więcej pracy, bo nikt nie odjął średniej. W Rozdziale 1 to samo pominięcie kosztowało perceptron sześciotysięczny mnożnik w epokach, a diagnoza była tam geometryczna: dane dryfowały daleko od początku układu. To ta sama geometria w kostiumie optymalizacji i dlatego normalizacja wejść nie jest radą higieniczną, tylko arytmetyką.1

Nic z powyższego nie wymagało biblioteki. Oto cały optymalizator.

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

Zamknięte rozwiązanie najmniejszych kwadratów dla tych ośmiu punktów to a=2.100403a = 2.100403, b=0b = 0, ze stratą 24.59244924.592449. Pętla znalazła je do ośmiu cyfr znaczących, nie wiedząc, że istnieje postać zamknięta — co ma znaczenie, bo od Rozdziału 5 dalej już jej nie będzie.

Trajektoria, bo o obserwowanie jej chodzi:

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

Większość odległości zostaje pokonana w pierwszych dwóch krokach, bo gradient jest największy wtedy, gdy jesteś najdalej od dna, i maleje, gdy się zbliżasz. Gradient descent automatycznie zwalnia w pobliżu minimum. To zaleta, a w Rozdziale 6 także problem.

Gdzie jeszcze nachylenie jest zerowe

Link do sekcji: Gdzie jeszcze nachylenie jest zerowe

Dotychczasowy argument ma dziurę. Krok zatrzymuje się, gdy L=0\nabla L = \mathbf{0}, a my nazywaliśmy to „minimum”. Punkt z zerowym gradientem to punkt krytyczny, a bycie minimum jest tylko jednym ze sposobów, by nim być:

  • minimum lokalne: pod górę w każdym kierunku, ale możliwe, że nie najniższy taki punkt gdziekolwiek;
  • maksimum lokalne: w dół w każdym kierunku;
  • punkt siodłowy: pod górę w jednych kierunkach i w dół w innych. Powierzchnia f(x,y)=x2y2f(x,y) = x^2 - y^2 ma f=(2x,2y)\nabla f = (2x, -2y), które jest zerem w początku układu, gdzie funkcja jest minimum wzdłuż osi xx i maksimum wzdłuż osi yy jednocześnie.

Gradient descent nie potrafi ich odróżnić, bo zawsze patrzy tylko na gradient, a gradient jest zerowy we wszystkich trzech.

Nasza prosta ma jeden punkt krytyczny i jest nim odpowiedź — strata błędu kwadratowego dla modelu liniowego jest wypukła, pojedynczą miską, a zejście po niej nie może nie znaleźć globalnego minimum. Ta własność nie przetrwa kontaktu z tym kursem. Strata sieci neuronowej nie jest wypukła, a od Rozdziału 5 dalej „minimum” nie jest rzeczą, która istnieje: jest ich wiele, o różnych głębokościach, a to, które dostaniesz, zależy od punktu startowego. To jest jedno zdanie i pozostaje jednym zdaniem, bo teoria jest rozległa, a praktyczna konsekwencja mała.

Całą konsekwencję możesz zobaczyć na jednej krzywej. Weź f(x)=x44x22+x10f(x) = \tfrac{x^4}{4} - \tfrac{x^2}{2} + \tfrac{x}{10}, która ma dwie doliny o różnych głębokościach:

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

Liczba kroków: 40, koniec przy x = 0.9456.

Zobacz dane w tabeli
Krokxf(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⁩
Interaktywny spadek gradientowy

Czterdzieści kroków od x=0.11x = 0.11, stabilizujące się przy 0.94560.9456 — płytszej z dwóch dolin. Teraz przesuń punkt startowy o jeden ząbek w lewo, do 0.100.10. Ten sam współczynnik, te same czterdzieści kroków, a stabilizuje się przy 1.0461-1.0461, gdzie strata jest o 0.199747 niższa. Dział wodny to garb przy 0.1010310.101031, a cała różnica między dwoma odpowiedziami sprowadza się do tego, po której jego stronie przypadkiem zacząłeś.

Lądowanie w płytkiej dolinie daje stratę gorszą o 56,7%, a algorytm nie ma jak tego wiedzieć, bo z wnętrza doliny każdy kierunek prowadzi pod górę. W gradient descent nie ma na to naprawy i żadna nie nadchodzi. W praktyce jest za to obserwacja, że ma to znacznie mniejsze znaczenie, niż sugeruje ten obraz — w bardzo wysokich wymiarach prawdziwej sieci większość punktów krytycznych okazuje się siodłami, nie pułapkami,2 a Rozdział 5 mierzy, jak często mała sieć naprawdę grzęźnie.

Tańsze kroki: stochastic, minibatch, momentum

Link do sekcji: Tańsze kroki: stochastic, minibatch, momentum

Jedna rzecz w grad powyżej powinna cię niepokoić: sumuje cały zbiór danych dla każdego kroku. Osiem części to nic. Milion to milion obliczeń gradientu, żeby raz przesunąć parametry.

Wyjście polega na tym, że gradient jest średnią, a średnią można oszacować z próby. Oblicz go na losowej garści — minibatch — i wykonaj krok na tej podstawie. Estymata jest zaszumiona; jest też nieobciążona, a setki tanich, zaszumionych kroków wygrywają z jednym drogim, dokładnym. Na stu tysiącach syntetycznych części, licząc gradienty per przykład zamiast kroków:

metodakroki do 0,1% od optimumgradienty per przykład
full batch7700,000
minibatch po 321003,200
jeden przykład naraz17,58017,580

Dwieście dziewiętnaście razy mniej arytmetyki, żeby dotrzeć w to samo miejsce. A skrajność — jeden przykład naraz, oryginalna aproksymacja stochastyczna Robbinsa i Monro3nie wygrywa: jest pięć razy gorsza niż batch po 32, bo 32 przykłady kosztują prawie tyle samo co jeden na sprzęcie mnożącym macierze, a szum maleje z pierwiastkiem rozmiaru batcha. Ten kompromis jest powodem, dla którego każdy skrypt treningowy, jaki kiedykolwiek przeczytasz, ma w sobie batch_size.

Momentum to druga tania poprawka, wymierzona prosto w rynnę. W źle uwarunkowanej dolinie kroki zygzakują przez wąski kierunek, pełznąc wzdłuż długiego. Momentum utrzymuje bieżącą średnią przeszłych gradientów, więc oscylujące składowe się znoszą, a spójna się akumuluje:4

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

Dwie dodatkowe linie. Na surowej, niewycentrowanej taśmie — liczba uwarunkowania 33 452, najgorszy przypadek, jaki mamy — przy najlepszym współczynniku, jaki może przyjąć zwykłe zejście:

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%

Mnożnik 172 za dwie linie kodu. Rozdział 6 zamienia to w Adam; mechanizm jest już tutaj.

Sprawdzenie, którego będziesz potrzebować w Rozdziale 5

Link do sekcji: Sprawdzenie, którego będziesz potrzebować w Rozdziale 5

Każdy gradient w tym rozdziale został wyprowadzony ręcznie i mógł więc być błędny. Naprawą jest tabela nachyleń z początku: zmierz pochodną numerycznie i porównaj. Użyj różnicy centralnej, L(θ+h)L(θh)2h\frac{L(\theta+h) - L(\theta-h)}{2h}, która kasuje wiodący składnik błędu i jest znacznie dokładniejsza dla tego samego 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)))

Względna forma porównania ma znaczenie: bezwzględna różnica 10410^{-4} to katastrofa przy gradiencie rozmiaru 10310^{-3} i rzecz bez znaczenia przy gradiencie rozmiaru 10610^{6}.

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

Pierwsza linia to ręcznie wyprowadzony gradient powyżej. Druga to ta sama funkcja z pominiętym czynnikiem 2 w jednej składowej — literówka o długości jednego znaku — i sprawdzenie łapie ją natychmiast. Wszystko poniżej około 10710^{-7} oznacza zgodność; wszystko powyżej 10410^{-4} oznacza błąd. Zachowaj tę funkcję: Rozdział 5 używa jej do debugowania silnika automatycznego różniczkowania i to jedyny powód, dla którego błędny gradient da się w ogóle znaleźć.

Wszystko w tym rozdziale opierało się na jednym założeniu, którego nigdy nie wypowiedzieliśmy: że możesz zapisać L/θ\partial L / \partial \theta.

Dla prostej z dwoma parametrami była to linijka algebry. Prawie natychmiast przestaje nią być. Poproś system algebry symbolicznej o pochodną straty sieci względem pojedynczej wagi pierwszej warstwy, dla pojedynczego przykładu, i policz arytmetykę w odpowiedzi:

siećoperacje w jednej pochodnej cząstkowej
cztery jednostki ukryte, jedna warstwa40
cztery jednostki ukryte, dwie warstwy301
cztery jednostki ukryte, trzy warstwy1,717

Trzeci wiersz to sieć z 57 parametrami — tak mała, że w Rozdziale 6 byłaby przypisem — a rozpisanie jej gradientu ręcznie oznacza około 97 869 operacji dla jednego przykładu treningowego. Nie ma notacji, która to ratuje. Ratuje to obserwacja, że reguła łańcuchowa zastosowana do złożenia ma ogromną strukturę, te same wielkości pośrednie pojawiają się wciąż od nowa, a obliczenie ich we właściwej kolejności daje wszystkie pochodne mniej więcej za cenę jednego przejścia w przód. To jest Rozdział 5.

Ale najpierw jest mniejszy problem i czeka natychmiast.

Mamy teraz maszynę, która potoczy się w dół po dowolnej różniczkowalnej stracie. Skieruj ją na pierwotne pytanie taśmy — zaakceptować czy odrzucić, cel równy 1 albo 0 — włóż sigmoid na wyjście, żeby przewidywała prawdopodobieństwo, i minimalizuj błąd kwadratowy. Ruszy. Będzie też ledwie się poruszać wtedy, gdy myli się najbardziej, a gradient mówi dlaczego:

wyjście zzpredykcjaprawdagradient z błędem kwadratowymgradient z 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, który jest pewnie, katastrofalnie w błędzie — przewiduje 0,0000454, gdy odpowiedź to 1 — produkuje gradient błędu kwadratowego równy 9×1059 \times 10^{-5}. Nie ma pojęcia, że ma kłopoty. Druga kolumna, ze straty, której jeszcze nie wyprowadziliśmy, zgłasza 1,0: maksymalną pilność dokładnie tam, gdzie na nią zasłużono.

Co rodzi pytanie, od którego zaczyna się następny rozdział. Poprzedni rozdział powiedział, że strata jest założeniem o szumie, a błąd kwadratowy zakłada szum Gaussa. Jaki model szumu ma odpowiedź tak-lub-nie — i jaka strata wychodzi, gdy przeprowadzisz na nim to samo wyprowadzenie?


Metoda jest starsza niż wszystkie te prace: Cauchy opisał ją w notatce dla Académie des Sciences w 1847 roku jako sposób rozwiązywania układów równań przez schodzenie w dół po sumie ich kwadratów residuów. Warto też czytać obok tego rozdziału: An overview of gradient descent optimization algorithms Sebastiana Rudera (arXiv:1609.04747), który omawia momentum przez Adam w czternastu czytelnych stronach; rozdział 3 książki Nocedala i Wrighta Numerical Optimization (2. wyd., Springer, 2006), gdzie twierdzenie 3.3 podaje tempo zbieżności steepest descent na funkcji kwadratowej w kategoriach liczby uwarunkowania — to teoria stojąca za tym, dlaczego uwarunkowanie decyduje o liczbie kroków, choć traktuje line search zamiast stałokrokowego pułapu 2/λmax2/\lambda_{\max} zmierzonego powyżej; albo §5.8 i §7.1 książki Deisenrotha, Faisala i Onga Mathematics for Machine Learning dla tego samego gruntu przy mniejszej maszynerii; §6.1 książki Prince'a Understanding Deep Learning i §4.3 książki Goodfellowa, Bengio i Courville'a Deep Learning; Dive into Deep Learning §12.1–12.3, gdzie analiza minibatch ma więcej pomiarów, niż zmieściło się tutaj; oraz rozdział 4 książki Gérona Hands-On Machine Learning (3. wyd.), najbardziej praktyczne ujęcie learning rate jako czegoś, co stroisz, a nie wyprowadzasz. Notatki MIT 6.390 stawiają gradient descent przed klasyfikacją, tak jak ten kurs i z tego samego powodu.

  1. LeCun, Y., Bottou, L., Orr, G. B. and Müller, K.-R. Efficient BackProp, w Neural Networks: Tricks of the Trade (Springer, 1998), s. 9–50. Sekcja 4.3 podaje rekomendację, a sekcja 5.1 argument użyty w ramce szczegółów powyżej: centrowanie i skalowanie wejść zmienia wartości własne macierzy drugich pochodnych, a więc liczbę kroków, nie tylko komfort numeryczny.

  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). Argument, że w wysokich wymiarach punkty krytyczne są przytłaczająco często siodłami, a nie minimami lokalnymi, ponieważ minimum wymaga, by każdy z tysięcy kierunków zakrzywiał się naraz w górę.

  3. Robbins, H. and Monro, S. A Stochastic Approximation Method. Annals of Mathematical Statistics 22(3), s. 400–407 (1951). Artykuł, który ustalił, że zaszumiona estymata gradientu wystarcza, jeśli rozmiar kroku maleje we właściwy sposób.

  4. Polyak, B. T. Some methods of speeding up the convergence of iteration methods. USSR Computational Mathematics and Mathematical Physics 4(5), s. 1–17 (1964). Metoda heavy-ball, czyli aktualizacja momentum powyżej, dwadzieścia dwa lata przed tym, jak backpropagation dotarło do tej dziedziny.

Gotowy, żeby to LIA wybierała za Ciebie?

Twórz ze wszystkimi modelami AI w jednym miejscu — zacznij dziś za darmo.