Sari la conținut
3/30Capitolul 3 din 30

La vale: Gradient Descent și cei doi pași pe care îi sare toată lumea

Calculează plafonul exact al ratei de învățare, apoi vezi cum o căutare brută pe 3.600 de direcții redescoperă gradientul.

Pe această pagină

Capitolul precedent s-a încheiat cu o vale.

Nu una metaforică: o curbă reală, pierderea reprezentată în funcție de un singur parametru, coborând și apoi urcând la loc. Iar pierderea de sub ea nu a fost aleasă fiindcă era comodă — a fost derivată dintr-o afirmație despre zgomotul din măsurători, iar eroarea pătratică a ieșit la celălalt capăt ca o consecință, nu ca o convenție.

Așadar avem un peisaj cu un fund și un motiv să credem că fundul este locul potrivit. Ce nu avem este o cale de a ajunge acolo.

Capitolul acesta construiește una, iar acesta este algoritmul care antrenează fiecare model din restul cursului — absolut fiecare, fără excepție, până la și inclusiv cele cu sute de miliarde de parametri. Încape în aproximativ douăzeci de linii. Cele două părți grele nu sunt în acele douăzeci de linii și sunt cele două lucruri pe care aproape orice explicație le sare:

  • De ce semnul minus. Actualizarea scade gradientul. Fiecare tutorial îl scrie; foarte puține spun de ce gradientul este direcția care merge în sus, singurul fapt care face ca semnul minus să fie altceva decât un act de credință.
  • Cât de mare este pasul. „Prea mare diverge, prea mic este lent” este adevărat și inutil. Există un număr exact, se poate calcula din pierdere, iar capitolul acesta îl calculează de două ori — o dată pentru o parabolă de jucărie și o dată pentru datele reale.

Configurarea și de ce nu poți pur și simplu să cauți

Link către secțiunea: Configurarea și de ce nu poți pur și simplu să cauți

Reformulat astfel încât capitolul să stea pe picioarele lui: cele opt piese de pe banda transportoare din Capitolul 1, dar cu o altă întrebare. Nu acceptă sau respinge — asta revine mai târziu — ci prezice greutatea unei piese din lățimea ei.

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

Măsurătorile sunt centrate, exact ca în Capitolul 1 și dintr-un motiv care revine cu dobândă înainte ca acest capitol să se termine. Modelul este o dreaptă, y^=ax+b\hat{y} = a x + b, iar pierderea este eroarea pătratică medie derivată în capitolul precedent:

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

Doi parametri. De ce să nu încercăm pur și simplu multe valori? Hai chiar să o facem — o grilă de la a=0a = 0 la 55 și de la b=5b = -5 la 55, în pași de 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

O jumătate de milion de evaluări ca să fixezi două numere la două zecimale — iar acea secundă este timpul de ceas pe o mașină, deci o rerulare ajunge oriunde între trei și șase; numărul de evaluări și minimul sunt partea care se reproduce. Gradient descent, la finalul acestui capitol, obține patru zecimale în opt pași și răspunsul float64 complet în treizeci și șase.

Dar viteza nu este argumentul, iar acesta este punctul care decide întregul curs. Căutarea pe grilă costă kPk^P evaluări pentru PP parametri la kk valori fiecare. Cu o mie de valori pe axă:

modelparametrievaluări pe grilă
această dreaptă210610^{6}
rețeaua XOR din Capitolul 59102710^{27}
o rețea multilayer mică20.0001060,00010^{60{,}000}

Al treilea rând nu este un număr mare, este unul fără sens — există aproximativ 108010^{80} atomi în universul observabil. Căutarea nu devine mai lentă pe măsură ce modelele cresc; încetează să mai existe. Tot ce urmează există din cauza acelui tabel.

O derivată este o măsurătoare pe care o poți face

Link către secțiunea: O derivată este o măsurătoare pe care o poți face

Fixează b=0b = 0 pentru o clipă, astfel încât să existe un singur parametru și o singură curbă, imaginea cu care te-a lăsat capitolul trecut. Ia un punct de pe ea, a=1a = 1, și întreabă: dacă împing ușor aa cu o cantitate mică hh, cât se mișcă pierderea, pe unitate de împingere?

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

