Naar inhoud springen
3/30Hoofdstuk 3 van 30

Bergafwaarts: Gradient Descent, en de twee stappen die iedereen overslaat

Bereken de exacte bovengrens voor een learning rate en zie hoe brute force over 3.600 richtingen de gradient herontdekt.

Op deze pagina

Het vorige hoofdstuk eindigde met een vallei.

Niet een metaforische: een echte curve, de loss uitgezet tegen één parameter, die omlaag duikt en weer omhoogkomt. En de loss eronder was niet gekozen omdat die netjes was — hij werd afgeleid uit een uitspraak over de ruis in de metingen, en de gekwadrateerde fout kwam er aan de andere kant uit als gevolg in plaats van als conventie.

Dus we hebben een landschap met een bodem, en een reden om te geloven dat die bodem de juiste plek is. Wat we niet hebben, is een manier om daar te komen.

Dit hoofdstuk bouwt er één, en het is het algoritme dat elk model in de rest van deze cursus traint — elk model, zonder uitzondering, tot en met modellen met honderden miljarden parameters. Het past in ongeveer twintig regels. De twee moeilijke delen zitten niet in die twintig regels, en het zijn precies de twee dingen die bijna elke uitleg overslaat:

  • Waarom het minteken. De update trekt de gradient af. Elke tutorial schrijft het op; maar weinig leggen uit waarom de gradient de richting is die omhoog gaat, en dat is het enige feit dat het minteken iets anders maakt dan een geloofsdaad.
  • Hoe groot een stap. ‘Te groot divergeert, te klein is traag’ is waar en nutteloos. Er is een exact getal, het is uit de loss te berekenen, en dit hoofdstuk berekent het twee keer — één keer voor een speelgoedparabool en één keer voor de echte data.

De opzet, en waarom je niet gewoon kunt zoeken

Link naar de sectie: De opzet, en waarom je niet gewoon kunt zoeken

Opnieuw geformuleerd zodat dit hoofdstuk op zichzelf staat: de acht onderdelen van de lopende band uit hoofdstuk 1, maar met een andere vraag. Niet accepteren of afwijzen — dat komt later terug — maar het gewicht van een onderdeel voorspellen op basis van de breedte.

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

De metingen zijn gecentreerd, precies zoals in hoofdstuk 1 en om een reden die voor het einde van dit hoofdstuk met rente terugkomt. Het model is een lijn, y^=ax+b\hat{y} = a x + b, en de loss is de mean squared error die het vorige hoofdstuk afleidde:

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

Twee parameters. Waarom niet gewoon heel veel waarden proberen? Laten we dat echt doen — een grid van a=0a = 0 tot 55 en b=5b = -5 tot 55, in stappen van 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

Een half miljoen evaluaties om twee getallen tot op twee decimalen vast te pinnen — en die seconde is wandkloktijd op één machine, dus een herhaling komt ergens tussen drie en zes uit; het aantal evaluaties en het minimum zijn het deel dat reproduceert. Gradient descent haalt aan het einde van dit hoofdstuk vier decimalen in acht stappen en het volledige float64-antwoord in zesendertig.

Maar snelheid is niet het argument, en dit is het punt dat de hele cursus beslist. Grid search kost kPk^P evaluaties voor PP parameters met kk waarden per stuk. Met duizend waarden per as:

modelparametersgrid-evaluaties
deze lijn210610^{6}
het XOR-netwerk uit hoofdstuk 59102710^{27}
een klein multilayer-netwerk20.0001060,00010^{60{,}000}

De derde rij is geen groot getal, maar een betekenisloos getal — er zijn grofweg 108010^{80} atomen in het waarneembare universum. Zoeken wordt niet trager naarmate modellen groeien; het houdt op te bestaan. Alles wat volgt bestaat vanwege die tabel.

Een afgeleide is een meting die je kunt doen

Link naar de sectie: Een afgeleide is een meting die je kunt doen

Zet b=0b = 0 even vast, zodat er één parameter en één curve is: het beeld waarmee het vorige hoofdstuk je achterliet. Neem een punt erop, a=1a = 1, en vraag: als ik aa een klein beetje hh geef, hoeveel beweegt de loss dan, per eenheid van die duw?

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

