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.
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 gPomiary 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ą, , a strata to średni błąd kwadratowy wyprowadzony w poprzednim rozdziale:
Dwa parametry. Dlaczego nie spróbować po prostu wielu wartości? Zróbmy to naprawdę — siatka od do i od do , krokiem :
grid 501 x 1001 = 501,501 evaluations in 3.67 s
best found: a = 2.1000, b = -0.0000, L = 24.592450Pół 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 ewaluacji dla parametrów przy wartościach na każdy. Przy tysiącu wartości na oś:
| model | parametry | ewaluacje siatki |
|---|---|---|
| ta prosta | 2 | |
| sieć XOR z Rozdziału 5 | 9 | |
| mała sieć wielowarstwowa | 20,000 |
Trzeci wiersz nie jest dużą liczbą, tylko liczbą pozbawioną sensu — w obserwowalnym wszechświecie jest mniej więcej 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 , żeby został jeden parametr i jedna krzywa — obraz, z którym zostawił cię poprzedni rozdział. Weź na niej punkt, , i zapytaj: jeśli przesunę o małą wartość , o ile przesunie się strata na jednostkę tego przesunięcia?
Ten iloraz to przyrost pionowy do poziomego — nachylenie prostej przechodzącej przez dwa punkty krzywej. Gdy maleje, dwa punkty zsuwają się ku sobie, a prosta staje się styczną. Jej nachylenie to pochodna : tempo, w jakim strata zmienia się na jednostkę zmiany . 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:
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-01Dzieją się tu dwie rzeczy i obie podtrzymują całą konstrukcję.
Błąd nie jest mgliście proporcjonalny do — jest dokładnie . Podziel 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 .
A potem wzorzec się psuje. Poniżej estymata staje się gorsza, a przy myli się już na drugiej cyfrze. Nie wydarzyło się nic matematycznego; zadziałało pudełko zmiennoprzecinkowe z poprzedniego rozdziału. i 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 — tutaj około , 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 . Możemy więc przestać mierzyć i zacząć wyprowadzać.
Złożenie i reguła łańcuchowa
Link do sekcji: Złożenie i reguła łańcuchowaOto idea, na której opiera się reszta kursu, wypowiedziana raz i prosto.
Złożyć dwie funkcje to podać jedną do drugiej: . 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 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:
Tempa się mnożą. Jeśli zmienia się trzy razy szybciej niż , a zmienia się dwa razy szybciej niż , to zmienia się sześć razy szybciej niż . 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 , tak że . Każde zależy od przez funkcję wewnętrzną , której pochodna wynosi . Reguła łańcuchowa, wyraz po wyrazie:
Te kręcone symbole 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:
W punkcie ten wektor wynosi . 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 , czyli kierunek. Pochodna kierunkowa to tempo zmiany straty, gdy idziesz w tę stronę:
Reguła łańcuchowa zamienia to w coś obliczalnego. Marsz wzdłuż zmienia z tempem i z tempem , a wkłady się sumują:
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 między wektorami,
ponieważ ma długość 1. Jedyną rzeczą, którą kontrolujesz, jest , największy przy i najmniejszy po półobrocie, przy stopniach. Zatem:
- Najstromsze wznoszenie jest wzdłuż samego , a nachylenie wynosi tam dokładnie .
- Najstromsze opadanie jest wzdłuż , a nachylenie wynosi tam .
- 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ż 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:
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 degreesPrzeszukiwanie, 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ę pomagaTeraz 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:
To rozwinięcie Taylora pierwszego rzędu. Odrzucony składnik to krzywizna — ten sam składnik, który sprawił, że estymata w tabeli nachyleń myliła się dokładnie o . Wstawmy krok, który zamierzamy wykonać, :
Strata spada o . Każda część tego wyrażenia jest nieujemna, więc obietnica jest prawdziwa — dla dostatecznie małego , bo pominięty składnik rośnie jak i w końcu go pożera. To cała teoria. Oto obietnica dotrzymana, a potem złamana:
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.999938Czytaj od dołu. Gdy maleje, rzeczywisty spadek zbiega do obiecanego — stosunek 0,99938, potem 0,99994 — czyli twierdzenie Taylora ma rację. Czytaj od góry i przy dostarczony „spadek” wynosi minus szesnaście. Krok poszedł w dół, a strata poszła w górę.
Reguła aktualizacji brzmi więc
i ma warunek, którego nikt nie wypowiada: 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, , gdzie . Jeden krok gradient descent to
Położenie jest w każdym kroku mnożone przez . 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 , czyli .
Granica jest dokładnie przy . Nie „około 1”, nie „1 zwykle jest za duże”. Przy mnożnik wynosi , a punkt odbija się między i 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 , gdzie mnożnik zmienia znak: poniżej podejście jest monotoniczne, powyżej punkt przestrzeliwuje i zmienia strony, a dokładnie przy mnożnik wynosi 0 i jeden jedyny krok ląduje w minimum.
Cztery reżimy z czterech linijek algebry. Przekrocz granice samodzielnie:
A teraz ciekawy przypadek:
Teraz reguła ogólna, która wypada z tego samego argumentu. Mnożnik był tak naprawdę , 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:
Dla , , pułap 1, czyli dokładnie to, co właśnie wyprowadziliśmy. Dla naszej taśmy macierz drugich pochodnych to , gdzie jest dwukolumnową macierzą wejść, a jej wartości własne to 2 i 14,89, więc pułap wynosi . To przewidywanie z pięcioma cyframi znaczącymi. Sprawdźmy je:
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 UPPięć 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:
| cechy | liczba uwarunkowania | najlepszy współczynnik | kroki do 1% od optimum |
|---|---|---|---|
| wycentrowane | 7.44 | 0.1184 | 10 |
| surowe milimetry i gramy | 33,452 | 0.0020037 | 79,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
Dwadzieścia linii
Link do sekcji: Dwadzieścia liniiNic z powyższego nie wymagało biblioteki. Oto cały optymalizator.
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.592448791134984Zamknięte rozwiązanie najmniejszych kwadratów dla tych ośmiu punktów to , , ze stratą . 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:
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.592449Wię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 zeroweDotychczasowy argument ma dziurę. Krok zatrzymuje się, gdy , 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 ma , które jest zerem w początku układu, gdzie funkcja jest minimum wzdłuż osi i maksimum wzdłuż osi 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ź , która ma dwie doliny o różnych głębokościach:
x = -1.046681 f(x) = -0.352386 minimum
x = 0.101031 f(x) = 0.005026 maximum
x = 0.945649 f(x) = -0.152639 minimumLą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, momentumJedna 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:
| metoda | kroki do 0,1% od optimum | gradienty per przykład |
|---|---|---|
| full batch | 7 | 700,000 |
| minibatch po 32 | 100 | 3,200 |
| jeden przykład naraz | 17,580 | 17,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 Monro3 — nie 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
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:
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 5Każ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, , która kasuje wiodący składnik błędu i jest znacznie dokładniejsza dla tego samego .
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 to katastrofa przy gradiencie rozmiaru i rzecz bez znaczenia przy gradiencie rozmiaru .
relative error: 1.8929136036763527e-11
with 2 dropped: 0.33333333331650744Pierwsza 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 oznacza zgodność; wszystko powyżej 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źć.
Dokąd to prowadzi dalej
Link do sekcji: Dokąd to prowadzi dalejWszystko w tym rozdziale opierało się na jednym założeniu, którego nigdy nie wypowiedzieliśmy: że możesz zapisać .
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 warstwa | 40 |
| cztery jednostki ukryte, dwie warstwy | 301 |
| cztery jednostki ukryte, trzy warstwy | 1,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 | predykcja | prawda | gradient z błędem kwadratowym | gradient z cross-entropy |
|---|---|---|---|---|
| 0.5000 | 1 | |||
| 0.1192 | 1 | |||
| 0.0025 | 1 | |||
| 1 |
Model, który jest pewnie, katastrofalnie w błędzie — przewiduje 0,0000454, gdy odpowiedź to 1 — produkuje gradient błędu kwadratowego równy . 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?
Źródła i metoda
Link do sekcji: Źródła i metodaMetoda 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 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.
Przypisy
Link do sekcji: Przypisy-
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. ↩
-
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ę. ↩
-
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. ↩
-
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. ↩