Raportul acela este o creștere peste deplasare — panta dreptei care trece prin două puncte de pe curbă. Pe măsură ce hh se micșorează, cele două puncte alunecă unul spre celălalt și dreapta devine tangenta. Panta ei este derivata L(a)L'(a): rata cu care pierderea se schimbă pe unitate de schimbare în aa. Nu o aproximare a ceva și nu o cantitate infinit de mică. O limită a unor rapoarte obișnuite.

Merită rulat, fiindcă numerele spun ceva ce definiția nu spune:

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

Aici se întâmplă două lucruri și ambele susțin tot restul.

Eroarea nu este vag proporțională cu hh — este exact 7.445h7.445\,h. Împarte hh la o sută, eroarea se împarte la o sută, la patru cifre semnificative de fiecare dată. Constanta aceea nu este decor: este jumătate din a doua derivată a pierderii și este prima apariție a unei idei de peste două secțiuni — că o curbă, aproape de un punct, arată ca o dreaptă plus o corecție proporțională cu h2h^2.

Și apoi tiparul se rupe. Sub h=108h = 10^{-8} estimarea devine mai proastă, iar la 101410^{-14} este greșită la a doua cifră. Nu s-a întâmplat nimic matematic; s-a întâmplat cutia floating-point din capitolul trecut. L(a+h)L(a+h) și L(a)L(a) coincid în primele zece cifre, scăderea lor distruge acele cifre, iar împărțirea dezastrului la un număr minuscul amplifică ce rămâne. Există un cel mai bun hh — aici în jur de 10810^{-8}, aproximativ rădăcina pătrată a epsilonului mașinii — iar a merge mai jos nu înseamnă mai atent, ci mai puțin atent. Ține minte asta; o funcție de la finalul capitolului depinde de ea.

Panta exactă, din calcul, nu din măsurare, este 16.385-16.385. Deci putem opri măsurarea și putem începe derivarea.

Iată ideea pe care se sprijină restul cursului, formulată o dată, simplu.

A compune două funcții înseamnă să o alimentezi pe una cu cealaltă: (fg)(x)=f(g(x))(f \circ g)(x) = f(g(x)). Nimic mai mult.

O rețea profundă nu este ca o compoziție. Este una. Un strat este o funcție; stivuirea straturilor înseamnă compunerea lor; „adâncimea” este numărul de funcții din lanț. Când Capitolul 5 construiește o rețea, construiește f4f3f2f1f_4 \circ f_3 \circ f_2 \circ f_1 și nimic altceva. Ceea ce înseamnă că, pentru scopurile noastre, cea mai importantă regulă a calculului este cea care derivează o compoziție:

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

Ratele se înmulțesc. Dacă gg se schimbă de trei ori mai repede decât xx, iar ff se schimbă de două ori mai repede decât gg, atunci ff se schimbă de șase ori mai repede decât xx. Acesta este tot conținutul și acesta este motivul pentru care un semnal care trece înapoi prin zece straturi este înmulțit cu zece numere — motiv pentru care Capitolul 6 petrece o secțiune pe ce se întâmplă când toate acele numere sunt puțin mai mici decât unu.

Folosește-o pe pierderea noastră. Scrie reziduul ri=axi+byir_i = a x_i + b - y_i, astfel încât L=1nri2L = \frac{1}{n}\sum r_i^2. Fiecare rir_i depinde de aa prin funcția internă axia x_i, a cărei derivată este xix_i. Regula lanțului, termen cu termen:

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

Simbolurile acelea curbate \partial marchează o derivată parțială: derivezi în raport cu o variabilă și le tratezi pe toate celelalte ca pe constante. Nu se întâmplă nimic nou — este aceeași limită ca înainte, luată de-a lungul unei axe. Adună derivatele parțiale într-un vector și ai gradientul:

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

În punctul (a,b)=(1,4)(a, b) = (1, 4), acel vector este (16.385, 8.0)(-16.385,\ 8.0). Două numere. Întrebarea este ce înseamnă ele, iar acesta este primul pas pe care îl sare toată lumea.

Gradientul este un vector de pante de-a lungul axelor. Atât am demonstrat. Nu este evident — nu ar trebui să fie evident — că asamblarea lor într-un vector produce ceva care arată într-o anumită direcție.

Așa că definim lucrul pe care chiar îl vrem. Alege un vector unitar u\mathbf{u}, o direcție. Derivata direcțională este rata cu care pierderea se schimbă când mergi în direcția aceea:

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