Die verhouding is een stijging gedeeld door run — de helling van de rechte lijn door twee punten op de curve. Naarmate hh krimpt, schuiven de twee punten naar elkaar toe en wordt de lijn de raaklijn. De helling daarvan is de afgeleide L(a)L'(a): de snelheid waarmee de loss verandert per eenheid verandering in aa. Geen benadering van iets, en geen oneindig kleine hoeveelheid. Een limiet van gewone verhoudingen.

Het is de moeite waard om dit uit te voeren, omdat de getallen iets zeggen wat de definitie niet zegt:

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

Hier gebeuren twee dingen, en allebei dragen ze gewicht.

De fout is niet vaag evenredig met hh — hij is exact 7.445h7.445\,h. Deel hh door honderd, en de fout deelt door honderd, elke keer tot vier significante cijfers. Die constante is geen versiering: het is de helft van de tweede afgeleide van de loss, en het is de eerste verschijning van een idee dat twee secties verder terugkomt — dat een curve dicht bij een punt eruitziet als een lijn plus een correctie evenredig met h2h^2.

En dan breekt het patroon. Onder h=108h = 10^{-8} wordt de schatting slechter, en bij 101410^{-14} is hij al fout in het tweede cijfer. Er is niets wiskundigs gebeurd; de floating-pointdoos uit het vorige hoofdstuk deed dit. L(a+h)L(a+h) en L(a)L(a) komen overeen in hun eerste tien cijfers, het aftrekken ervan vernietigt die cijfers, en delen door een piepklein getal versterkt wat overblijft. Er is een beste hh — hier rond 10810^{-8}, ongeveer de wortel van de machine-epsilon — en kleiner gaan is niet zorgvuldiger, maar minder zorgvuldig. Onthoud dat; een functie aan het einde van dit hoofdstuk hangt ervan af.

De exacte helling, uit calculus in plaats van meting, is 16.385-16.385. Dus we kunnen stoppen met meten en beginnen met afleiden.

Dit is het idee waarop de rest van de cursus is gebouwd, één keer helder gezegd.

Twee functies componeren betekent dat je de ene in de andere voert: (fg)(x)=f(g(x))(f \circ g)(x) = f(g(x)). Niets meer.

Een deep network is niet zoals een compositie. Het is er één. Een laag is een functie; lagen stapelen is ze componeren; ‘diepte’ is het aantal functies in de keten. Wanneer hoofdstuk 5 een netwerk bouwt, bouwt het f4f3f2f1f_4 \circ f_3 \circ f_2 \circ f_1 en niets anders. Dat betekent dat de allerbelangrijkste regel uit de calculus, voor ons doel, de regel is die een compositie differentieert:

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

Snelheden vermenigvuldigen. Als gg drie keer zo snel verandert als xx, en ff twee keer zo snel verandert als gg, dan verandert ff zes keer zo snel als xx. Dat is de hele inhoud, en het is waarom een signaal dat teruggaat door tien lagen met tien getallen wordt vermenigvuldigd — en waarom hoofdstuk 6 een sectie besteedt aan wat er gebeurt wanneer die getallen allemaal iets kleiner zijn dan één.

Gebruik dit op onze loss. Schrijf het residu als ri=axi+byir_i = a x_i + b - y_i, zodat L=1nri2L = \frac{1}{n}\sum r_i^2. Elke rir_i hangt af van aa via de binnenste functie axia x_i, waarvan de afgeleide xix_i is. Chain rule, term voor term:

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

Die gekrulde \partial-symbolen markeren een partiële afgeleide: differentieer ten opzichte van één variabele en behandel alle andere als constanten. Er gebeurt niets nieuws — het is dezelfde limiet als eerder, genomen langs één as. Verzamel de partiële afgeleiden in een vector en je hebt de gradient:

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

Op het punt (a,b)=(1,4)(a, b) = (1, 4) is die vector (16.385, 8.0)(-16.385,\ 8.0). Twee getallen. De vraag is wat ze betekenen, en dit is de eerste stap die iedereen overslaat.

De gradient is een vector van hellingen langs de assen. Dat is alles wat we hebben bewezen. Het is niet duidelijk — en het zou ook niet duidelijk moeten zijn — dat ze samenvoegen tot een vector iets oplevert dat ergens specifiek heen wijst.

Definieer dus wat we echt willen. Kies een eenheidsvector u\mathbf{u}, een richting. De richtingsafgeleide is de snelheid waarmee de loss verandert als je die kant op loopt:

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

De chain rule maakt hier iets berekenbaars van. Lopen langs u\mathbf{u} verandert aa met snelheid u1u_1 en bb met snelheid u2u_2, en de bijdragen tellen op:

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}

De veranderingssnelheid in elke richting is het dot product van de gradient met die richting. En nu de punchline, één regel meetkunde. Schrijf het dot product met de hoek ϕ\phi tussen de vectoren,

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

omdat u\mathbf{u} lengte 1 heeft. Het enige waar je controle over hebt is cosϕ\cos\phi, die het grootst is bij ϕ=0\phi = 0 en het kleinst bij een halve draai, ϕ=180\phi = 180 graden. Dus:

  • De steilste stijging is langs L\nabla L zelf, en de helling daar is exact L\lVert \nabla L \rVert.
  • De steilste daling is langs L-\nabla L, en de helling daar is L-\lVert \nabla L \rVert.
  • Loodrecht op de gradient verandert de loss helemaal niet. Daarom kruisen de lijnen van een contourkaart de gradient onder een rechte hoek.

Dat is het minteken. Geen conventie, geen tekenomkering die iemand koos: de richting van snelste daling is de negatieve gradient omdat cosϕ\cos\phi minimaal is bij een halve draai, en om geen enkele andere reden.

Omdat dit een claim is over alle richtingen, testen we hem tegen alle richtingen. Sample er 3.600, één per tiende graad, en meet elke richting door een duwtje te geven:

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

Een zoekactie die niets weet over gradients, over 3.600 richtingen, vindt zijn steilste klim bij 154,0 graden — de eigen richting van de gradient, binnen de resolutie van 0,1 graad van de zoekactie. En de helling die hij daar vindt, 18,2337, is de lengte van de gradient tot zes cijfers. De stelling is geen verhaal over wat gradients betekenen; het is een meetbaar feit, en dit is de meting.

Nu de tweede overgeslagen stap. We weten welke kant omlaag is. Daaruit volgt niet dat die kant op lopen de loss verlaagt, want ‘omlaag’ is een uitspraak over een infinitesimale duw en een stap is niet infinitesimaal.

De brug is linearisatie. Dicht bij een punt is een gladde functie haar raaklijn plus een correctie:

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

Dat is de eerste-orde Taylor-expansie. De weggegooide O(δ2)O(\lVert\boldsymbol{\delta}\rVert^2) is de kromming — dezelfde term die de schatting in de hellingstabel precies 7.445h7.445\,h fout maakte. Vul de stap in die we willen zetten, δ=η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

De loss daalt met ηL2\eta \lVert \nabla L \rVert^2. Elk deel daarvan is niet-negatief, dus de belofte is echt — voor een klein genoeg η\eta, omdat de verwaarloosde term groeit als η2\eta^2 en hem uiteindelijk opslokt. Dat is de hele theorie. Hier zie je de belofte waargemaakt worden, en daarna breken:

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

Lees het van onder naar boven. Naarmate η\eta krimpt, convergeert de geleverde daling naar de beloofde — ratio 0,99938, daarna 0,99994 — en dat is Taylors stelling die klopt. Lees het van boven naar beneden en bij η=0.2\eta = 0.2 is de geleverde ‘daling’ negatief zestien. De stap ging bergaf en de loss ging omhoog.

Dus de update-regel is

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

en hij komt met een voorwaarde die niemand noemt: η\eta moet klein genoeg zijn. Klein genoeg vergeleken met wat, precies, is de volgende sectie.

De learning rate heeft een plafond, en dat is berekenbaar

Link naar de sectie: De learning rate heeft een plafond, en dat is berekenbaar

Begin met de simpelste vallei die er is, f(x)=x2f(x) = x^2, waarbij f(x)=2xf'(x) = 2x. Eén stap gradient descent is

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

De positie wordt elke stap vermenigvuldigd met (12η)(1 - 2\eta). Dat is een meetkundige rij, en meetkundige rijen hebben precies één regel: ze krimpen wanneer de vermenigvuldiger in absolute waarde kleiner is dan 1, en anders groeien ze. Dus 12η<1\lvert 1 - 2\eta \rvert < 1, oftewel 0<η<10 < \eta < 1.