Regula lanțului transformă asta în ceva calculabil. Mersul de-a lungul lui u\mathbf{u} schimbă aa cu rata u1u_1 și bb cu rata u2u_2, iar contribuțiile se adună:

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}

Rata de schimbare în orice direcție este produsul scalar dintre gradient și acea direcție. Iar acum poanta, care este o singură linie de geometrie. Scriind produsul scalar cu unghiul ϕ\phi dintre vectori,

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

fiindcă u\mathbf{u} are lungimea 1. Singurul lucru pe care îl controlezi este cosϕ\cos\phi, care este maxim la ϕ=0\phi = 0 și minim la o jumătate de întoarcere, ϕ=180\phi = 180 grade. Deci:

  • Urcarea cea mai abruptă este de-a lungul lui L\nabla L însuși, iar panta de acolo este exact L\lVert \nabla L \rVert.
  • Coborârea cea mai abruptă este de-a lungul lui L-\nabla L, iar panta de acolo este L-\lVert \nabla L \rVert.
  • Perpendicular pe gradient, pierderea nu se schimbă deloc. De aceea liniile unei hărți de contur intersectează gradientul în unghi drept.

Acesta este semnul minus. Nu o convenție, nu o inversare de semn aleasă de cineva: direcția celei mai rapide scăderi este gradientul negativ fiindcă cosϕ\cos\phi este minimizat la o jumătate de întoarcere și din niciun alt motiv.

Fiindcă aceasta este o afirmație despre toate direcțiile, testeaz-o pe toate direcțiile. Eșantionează 3.600 dintre ele, una la fiecare zecime de grad, și măsoar-o pe fiecare printr-o mică împingere:

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

O căutare care nu știe nimic despre gradient, pe 3.600 de direcții, își găsește cea mai abruptă urcare la 154,0 grade — direcția proprie a gradientului, în limita rezoluției de 0,1 grade a căutării. Iar panta pe care o găsește acolo, 18,2337, este lungimea gradientului la șase cifre. Teorema nu este o poveste despre ce înseamnă gradientele; este un fapt măsurabil, iar aceasta este măsurătoarea.

Acum al doilea pas sărit. Știm încotro este jos. Nu rezultă că, mergând în acea direcție, pierderea scade, fiindcă „jos” este o afirmație despre o împingere infinitesimală, iar un pas nu este infinitesimal.

Puntea este liniarizarea. Aproape de un punct, o funcție netedă este tangenta ei plus o corecție:

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

Aceasta este dezvoltarea Taylor de ordinul întâi. Termenul eliminat O(δ2)O(\lVert\boldsymbol{\delta}\rVert^2) este curbura — același termen care a făcut ca estimarea din tabelul pantelor să fie greșită exact cu 7.445h7.445\,h. Pune pasul pe care intenționăm să îl facem, δ=η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

Pierderea scade cu ηL2\eta \lVert \nabla L \rVert^2. Fiecare parte a expresiei este nenegativă, deci promisiunea este reală — pentru un η\eta suficient de mic, fiindcă termenul neglijat crește ca η2\eta^2 și până la urmă îl înghite. Aceasta este toată teoria. Iată promisiunea respectată și apoi încălcată:

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

Citește de jos în sus. Pe măsură ce η\eta se micșorează, scăderea livrată converge către cea promisă — raport 0,99938, apoi 0,99994 — adică teorema lui Taylor având dreptate. Citește de sus în jos și, la η=0.2\eta = 0.2, „scăderea” livrată este minus șaisprezece. Pasul a mers la vale și pierderea a crescut.

Deci regula de actualizare este

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

și vine cu o condiție pe care nimeni nu o spune: η\eta trebuie să fie suficient de mic. Suficient de mic în comparație cu ce, mai exact, este secțiunea următoare.

Rata de învățare are un plafon și este calculabil

Link către secțiunea: Rata de învățare are un plafon și este calculabil

Începe cu cea mai simplă vale posibilă, f(x)=x2f(x) = x^2, unde f(x)=2xf'(x) = 2x. Un pas de gradient descent este

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

Poziția este înmulțită cu (12η)(1 - 2\eta) la fiecare pas. Aceasta este o progresie geometrică, iar progresiile geometrice au exact o regulă: se micșorează când multiplicatorul este mai mic decât 1 în valoare absolută și cresc altfel. Deci 12η<1\lvert 1 - 2\eta \rvert < 1, adică 0<η<10 < \eta < 1.