De grens ligt exact bij η=1\eta = 1. Niet ‘rond 1’, niet ‘1 is meestal te groot’. Bij η=1\eta = 1 is de vermenigvuldiger 1-1 en stuitert het punt voor altijd tussen xx en x-x, zonder dichterbij te komen of te ontsnappen. Eronder convergeert het; erboven divergeert het. Het interval splitst nog eens bij η=0.5\eta = 0.5, waar de vermenigvuldiger van teken verandert: daaronder is de nadering monotoon, daarboven schiet het punt door en wisselt het van kant, en bij exact 0.50.5 is de vermenigvuldiger 0 en landt één enkele stap op het minimum.

Vier regimes, uit vier regels algebra. Ga zelf over de grenzen heen:

Aantal stappen: 14, eindigt bij x = -0.0836.

Bekijk de gegevens als tabel
Stapxf(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⁩
Gradiëntafdaling, interactief

Veertien stappen met een rate van 0,1, vanaf x=1.9x = -1.9, eindigend bij 0.0836-0.0836. Duw de rate naar 0,5 en de allereerste stap landt op de bodem. Duw hem naar 0,9 en hij eindigt bij dezelfde 0.0836-0.0836 als 0,1 — dezelfde afstand, tegenovergestelde stijl, omdat 12η\lvert 1 - 2\eta \rvert voor allebei 0,8 is — maar hij komt er door zigzaggend de vallei over te steken in plaats van langs één kant omlaag te lopen.

En nu de interessante:

Aantal stappen: 14, eindigt bij x = -1.9000.

Bekijk de gegevens als tabel
Stapxf(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⁩
Gradiëntafdaling, interactief

Precies op de grens. Veertien stappen met een rate van 1, en hij eindigt bij 1.9-1.9: precies waar hij begon, na niets anders te hebben gedaan dan stuiteren. Eén duwtje hoger en het stuiteren groeit in plaats van gelijk te blijven; bij 1,2 is hij in vier stappen buiten de grafiek. Een rate die te groot is, convergeert niet langzaam. Hij convergeert niet.

Nu de algemene regel, die uit hetzelfde argument valt. De vermenigvuldiger 12η1 - 2\eta was eigenlijk 1ηf1 - \eta f'', en dicht bij een minimum heeft een loss met meerdere parameters zo’n getal per richting — de eigenwaarden van de matrix met tweede afgeleiden. Elke richting moet tegelijk stabiel zijn, dus het plafond wordt bepaald door de grootste:

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

Voor f(x)=x2f(x) = x^2, f=2f'' = 2, plafond 1, precies wat we net afleidden. Voor onze band is de matrix van tweede afgeleiden 2nAA\frac{2}{n} A^{\top} A met AA de tweekoloms matrix van inputs, en de eigenwaarden zijn 2 en 14,89, dus het plafond is 2/14.89=0.134322 / 14.89 = 0.13432. Dat is een voorspelling met vijf significante cijfers. Test hem:

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

Vijf decimalen overeenstemming tussen één regel lineaire algebra en honderdduizend iteraties van een for-loop.

En hier komt hoofdstuk 1 terug. Alles hierboven gebruikte de gecentreerde metingen. Draai dezelfde code op ruwe millimeters en grammen en de eigenwaarden zijn 0,0298 en 998,1 in plaats van 2 en 14,89. Het plafond stort in van 0,134 naar 0,002004 — net zo exact, convergerend bij lr=0.002003 en ontploffend bij lr=0.002004.

Erger dan het plafond is de verhouding tussen de eigenwaarden. Het condition number meet hoe ver de vallei van rond af is: een lange smalle geul dwingt een rate af die klein genoeg is voor de steile wanden, en daarna wordt over de bodem van de geul met dezelfde kruipsnelheid gelopen. Die van ons gaat van 7,44 gecentreerd naar 33.452 ruw. Met de beste rate die elke versie kan nemen:

featurescondition numberbeste ratestappen tot binnen 1% van het optimum
gecentreerd7,440,118410
ruwe millimeters en grammen33.4520,002003779.513

Dezelfde data, dezelfde code, hetzelfde antwoord aan het einde — en achtduizend keer zoveel werk, omdat niemand een gemiddelde aftrok. In hoofdstuk 1 kostte dezelfde omissie de perceptron een factor zesduizend in epochs, en de diagnose daar was geometrisch: de data zweefde ver van de oorsprong. Het is hier dezelfde geometrie in optimalisatiekostuum, en daarom is inputnormalisatie geen hygiëneadvies maar rekenkunde.1

Niets hierboven had een library nodig. Hier is de hele optimiser.

descent.pyPYTHON
def loss(theta):
    a, b = theta
    return np.mean((a * x + b - y) ** 2)


def grad(theta):
    a, b = theta
    residual = a * x + b - y
    return np.array([np.mean(2 * residual * x), np.mean(2 * residual)])


def descend(theta, lr, steps):
    theta = np.array(theta, dtype=float)
    for _ in range(steps):
        theta = theta - lr * grad(theta)   
    return theta


theta = descend([0.0, 0.0], lr=0.05, steps=60)
print(theta, loss(theta))
TEXT
[ 2.10040296e+00 -2.76445533e-15] 24.592448791134984

Het closed-form least-squares-antwoord voor deze acht punten is a=2.100403a = 2.100403, b=0b = 0, met een loss van 24.59244924.592449. De loop vond het tot acht significante cijfers zonder te weten dat er een closed form bestaat — en dat doet ertoe, want vanaf hoofdstuk 5 zal die er niet zijn.

De trajectory, want ernaar kijken is het punt:

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

Het grootste deel van de afstand wordt in de eerste twee stappen afgelegd, omdat de gradient het grootst is wanneer je het verst van de bodem bent en krimpt naarmate je nadert. Gradient descent vertraagt automatisch dicht bij een minimum. Dat is een feature en het is ook, in hoofdstuk 6, een probleem.

Het argument tot nu toe heeft een gat. De stap stopt wanneer L=0\nabla L = \mathbf{0}, en we hebben dat ‘het minimum’ genoemd. Een punt met nul gradient is een kritisch punt, en een minimum zijn is maar één van de manieren om dat te zijn:

  • een lokaal minimum: bergop in elke richting, maar mogelijk niet het laagste van zulke punten ergens;
  • een lokaal maximum: bergaf in elke richting;
  • een zadelpunt: bergop in sommige richtingen en bergaf in andere. Het oppervlak f(x,y)=x2y2f(x,y) = x^2 - y^2 heeft f=(2x,2y)\nabla f = (2x, -2y), wat nul is in de oorsprong, waar de functie tegelijk een minimum is langs de xx-as en een maximum langs de yy-as.

Gradient descent kan deze niet uit elkaar houden, omdat het alleen ooit naar de gradient kijkt, en de gradient is bij alle drie nul.

Onze lijn heeft één kritisch punt en dat is het antwoord — een squared-error loss over een lineair model is convex, één enkele kom, en descent daarop kan niet mislukken in het vinden van het globale minimum. Die eigenschap overleeft de aanraking met deze cursus niet. De loss van een neural network is niet convex, en vanaf hoofdstuk 5 is ‘het minimum’ niet iets dat bestaat: er zijn er veel, met verschillende dieptes, en welke je krijgt hangt af van waar je begon. Dat is één zin en blijft één zin, omdat de theorie groot is en de praktische consequentie klein.

Je kunt de hele consequentie op één curve zien. Neem f(x)=x44x22+x10f(x) = \tfrac{x^4}{4} - \tfrac{x^2}{2} + \tfrac{x}{10}, die twee valleien met verschillende dieptes heeft:

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

Aantal stappen: 40, eindigt bij x = 0.9456.

Bekijk de gegevens als tabel
Stapxf(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⁩
Gradiëntafdaling, interactief

Veertig stappen vanaf x=0.11x = 0.11, eindigend bij 0.94560.9456 — de ondiepere van de twee valleien. Verplaats nu het startpunt één tik naar links, naar 0.100.10. Dezelfde rate, dezelfde veertig stappen, en hij eindigt in plaats daarvan bij 1.0461-1.0461, waar de loss 0,199747 lager is. De waterscheiding is de bult bij 0.1010310.101031, en het hele verschil tussen de twee antwoorden is aan welke kant daarvan je toevallig begon.

In de ondiepe vallei landen is 56,7% slechter in loss, en het algoritme kan dat niet weten, omdat vanuit een vallei elke richting bergop is. Hier is geen reparatie voor in gradient descent en die komt er ook niet. Wat er in de praktijk wel is, is de bevinding dat dit veel minder uitmaakt dan dit plaatje suggereert — in de heel hoge dimensies van een echt netwerk blijken de meeste kritische punten zadels te zijn in plaats van vallen,2 en hoofdstuk 5 meet hoe vaak een klein netwerk echt vastloopt.

Goedkopere stappen: stochastic, minibatch, momentum

Link naar de sectie: Goedkopere stappen: stochastic, minibatch, momentum

Eén ding aan grad hierboven zou je moeten storen: het sommeert over de hele dataset voor elke stap. Acht onderdelen is niets. Een miljoen is een miljoen gradient-berekeningen om de parameters één keer te verplaatsen.

De ontsnapping is dat de gradient een gemiddelde is, en een gemiddelde kun je schatten uit een sample. Bereken hem op een willekeurig handjevol — een minibatch — en stap daarop. De schatting is ruisig; hij is ook unbiased, en honderden goedkope ruisige stappen verslaan één dure exacte. Op honderdduizend synthetische onderdelen, geteld in gradients per voorbeeld in plaats van stappen:

methodestappen tot binnen 0,1% van het optimumgradients per voorbeeld
full batch7700.000
minibatch van 321003.200
één voorbeeld per keer17.58017.580

Tweehonderdnegentien keer minder rekenwerk om op dezelfde plek te komen. En het uiterste — één voorbeeld per keer, de oorspronkelijke stochastic approximation van Robbins en Monro3 — is niet de winnaar: het is vijf keer slechter dan batches van 32, omdat 32 voorbeelden bijna niets meer kosten dan één op hardware die matrices vermenigvuldigt, terwijl de ruis afneemt met de wortel van de batchgrootte. Die trade-off is waarom elk trainingsscript dat je ooit leest een batch_size bevat.

Momentum is de andere goedkope fix, en die is recht op de geul gericht. In een slecht geconditioneerde vallei zigzaggen de stappen over de smalle richting terwijl ze langs de lange richting kruipen. Momentum houdt een lopend gemiddelde van eerdere gradients bij, zodat de oscillerende componenten elkaar opheffen en de consistente component zich ophoopt:4

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

Twee extra regels. Op de ruwe ongecentreerde band — condition number 33.452, het ergste geval dat we hebben — met de beste rate die gewone descent kan nemen:

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%

Een factor 172 voor twee regels code. Hoofdstuk 6 maakt hier Adam van; het mechanisme staat hier al.

Elke gradient in dit hoofdstuk is met de hand afgeleid en kan dus fout zijn. De fix is de hellingstabel van het begin: meet de afgeleide numeriek en vergelijk. Gebruik het centrale verschil, L(θ+h)L(θh)2h\frac{L(\theta+h) - L(\theta-h)}{2h}, dat de leidende foutterm opheft en voor dezelfde hh veel nauwkeuriger is.

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)))

De relatieve vorm van de vergelijking doet ertoe: een absoluut verschil van 10410^{-4} is een ramp op een gradient van grootte 10310^{-3} en irrelevant op één van grootte 10610^{6}.

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

De eerste regel is de hierboven met de hand afgeleide gradient. De tweede is dezelfde functie waarbij de factor 2 in één component is weggelaten — een typefout van één teken — en de check vangt hem meteen. Alles onder ongeveer 10710^{-7} is overeenstemming; alles boven 10410^{-4} is een bug. Bewaar deze functie: hoofdstuk 5 gebruikt hem om een automatic differentiation engine te debuggen, en het is de enige reden dat een verkeerde gradient überhaupt te vinden is.

Alles in dit hoofdstuk rustte op één aanname die nooit is uitgesproken: dat je L/θ\partial L / \partial \theta kunt opschrijven.

Voor een lijn met twee parameters was dat één regel algebra. Dat houdt vrijwel meteen op. Vraag een symbolic algebra system om de afgeleide van de loss van een netwerk ten opzichte van één enkel gewicht in de eerste laag, voor één enkel voorbeeld, en tel de rekenoperaties in het antwoord:

netwerkoperaties in één partiële afgeleide
vier hidden units, één laag40
vier hidden units, twee lagen301
vier hidden units, drie lagen1.717