Granița este la η=1\eta = 1 exact. Nu „în jur de 1”, nu „1 este de obicei prea mare”. La η=1\eta = 1 multiplicatorul este 1-1, iar punctul sare între xx și x-x la nesfârșit, nici apropiindu-se, nici scăpând. Sub ea converge; peste ea diverge. Intervalul se împarte din nou la η=0.5\eta = 0.5, unde multiplicatorul își schimbă semnul: sub acel punct apropierea este monotonă, peste el punctul depășește minimul și alternează părțile, iar exact la 0.50.5 multiplicatorul este 0 și un singur pas aterizează pe minim.

Patru regimuri, din patru linii de algebră. Du-te și treci singur granițele:

14 pași, se încheie la x = -0.0836.

Vezi datele ca tabel
Pasxf(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⁩
Coborâre pe gradient, interactivă

Paisprezece pași la o rată de 0,1, din x=1.9x = -1.9, terminând la 0.0836-0.0836. Împinge rata la 0,5 și primul pas aterizează chiar pe fund. Împinge-o la 0,9 și termină la același 0.0836-0.0836 ca 0,1 — aceeași distanță, stil opus, fiindcă 12η\lvert 1 - 2\eta \rvert este 0,8 pentru ambele — dar ajunge acolo în zigzag peste vale, nu coborând pe o singură parte.

Și acum cazul interesant:

14 pași, se încheie la x = -1.9000.

Vezi datele ca tabel
Pasxf(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⁩
Coborâre pe gradient, interactivă

Exact pe graniță. Paisprezece pași la o rată de 1 și termină la 1.9-1.9: precis de unde a început, fără să fi făcut altceva decât să sară. O împingere mai sus și săriturile cresc în loc să se mențină; la 1,2 iese din grafic în patru pași. O rată prea mare nu converge lent. Nu converge.

Acum regula generală, care iese din același argument. Multiplicatorul 12η1 - 2\eta era de fapt 1ηf1 - \eta f'', iar aproape de un minim o pierdere cu mai mulți parametri are câte un astfel de număr pe direcție — valorile proprii ale matricei derivatelor de ordinul doi. Fiecare direcție trebuie să fie stabilă simultan, deci plafonul este stabilit de cea mai mare:

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

Pentru f(x)=x2f(x) = x^2, f=2f'' = 2, plafon 1, exact ce tocmai am derivat. Pentru banda noastră, matricea derivatelor de ordinul doi este 2nAA\frac{2}{n} A^{\top} A cu AA matricea cu două coloane a intrărilor, iar valorile ei proprii sunt 2 și 14,89, deci plafonul este 2/14.89=0.134322 / 14.89 = 0.13432. Aceasta este o predicție cu cinci cifre semnificative. Testeaz-o:

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

Cinci zecimale de acord între o linie de algebră liniară și o sută de mii de iterații ale unei bucle for.

Și aici revine Capitolul 1. Tot ce e mai sus a folosit măsurătorile centrate. Rulează codul identic pe milimetri și grame brute, iar valorile proprii sunt 0,0298 și 998,1 în loc de 2 și 14,89. Plafonul se prăbușește de la 0,134 la 0,002004 — la fel de exact, convergând la lr=0.002003 și explodând la lr=0.002004.

Mai rău decât plafonul este raportul dintre valorile proprii. Numărul de condiționare măsoară cât de departe de rotundă este valea: un șanț lung și subțire forțează o rată suficient de mică pentru pereții abrupți, iar apoi podeaua șanțului este parcursă cu aceeași târâre. Al nostru trece de la 7,44 centrat la 33.452 brut. Cu cea mai bună rată pe care o poate lua fiecare versiune:

caracteristicinumăr de condiționarecea mai bună ratăpași până la 1% de optim
centrate7,440,118410
milimetri și grame brute33.4520,002003779.513

Aceleași date, același cod, același răspuns la final — și de opt mii de ori mai multă muncă, fiindcă nimeni nu a scăzut o medie. În Capitolul 1, aceeași omisiune a costat perceptronul un factor de șase mii în epoci, iar diagnosticul de acolo era geometric: datele pluteau departe de origine. Este aceeași geometrie aici, într-un costum de optimizare, și de aceea normalizarea input-ului nu este sfat de igienă, ci aritmetică.1

Nimic de mai sus nu a avut nevoie de o bibliotecă. Iată întregul optimizator.

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

Răspunsul în formă închisă al celor mai mici pătrate pentru aceste opt puncte este a=2.100403a = 2.100403, b=0b = 0, cu o pierdere de 24.59244924.592449. Bucla l-a găsit la opt cifre semnificative fără să știe că există o formă închisă — ceea ce contează, fiindcă din Capitolul 5 mai departe nu va exista una.

Traiectoria, fiindcă scopul este să o vezi:

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

Cea mai mare parte a distanței este acoperită în primii doi pași, fiindcă gradientul este cel mai mare când ești cel mai departe de fund și se micșorează pe măsură ce te apropii. Gradient descent încetinește automat lângă un minim. Este o facilitate și este, în Capitolul 6, și o problemă.

Argumentul de până acum are o gaură. Pasul se oprește când L=0\nabla L = \mathbf{0}, iar noi am tot numit asta „minimul”. Un punct cu gradient zero este un punct critic, iar a fi minim este doar una dintre modalitățile de a fi unul:

  • un minim local: în urcare în toate direcțiile, dar posibil să nu fie cel mai jos astfel de punct de oriunde;
  • un maxim local: în coborâre în toate direcțiile;
  • un punct șa: în urcare în unele direcții și în coborâre în altele. Suprafața f(x,y)=x2y2f(x,y) = x^2 - y^2 are f=(2x,2y)\nabla f = (2x, -2y), care este zero în origine, unde funcția este un minim de-a lungul axei xx și un maxim de-a lungul axei yy în același timp.

Gradient descent nu le poate deosebi, fiindcă se uită doar la gradient, iar gradientul este zero în toate trei.

Dreapta noastră are un singur punct critic și acesta este răspunsul — o pierdere cu eroare pătratică peste un model liniar este convexă, un singur bol, iar descent pe ea nu poate rata minimul global. Această proprietate nu supraviețuiește contactului cu acest curs. Pierderea unei rețele neuronale nu este convexă, iar din Capitolul 5 mai departe „minimul” nu este un lucru care există: sunt multe, de adâncimi diferite, iar care dintre ele îți iese depinde de unde ai pornit. Aceasta este o propoziție și rămâne o propoziție, fiindcă teoria este mare, iar consecința practică este mică.

Poți vedea întreaga consecință pe o singură curbă. Ia f(x)=x44x22+x10f(x) = \tfrac{x^4}{4} - \tfrac{x^2}{2} + \tfrac{x}{10}, care are două văi de adâncimi diferite:

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 pași, se încheie la x = 0.9456.

Vezi datele ca tabel
Pasxf(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⁩
Coborâre pe gradient, interactivă

Patruzeci de pași din x=0.11x = 0.11, stabilizându-se la 0.94560.9456 — cea mai puțin adâncă dintre cele două văi. Mută acum punctul de pornire cu un pas la stânga, la 0.100.10. Aceeași rată, aceiași patruzeci de pași, și se stabilizează la 1.0461-1.0461 în schimb, unde pierderea este cu 0,199747 mai mică. Linia de cumpănă este cocoașa de la 0.1010310.101031, iar întreaga diferență dintre cele două răspunsuri este de care parte a ei s-a întâmplat să pornești.

A ateriza în valea superficială este cu 56,7% mai rău ca pierdere, iar algoritmul nu are de unde să știe, fiindcă din interiorul unei văi orice direcție este în urcare. Nu există reparație pentru asta în gradient descent și nici nu vine una. Ce există, în practică, este constatarea că lucrul acesta contează mult mai puțin decât sugerează imaginea — în dimensiunile foarte înalte ale unei rețele reale, majoritatea punctelor critice se dovedesc a fi șei, nu capcane,2 iar Capitolul 5 măsoară cât de des se blochează de fapt o rețea mică.

Pași mai ieftini: stochastic, minibatch, momentum

Link către secțiunea: Pași mai ieftini: stochastic, minibatch, momentum

Un lucru despre grad de mai sus ar trebui să te deranjeze: însumează peste întregul set de date la fiecare pas. Opt piese nu înseamnă nimic. Un milion înseamnă un milion de calcule de gradient ca să miști parametrii o singură dată.

Scăparea este că gradientul este o medie, iar o medie poate fi estimată dintr-un eșantion. Calculeaz-o pe un pumn aleatoriu — un minibatch — și fă pasul pe aceea. Estimarea este zgomotoasă; este și ne-biasată, iar sute de pași zgomotoși ieftini bat un pas exact scump. Pe o sută de mii de piese sintetice, numărând gradiente pe exemplu, nu pași:

metodăpași până la 0,1% de optimgradiente pe exemplu
full batch7700.000
minibatch de 321003.200
câte un exemplu17.58017.580

De două sute nouăsprezece ori mai puțină aritmetică pentru a ajunge în același loc. Iar extrema — câte un exemplu, aproximarea stochastic originală a lui Robbins și Monro3nu este câștigătoarea: este de cinci ori mai proastă decât batch-urile de 32, fiindcă 32 de exemple nu costă aproape nimic mai mult decât unul pe hardware care înmulțește matrici, în timp ce zgomotul scade cu rădăcina pătrată a dimensiunii batch-ului. Acest compromis este motivul pentru care fiecare script de antrenare pe care îl vei citi vreodată are un batch_size în el.

Momentum este cealaltă remediere ieftină și țintește direct șanțul. Într-o vale prost condiționată, pașii fac zigzag peste direcția îngustă în timp ce se târăsc de-a lungul celei lungi. Momentum păstrează o medie curentă a gradientelor trecute, astfel încât componentele oscilante se anulează și cea consistentă se acumulează:4

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

Două linii în plus. Pe banda brută necentrată — număr de condiționare 33.452, cel mai rău caz pe care îl avem — la cea mai bună rată pe care o poate lua descent simplu:

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%

Un factor de 172 pentru două linii de cod. Capitolul 6 transformă asta în Adam; mecanismul este deja aici.

Verificarea de care vei avea nevoie în Capitolul 5

Link către secțiunea: Verificarea de care vei avea nevoie în Capitolul 5

Fiecare gradient din acest capitol a fost derivat de mână și, prin urmare, putea fi greșit. Remediul este tabelul pantelor de la început: măsoară derivata numeric și compară. Folosește diferența centrală, L(θ+h)L(θh)2h\frac{L(\theta+h) - L(\theta-h)}{2h}, care anulează termenul principal de eroare și este mult mai precisă pentru același 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)))

Forma relativă a comparației contează: o diferență absolută de 10410^{-4} este un dezastru pe un gradient de mărime 10310^{-3} și irelevantă pe unul de mărime 10610^{6}.

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

Prima linie este gradientul derivat de mână de mai sus. A doua este aceeași funcție cu factorul 2 omis dintr-o componentă — o greșeală de un singur caracter — iar verificarea o prinde imediat. Orice sub aproximativ 10710^{-7} înseamnă acord; orice peste 10410^{-4} este un bug. Păstrează această funcție: Capitolul 5 o folosește pentru a depana un motor de diferențiere automată și este singurul motiv pentru care un gradient greșit poate fi găsit.

Totul în acest capitol s-a sprijinit pe o presupunere care nu a fost niciodată enunțată: că poți scrie L/θ\partial L / \partial \theta.

Pentru o dreaptă cu doi parametri, asta a fost o linie de algebră. Încetează să mai fie una aproape imediat. Cere unui sistem de algebră simbolică derivata pierderii unei rețele în raport cu o singură pondere din primul strat, pentru un singur exemplu, și numără aritmetica din răspuns:

rețeaoperații într-o derivată parțială
patru unități ascunse, un strat40
patru unități ascunse, două straturi301
patru unități ascunse, trei straturi1.717