De derde rij is een netwerk met 57 parameters — zo klein dat het in hoofdstuk 6 een voetnoot zou zijn — en zijn gradient met de hand uitschrijven betekent ongeveer 97.869 operaties voor één trainingsvoorbeeld. Er is geen notatie die dit redt. Wat het redt, is de observatie dat de chain rule toegepast op een compositie enorme structuur heeft, dat dezelfde tussenliggende grootheden steeds opnieuw verschijnen, en dat ze in de juiste volgorde berekenen alle afgeleiden oplevert voor ongeveer de prijs van één forward pass. Dat is hoofdstuk 5.

Maar er is eerst een kleiner probleem, en dat wacht meteen.

We hebben nu een machine die bergaf rolt op elke differentieerbare loss. Richt hem op de oorspronkelijke vraag van de band — accepteren of afwijzen, een target dat 1 of 0 is — zet een sigmoid op de output zodat hij een kans voorspelt, en minimaliseer squared error. Hij zal draaien. Hij zal ook nauwelijks bewegen wanneer hij het meest fout zit, en de gradient zegt waarom:

output zzvoorspellingwaarheidgradient met squared errorgradient met 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

Een model dat zelfverzekerd, catastrofaal fout zit — het voorspelt 0,0000454 wanneer het antwoord 1 is — produceert een squared-error gradient van 9×1059 \times 10^{-5}. Het heeft geen idee dat het in de problemen zit. De andere kolom, afkomstig van een loss die we nog niet hebben afgeleid, meldt 1,0: maximale urgentie, precies waar die verdiend is.