Al treilea rând este o rețea cu 57 de parametri — o rețea atât de mică încât ar fi o notă de subsol în Capitolul 6 — iar scrierea gradientului ei de mână înseamnă aproximativ 97.869 de operații pentru un singur exemplu de antrenare. Nu există notație care să salveze asta. Ce o salvează este observația că regula lanțului aplicată unei compoziții are o structură enormă, că aceleași cantități intermediare apar iar și iar, iar calcularea lor în ordinea potrivită obține toate derivatele la aproximativ prețul unui forward pass. Acesta este Capitolul 5.

Dar mai întâi există o problemă mai mică, care așteaptă imediat.

Avem acum o mașină care va rostogoli la vale pe orice pierdere diferențiabilă. Îndreapt-o către întrebarea originală a benzii — acceptă sau respinge, o țintă care este 1 sau 0 — pune un sigmoid pe ieșire ca să prezică o probabilitate și minimizează eroarea pătratică. Va rula. Dar abia se va mișca tocmai când greșește cel mai mult, iar gradientul spune de ce:

ieșire zzpredicțieadevărgradient cu eroare pătraticăgradient cu 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

Un model care greșește încrezător, catastrofal — prezicând 0,0000454 când răspunsul este 1 — produce un gradient cu eroare pătratică de 9×1059 \times 10^{-5}. Nu are idee că are probleme. Cealaltă coloană, dintr-o pierdere pe care încă nu am derivat-o, raportează 1,0: urgență maximă, exact acolo unde este meritată.

Ceea ce ridică întrebarea cu care se deschide capitolul următor. Capitolul trecut a spus că o pierdere este o presupunere despre zgomot, iar eroarea pătratică presupune zgomot gaussian. Ce model de zgomot are un răspuns da-sau-nu — și ce pierdere iese când rulezi aceeași derivare pe el?


Metoda este mai veche decât toate acestea: Cauchy a descris-o într-o notă către Académie des Sciences în 1847, ca mod de a rezolva sisteme de ecuații mergând la vale pe suma reziduurilor lor pătratice. Merită citite alături de acest capitol și: An overview of gradient descent optimization algorithms de Sebastian Ruder (arXiv:1609.04747), care acoperă momentum până la Adam în paisprezece pagini lizibile; capitolul 3 din Numerical Optimization de Nocedal și Wright (ed. a 2-a, Springer, 2006), a cărui teoremă 3.3 dă rata de convergență a steepest descent pe o cuadratică în funcție de numărul de condiționare — teoria din spatele motivului pentru care condiționarea decide numărul de pași, deși tratează line search, nu plafonul 2/λmax2/\lambda_{\max} cu pas fix măsurat mai sus; sau §5.8 și §7.1 din Mathematics for Machine Learning de Deisenroth, Faisal și Ong pentru același teren cu mai puțină mașinărie; §6.1 din Understanding Deep Learning de Prince și §4.3 din Deep Learning de Goodfellow, Bengio și Courville; Dive into Deep Learning §12.1–12.3, care are analiza minibatch cu mai multe măsurători decât încape aici; și capitolul 4 din Hands-On Machine Learning de Géron (ed. a 3-a), cel mai practic tratament al ratei de învățare ca lucru pe care îl ajustezi, nu îl derivezi. Notele MIT 6.390 pun gradient descent înaintea clasificării, așa cum face acest curs și din același motiv.

  1. LeCun, Y., Bottou, L., Orr, G. B. și Müller, K.-R. Efficient BackProp, în Neural Networks: Tricks of the Trade (Springer, 1998), pp. 9–50. Secțiunea 4.3 dă recomandarea, iar secțiunea 5.1 argumentul folosit în caseta de detalii de mai sus: centrarea și scalarea intrărilor schimbă valorile proprii ale matricei derivatelor de ordinul doi și, prin urmare, numărul de pași, nu doar confortul numeric.

  2. Dauphin, Y. N., Pascanu, R., Gulcehre, C., Cho, K., Ganguli, S. și Bengio, Y. Identifying and attacking the saddle point problem in high-dimensional non-convex optimization, arXiv:1406.2572 (2014). Argumentul că în dimensiuni înalte punctele critice sunt covârșitor șei, nu minime locale, deoarece un minim cere ca fiecare dintre miile de direcții să se curbeze în sus simultan.

  3. Robbins, H. și Monro, S. A Stochastic Approximation Method. Annals of Mathematical Statistics 22(3), pp. 400–407 (1951). Lucrarea care a stabilit că o estimare zgomotoasă a unui gradient este suficientă, dacă dimensiunea pasului scade în modul potrivit.

  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). Metoda heavy-ball, care este actualizarea cu momentum de mai sus, cu douăzeci și doi de ani înainte ca backpropagation să ajungă în acest domeniu.

Gata să lași LIA să aleagă?

Construiește cu toate modelele AI într-un singur loc — începe gratuit azi.