Dat roept de vraag op waarmee het volgende hoofdstuk opent. Het vorige hoofdstuk zei dat een loss een aanname is over de ruis, en squared error veronderstelt Gaussiaanse ruis. Welk ruismodel heeft een ja-of-nee-antwoord — en welke loss komt eruit wanneer je dezelfde afleiding daarop loslaat?


De methode is ouder dan al deze bronnen: Cauchy beschreef haar in 1847 in een notitie aan de Académie des Sciences, als een manier om stelsels vergelijkingen op te lossen door bergaf te lopen op de som van hun gekwadrateerde residuen. Ook de moeite waard om naast dit hoofdstuk te lezen: Sebastian Ruders An overview of gradient descent optimization algorithms (arXiv:1609.04747), dat momentum tot en met Adam behandelt in veertien leesbare pagina’s; hoofdstuk 3 van Nocedal en Wrights Numerical Optimization (2e ed., Springer, 2006), waarvan stelling 3.3 de convergentiesnelheid van steepest descent op een kwadratische functie geeft in termen van het condition number — dit is de theorie achter waarom conditionering het aantal stappen bepaalt, al behandelt het line search in plaats van het fixed-step 2/λmax2/\lambda_{\max}-plafond dat hierboven is gemeten — of §5.8 en §7.1 van Deisenroth, Faisal en Ongs Mathematics for Machine Learning voor hetzelfde terrein met minder machinerie; §6.1 van Prince’s Understanding Deep Learning en §4.3 van Goodfellow, Bengio en Courvilles Deep Learning; Dive into Deep Learning §12.1–12.3, dat de minibatch-analyse bevat met meer metingen dan hier ruimte voor is; en hoofdstuk 4 van Gérons Hands-On Machine Learning (3e ed.), de meest praktische behandeling van de learning rate als iets dat je tuned in plaats van afleidt. De MIT 6.390-notities plaatsen gradient descent vóór classificatie, zoals deze cursus doet en om dezelfde reden.

  1. LeCun, Y., Bottou, L., Orr, G. B. en Müller, K.-R. Efficient BackProp, in Neural Networks: Tricks of the Trade (Springer, 1998), pp. 9–50. Sectie 4.3 geeft de aanbeveling en sectie 5.1 het argument dat in het detailvak hierboven wordt gebruikt: inputs centreren en schalen verandert de eigenwaarden van de matrix met tweede afgeleiden, en dus het aantal stappen, niet alleen het numerieke comfort.

  2. Dauphin, Y. N., Pascanu, R., Gulcehre, C., Cho, K., Ganguli, S. en Bengio, Y. Identifying and attacking the saddle point problem in high-dimensional non-convex optimization, arXiv:1406.2572 (2014). Het argument dat kritische punten in hoge dimensies overweldigend vaak zadels zijn in plaats van lokale minima, omdat een minimum vereist dat elk van duizenden richtingen tegelijk omhoog kromt.

  3. Robbins, H. en Monro, S. A Stochastic Approximation Method. Annals of Mathematical Statistics 22(3), pp. 400–407 (1951). Het paper dat vastlegde dat een ruisige schatting van een gradient genoeg is, mits een stapgrootte die op de juiste manier krimpt.

  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). De heavy-ball-methode, de momentum-update hierboven, tweeëntwintig jaar voordat backpropagation dit veld bereikte.

Klaar om LIA te laten kiezen?

Bouw met elk AI-model op één plek — begin vandaag nog gratis.