Aller au contenu
3/30Chapitre 3 sur 30

En descente : gradient descent et les deux étapes que tout le monde saute

Calculez le plafond exact d’un taux d’apprentissage, puis voyez une recherche brute retrouver le gradient parmi 3 600 directions.

Dans cet article

Le chapitre précédent s’est terminé sur une vallée.

Pas une vallée métaphorique : une vraie courbe, la perte tracée en fonction d’un seul paramètre, qui descend puis remonte. Et la perte sous cette courbe n’avait pas été choisie parce qu’elle était pratique — elle avait été dérivée d’un énoncé sur le bruit dans les mesures, et l’erreur quadratique en était sortie comme une conséquence plutôt que comme une convention.

Nous avons donc un paysage avec un fond, et une raison de penser que ce fond est le bon endroit où aller. Ce que nous n’avons pas, c’est une manière d’y parvenir.

Ce chapitre en construit une, et c’est l’algorithme qui entraîne tous les modèles du reste de ce cours — tous, sans exception, jusqu’à ceux qui comptent des centaines de milliards de paramètres. Il tient en une vingtaine de lignes. Les deux parties difficiles ne sont pas dans ces vingt lignes, et ce sont les deux choses que presque toutes les explications sautent :

  • Pourquoi le signe moins. La mise à jour soustrait le gradient. Tous les tutoriels l’écrivent ; très peu disent pourquoi le gradient est la direction qui monte, ce qui est le seul fait qui fasse du signe moins autre chose qu’un acte de foi.
  • Quelle taille de pas. « Trop grand diverge, trop petit est lent » est vrai et inutile. Il existe un nombre exact, calculable à partir de la perte, et ce chapitre le calcule deux fois — une fois pour une parabole jouet et une fois pour les données réelles.

Le cadre, et pourquoi vous ne pouvez pas simplement chercher

Lien vers la section : Le cadre, et pourquoi vous ne pouvez pas simplement chercher

Reformulons pour que ce chapitre tienne seul : les huit pièces du tapis roulant du chapitre 1, mais avec une autre question. Pas accepter ou rejeter — cela reviendra plus tard — mais prédire le poids d’une pièce à partir de sa largeur.

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

Les mesures sont centrées, exactement comme au chapitre 1 et pour une raison qui rapportera gros avant la fin de ce chapitre. Le modèle est une droite, y^=ax+b\hat{y} = a x + b, et la perte est l’erreur quadratique moyenne dérivée au chapitre précédent :

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

Deux paramètres. Pourquoi ne pas simplement essayer beaucoup de valeurs ? Faisons-le vraiment — une grille de a=0a = 0 à 55 et de b=5b = -5 à 55, par pas 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

Un demi-million d’évaluations pour fixer deux nombres à deux décimales près — et cette seconde est du temps réel sur une machine, donc une relance tombe quelque part entre trois et six ; le nombre d’évaluations et le minimum sont les éléments reproductibles. À la fin de ce chapitre, gradient descent obtient quatre décimales en huit étapes et la réponse float64 complète en trente-six.

Mais la vitesse n’est pas l’argument, et c’est le point qui décide de tout le cours. La recherche sur grille coûte kPk^P évaluations pour PP paramètres à kk valeurs chacun. Avec mille valeurs par axe :

modèleparamètresévaluations de grille
cette droite210610^{6}
le réseau XOR du chapitre 59102710^{27}
un petit réseau multicouche20 0001060,00010^{60{,}000}

La troisième ligne n’est pas un grand nombre, c’est un nombre dépourvu de sens — il y a environ 108010^{80} atomes dans l’univers observable. La recherche ne devient pas plus lente quand les modèles grandissent ; elle cesse d’exister. Tout ce qui suit existe à cause de ce tableau.

Une dérivée est une mesure que vous pouvez prendre

Lien vers la section : Une dérivée est une mesure que vous pouvez prendre

Fixons b=0b = 0 un instant pour n’avoir qu’un paramètre et une courbe, l’image laissée par le dernier chapitre. Prenez un point dessus, a=1a = 1, et demandez-vous : si je décale aa d’une petite quantité hh, de combien la perte bouge-t-elle, par unité de décalage ?

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

Ce rapport est une élévation sur une distance — la pente de la droite passant par deux points de la courbe. Quand hh rétrécit, les deux points glissent l’un vers l’autre et la droite devient la tangente. Sa pente est la dérivée L(a)L'(a) : le taux auquel la perte change par unité de changement de aa. Ce n’est ni une approximation de quoi que ce soit, ni une quantité infiniment petite. C’est une limite de rapports ordinaires.

Cela vaut la peine de l’exécuter, parce que les nombres disent quelque chose que la définition ne dit pas :

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

Deux choses se produisent ici, et les deux sont essentielles.

L’erreur n’est pas vaguement proportionnelle à hh — elle est exactement 7.445h7.445\,h. Divisez hh par cent, l’erreur est divisée par cent, à quatre chiffres significatifs à chaque fois. Cette constante n’est pas décorative : c’est la moitié de la seconde dérivée de la perte, et c’est la première apparition d’une idée dans deux sections — qu’une courbe près d’un point ressemble à une droite plus une correction proportionnelle à h2h^2.

Puis le motif se brise. Sous h=108h = 10^{-8}, l’estimation devient pire, et à 101410^{-14} elle est fausse dès le deuxième chiffre. Rien de mathématique ne s’est produit ; la boîte à nombres à virgule flottante du dernier chapitre, si. L(a+h)L(a+h) et L(a)L(a) concordent sur leurs dix premiers chiffres, les soustraire détruit ces chiffres, et diviser les débris par un minuscule nombre amplifie ce qui reste. Il existe un meilleur hh — ici autour de 10810^{-8}, à peu près la racine carrée de l’epsilon machine — et aller plus petit n’est pas plus prudent, c’est moins prudent. Gardez cela en tête ; une fonction à la fin de ce chapitre en dépend.

La pente exacte, issue du calcul plutôt que de la mesure, est 16.385-16.385. Nous pouvons donc arrêter de mesurer et commencer à dériver.

Voici l’idée sur laquelle repose le reste du cours, énoncée une fois, simplement.

Composer deux fonctions consiste à faire entrer l’une dans l’autre : (fg)(x)=f(g(x))(f \circ g)(x) = f(g(x)). Rien de plus.

Un réseau profond n’est pas comme une composition. Il en est une. Une couche est une fonction ; empiler des couches, c’est les composer ; la « profondeur » est le nombre de fonctions dans la chaîne. Quand le chapitre 5 construit un réseau, il construit f4f3f2f1f_4 \circ f_3 \circ f_2 \circ f_1 et rien d’autre. Ce qui signifie que la règle de calcul la plus importante, pour nous, est celle qui dérive une composition :

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

Les taux se multiplient. Si gg change trois fois plus vite que xx, et si ff change deux fois plus vite que gg, alors ff change six fois plus vite que xx. C’est tout le contenu, et c’est pourquoi un signal qui repasse en arrière à travers dix couches est multiplié par dix nombres — ce qui explique pourquoi le chapitre 6 consacre une section à ce qui se passe quand ces nombres sont tous légèrement inférieurs à un.

Utilisons-la sur notre perte. Écrivons le résidu ri=axi+byir_i = a x_i + b - y_i, de sorte que L=1nri2L = \frac{1}{n}\sum r_i^2. Chaque rir_i dépend de aa via la fonction interne axia x_i, dont la dérivée est xix_i. Règle de chaîne, terme par terme :

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

Ces symboles bouclés \partial marquent une dérivée partielle : dériver par rapport à une variable et traiter toutes les autres comme des constantes. Rien de nouveau ne se passe — c’est la même limite qu’avant, prise le long d’un axe. Rassemblez les dérivées partielles dans un vecteur et vous obtenez le gradient :

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

Au point (a,b)=(1,4)(a, b) = (1, 4), ce vecteur vaut (16.385, 8.0)(-16.385,\ 8.0). Deux nombres. La question est ce qu’ils signifient, et c’est la première étape que tout le monde saute.

Le gradient est un vecteur de pentes le long des axes. C’est tout ce que nous avons prouvé. Il n’est pas évident — et ne devrait pas l’être — que les assembler dans un vecteur produise quelque chose qui pointe dans une direction particulière.

Définissons donc ce que nous voulons réellement. Choisissez un vecteur unitaire u\mathbf{u}, une direction. La dérivée directionnelle est le taux auquel la perte change quand vous marchez dans cette direction :

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

La règle de chaîne transforme cela en quelque chose de calculable. Marcher le long de u\mathbf{u} fait changer aa au taux u1u_1 et bb au taux u2u_2, et les contributions s’additionnent :

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}

Le taux de changement dans n’importe quelle direction est le produit scalaire du gradient avec cette direction. Et maintenant la chute, qui tient en une ligne de géométrie. En écrivant le produit scalaire avec l’angle ϕ\phi entre les vecteurs,

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

puisque u\mathbf{u} a une longueur de 1. La seule chose que vous contrôlez est cosϕ\cos\phi, qui est maximal à ϕ=0\phi = 0 et minimal à un demi-tour, ϕ=180\phi = 180 degrés. Donc :

  • La montée la plus raide est dans la direction de L\nabla L lui-même, et la pente y vaut exactement L\lVert \nabla L \rVert.
  • La descente la plus raide est dans la direction de L-\nabla L, et la pente y vaut L-\lVert \nabla L \rVert.
  • Perpendiculairement au gradient, la perte ne change pas du tout. C’est pourquoi les lignes d’une carte de contours croisent le gradient à angle droit.

Voilà le signe moins. Pas une convention, pas une inversion de signe choisie par quelqu’un : la direction de décroissance la plus rapide est le gradient négatif parce que cosϕ\cos\phi est minimisé à un demi-tour, et pour aucune autre raison.

Comme c’est une affirmation sur toutes les directions, testons-la contre toutes les directions. Échantillonnez-en 3 600, une par dixième de degré, et mesurez chacune en faisant un petit déplacement :

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

Une recherche qui ne sait rien des gradients, sur 3 600 directions, trouve sa montée la plus raide à 154,0 degrés — la direction même du gradient, à la résolution de 0,1 degré près de la recherche. Et la pente qu’elle y trouve, 18,2337, est la longueur du gradient à six chiffres près. Le théorème n’est pas une histoire sur ce que les gradients signifient ; c’est un fait mesurable, et voici la mesure.

Maintenant la deuxième étape sautée. Nous savons dans quelle direction se trouve le bas. Il ne s’ensuit pas que marcher dans cette direction abaisse la perte, parce que « vers le bas » est une affirmation sur un déplacement infinitésimal, et qu’un pas n’est pas infinitésimal.

Le pont est la linéarisation. Près d’un point, une fonction lisse est sa tangente plus une correction :

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

C’est le développement de Taylor du premier ordre. Le O(δ2)O(\lVert\boldsymbol{\delta}\rVert^2) écarté est la courbure — le même terme qui rendait l’estimation du tableau des pentes fausse d’exactement 7.445h7.445\,h. Insérons le pas que nous comptons faire, δ=η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

La perte baisse de ηL2\eta \lVert \nabla L \rVert^2. Toutes les parties de cette expression sont non négatives, donc la promesse est réelle — pour un η\eta assez petit, parce que le terme négligé croît comme η2\eta^2 et finit par l’engloutir. C’est toute la théorie. Voici la promesse tenue, puis rompue :

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

Lisez depuis le bas. Quand η\eta rétrécit, la baisse obtenue converge vers celle promise — ratio 0,99938, puis 0,99994 — ce qui est le théorème de Taylor en train d’avoir raison. Lisez depuis le haut et, à η=0.2\eta = 0.2, la « baisse » obtenue est moins seize. Le pas allait vers le bas et la perte a augmenté.

La règle de mise à jour est donc

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

et elle vient avec une condition que personne n’énonce : η\eta doit être assez petit. Assez petit par rapport à quoi, exactement, est la section suivante.

Le taux d’apprentissage a un plafond, et il est calculable

Lien vers la section : Le taux d’apprentissage a un plafond, et il est calculable

Commençons par la vallée la plus simple qui soit, f(x)=x2f(x) = x^2, où f(x)=2xf'(x) = 2x. Une étape de gradient descent est

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

La position est multipliée par (12η)(1 - 2\eta) à chaque étape. C’est une suite géométrique, et les suites géométriques ont une seule règle : elles rétrécissent quand le multiplicateur est inférieur à 1 en valeur absolue, et grandissent sinon. Donc 12η<1\lvert 1 - 2\eta \rvert < 1, c’est-à-dire 0<η<10 < \eta < 1.

La frontière est exactement à η=1\eta = 1. Pas « autour de 1 », pas « 1 est généralement trop grand ». À η=1\eta = 1, le multiplicateur vaut 1-1 et le point rebondit entre xx et x-x indéfiniment, sans s’approcher ni s’échapper. En dessous, convergence ; au-dessus, divergence. L’intervalle se scinde encore à η=0.5\eta = 0.5, où le multiplicateur change de signe : en dessous, l’approche est monotone ; au-dessus, le point dépasse le minimum et alterne de côté ; et à exactement 0.50.5, le multiplicateur vaut 0 et un seul pas atterrit sur le minimum.

Quatre régimes, issus de quatre lignes d’algèbre. Allez franchir les frontières vous-même :

14 étapes, jusqu’à x = -0.0836.

Voir les données sous forme de tableau
Étapexf(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⁩
Descente de gradient interactive

Quatorze étapes à un taux de 0,1, depuis x=1.9x = -1.9, finissant à 0.0836-0.0836. Poussez le taux à 0,5 et le tout premier pas atterrit au fond. Poussez-le à 0,9 et il finit au même 0.0836-0.0836 que 0,1 — même distance, style opposé, parce que 12η\lvert 1 - 2\eta \rvert vaut 0,8 dans les deux cas — mais il y arrive en zigzaguant à travers la vallée au lieu de descendre d’un seul côté.

Et maintenant le cas intéressant :

14 étapes, jusqu’à x = -1.9000.

Voir les données sous forme de tableau
Étapexf(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⁩
Descente de gradient interactive

Exactement sur la frontière. Quatorze étapes à un taux de 1, et cela se termine à 1.9-1.9 : précisément là où cela avait commencé, après n’avoir rien fait d’autre que rebondir. Un tout petit cran plus haut et les rebonds grandissent au lieu de se maintenir ; à 1,2, c’est hors graphique en quatre étapes. Un taux trop grand ne converge pas lentement. Il ne converge pas.

Maintenant la règle générale, qui tombe du même argument. Le multiplicateur 12η1 - 2\eta était en réalité 1ηf1 - \eta f'', et près d’un minimum une perte à plusieurs paramètres a un tel nombre par direction — les valeurs propres de la matrice des secondes dérivées. Toutes les directions doivent être stables en même temps, donc le plafond est fixé par la plus grande :

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

Pour f(x)=x2f(x) = x^2, f=2f'' = 2, plafond 1, ce que nous venons de dériver. Pour notre tapis, la matrice des secondes dérivées est 2nAA\frac{2}{n} A^{\top} A avec AA la matrice à deux colonnes des entrées, et ses valeurs propres sont 2 et 14,89, donc le plafond est 2/14.89=0.134322 / 14.89 = 0.13432. C’est une prédiction à cinq chiffres significatifs. Testons-la :

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

Cinq décimales d’accord entre une ligne d’algèbre linéaire et cent mille itérations d’une boucle for.

Et voici où le chapitre 1 revient. Tout ce qui précède utilisait les mesures centrées. Exécutez le code identique sur les millimètres et grammes bruts et les valeurs propres sont 0,0298 et 998,1 au lieu de 2 et 14,89. Le plafond s’effondre de 0,134 à 0,002004 — tout aussi exactement, en convergeant à lr=0.002003 et en explosant à lr=0.002004.

Pire que le plafond : le rapport entre les valeurs propres. Le nombre de conditionnement mesure à quel point la vallée s’éloigne d’une forme ronde : une longue tranchée étroite impose un taux assez petit pour les parois raides, puis le fond de la tranchée est parcouru à ce même pas de tortue. Le nôtre passe de 7,44 centré à 33 452 brut. Avec le meilleur taux que chaque version puisse prendre :

caractéristiquesnombre de conditionnementmeilleur tauxétapes pour être à moins de 1 % de l’optimum
centrées7,440,118410
millimètres et grammes bruts33 4520,002003779 513

Mêmes données, même code, même réponse à la fin — et huit mille fois plus de travail, parce que personne n’a soustrait une moyenne. Au chapitre 1, la même omission a coûté au perceptron un facteur six mille en époques, et le diagnostic y était géométrique : les données flottaient loin de l’origine. C’est la même géométrie ici, déguisée en optimisation, et c’est pourquoi la normalisation des entrées n’est pas un conseil d’hygiène mais de l’arithmétique.1

Rien de ce qui précède n’avait besoin d’une bibliothèque. Voici tout l’optimiseur.

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

La solution des moindres carrés en forme fermée pour ces huit points est a=2.100403a = 2.100403, b=0b = 0, avec une perte de 24.59244924.592449. La boucle l’a trouvée à huit chiffres significatifs sans savoir qu’une forme fermée existe — ce qui compte, parce qu’à partir du chapitre 5 il n’y en aura plus.

La trajectoire, puisque la regarder est le but :

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

La majeure partie de la distance est couverte dans les deux premières étapes, parce que le gradient est maximal quand vous êtes le plus loin du fond et rétrécit à mesure que vous approchez. Gradient descent ralentit automatiquement près d’un minimum. C’est une fonctionnalité et ce sera aussi, au chapitre 6, un problème.

L’argument jusqu’ici a un trou. Le pas s’arrête quand L=0\nabla L = \mathbf{0}, et nous avons appelé cela « le minimum ». Un point à gradient nul est un point critique, et être un minimum n’est qu’une des façons d’en être un :

  • un minimum local : montée dans toutes les directions, mais pas nécessairement le plus bas de tous ces points ;
  • un maximum local : descente dans toutes les directions ;
  • un point selle : montée dans certaines directions et descente dans d’autres. La surface f(x,y)=x2y2f(x,y) = x^2 - y^2 a f=(2x,2y)\nabla f = (2x, -2y), qui vaut zéro à l’origine, où la fonction est à la fois un minimum le long de l’axe xx et un maximum le long de l’axe yy.

Gradient descent ne peut pas les distinguer, parce qu’il ne regarde jamais que le gradient, et le gradient est nul dans les trois cas.

Notre droite a un seul point critique, et c’est la réponse — une perte d’erreur quadratique sur un modèle linéaire est convexe, un seul bol, et la descente sur elle ne peut pas échouer à trouver le minimum global. Cette propriété ne survivra pas au contact de ce cours. La perte d’un réseau neuronal n’est pas convexe, et à partir du chapitre 5 « le minimum » n’est pas une chose qui existe : il y en a beaucoup, de profondeurs différentes, et celui que vous obtenez dépend de votre point de départ. Cela tient en une phrase et restera une phrase, parce que la théorie est vaste et la conséquence pratique faible.

Vous pouvez voir toute la conséquence sur une seule courbe. Prenez f(x)=x44x22+x10f(x) = \tfrac{x^4}{4} - \tfrac{x^2}{2} + \tfrac{x}{10}, qui possède deux vallées de profondeurs différentes :

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 étapes, jusqu’à x = 0.9456.

Voir les données sous forme de tableau
Étapexf(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⁩
Descente de gradient interactive

Quarante étapes depuis x=0.11x = 0.11, qui se stabilisent à 0.94560.9456 — la moins profonde des deux vallées. Déplacez maintenant le point de départ d’un cran à gauche, à 0.100.10. Même taux, mêmes quarante étapes, et cela se stabilise plutôt à 1.0461-1.0461, où la perte est 0,199747 plus basse. La ligne de partage des eaux est la bosse à 0.1010310.101031, et toute la différence entre les deux réponses tient au côté duquel vous avez commencé.

Atterrir dans la vallée peu profonde donne une perte 56,7 % pire, et l’algorithme n’a aucun moyen de le savoir, parce que depuis l’intérieur d’une vallée toutes les directions montent. Gradient descent ne répare pas cela, et aucune réparation n’arrive. Ce qu’il y a, en pratique, c’est le constat que cela compte beaucoup moins que cette image ne le suggère — dans les très hautes dimensions d’un vrai réseau, la plupart des points critiques se révèlent être des selles plutôt que des pièges,2 et le chapitre 5 mesure à quelle fréquence un petit réseau se retrouve réellement bloqué.

Des pas moins chers : stochastique, minibatch, momentum

Lien vers la section : Des pas moins chers : stochastique, minibatch, momentum

Une chose dans grad ci-dessus devrait vous gêner : il somme sur tout le dataset à chaque pas. Huit pièces, ce n’est rien. Un million, c’est un million de calculs de gradient pour déplacer les paramètres une fois.

La sortie de secours est que le gradient est une moyenne, et qu’une moyenne peut être estimée à partir d’un échantillon. Calculez-le sur une poignée aléatoire — un minibatch — et faites un pas avec cela. L’estimation est bruitée ; elle est aussi non biaisée, et des centaines de pas bruités bon marché battent un seul pas exact coûteux. Sur cent mille pièces synthétiques, en comptant les gradients par exemple plutôt que les étapes :

méthodeétapes pour être à moins de 0,1 % de l’optimumgradients par exemple
batch complet7700 000
minibatch de 321003 200
un exemple à la fois17 58017 580

Deux cent dix-neuf fois moins d’arithmétique pour atteindre le même endroit. Et l’extrême — un exemple à la fois, l’approximation stochastique originale de Robbins et Monro3 — n’est pas gagnant : il est cinq fois pire que des batchs de 32, parce que 32 exemples ne coûtent presque rien de plus qu’un seul sur du matériel qui multiplie des matrices, tandis que le bruit décroît avec la racine carrée de la taille du batch. Ce compromis est la raison pour laquelle chaque script d’entraînement que vous lirez aura un batch_size dedans.

Momentum est l’autre correctif bon marché, et il vise directement la tranchée. Dans une vallée mal conditionnée, les pas zigzaguent à travers la direction étroite tout en rampant le long de la longue. Momentum conserve une moyenne courante des gradients passés, de sorte que les composantes oscillantes s’annulent et que la composante cohérente s’accumule :4

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

Deux lignes de plus. Sur le tapis brut non centré — nombre de conditionnement 33 452, le pire cas que nous ayons — au meilleur taux que la descente simple puisse prendre :

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 facteur 172 pour deux lignes de code. Le chapitre 6 transforme cela en Adam ; le mécanisme est déjà là.

La vérification dont vous aurez besoin au chapitre 5

Lien vers la section : La vérification dont vous aurez besoin au chapitre 5

Chaque gradient de ce chapitre a été dérivé à la main et pouvait donc être faux. Le correctif est le tableau des pentes du début : mesurer numériquement la dérivée et comparer. Utilisez la différence centrale, L(θ+h)L(θh)2h\frac{L(\theta+h) - L(\theta-h)}{2h}, qui annule le terme d’erreur dominant et est beaucoup plus précise pour le même 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)))

La forme relative de la comparaison compte : une différence absolue de 10410^{-4} est catastrophique sur un gradient de taille 10310^{-3} et sans importance sur un gradient de taille 10610^{6}.

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

La première ligne est le gradient dérivé à la main ci-dessus. La seconde est la même fonction avec le facteur 2 oublié sur une composante — une faute de frappe d’un seul caractère — et la vérification l’attrape immédiatement. Tout ce qui est sous environ 10710^{-7} est un accord ; tout ce qui est au-dessus de 10410^{-4} est un bug. Gardez cette fonction : le chapitre 5 l’utilise pour déboguer un moteur de différentiation automatique, et c’est la seule raison pour laquelle un gradient faux peut être trouvé.

Tout ce chapitre reposait sur une hypothèse qui n’a jamais été énoncée : que vous pouvez écrire L/θ\partial L / \partial \theta.

Pour une droite à deux paramètres, c’était une ligne d’algèbre. Cela cesse presque immédiatement d’en être une. Demandez à un système d’algèbre symbolique la dérivée de la perte d’un réseau par rapport à un seul poids de première couche, pour un seul exemple, et comptez l’arithmétique dans la réponse :

réseauopérations dans une dérivée partielle
quatre unités cachées, une couche40
quatre unités cachées, deux couches301
quatre unités cachées, trois couches1 717

La troisième ligne est un réseau avec 57 paramètres — un réseau si petit qu’il serait une note de bas de page au chapitre 6 — et écrire son gradient à la main signifie environ 97 869 opérations pour un seul exemple d’entraînement. Aucune notation ne sauve cela. Ce qui le sauve, c’est l’observation que la règle de chaîne appliquée à une composition possède une énorme structure, que les mêmes quantités intermédiaires apparaissent encore et encore, et que les calculer dans le bon ordre donne toutes les dérivées pour à peu près le prix d’un forward pass. C’est le chapitre 5.

Mais il y a d’abord un problème plus petit, et il attend juste après.

Nous avons maintenant une machine qui dévalera n’importe quelle perte différentiable. Dirigez-la vers la question originale du tapis — accepter ou rejeter, une cible qui vaut 1 ou 0 — mettez une sigmoïde en sortie pour qu’elle prédise une probabilité, et minimisez l’erreur quadratique. Elle tournera. Elle bougera aussi à peine quand elle se trompera le plus, et le gradient dit pourquoi :

sortie zzprédictionvéritégradient avec erreur quadratiquegradient avec entropie croisée
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 modèle qui se trompe de manière confiante et catastrophique — prédisant 0,0000454 quand la réponse est 1 — produit un gradient d’erreur quadratique de 9×1059 \times 10^{-5}. Il n’a aucune idée qu’il est en difficulté. L’autre colonne, issue d’une perte que nous n’avons pas encore dérivée, rapporte 1,0 : urgence maximale, exactement là où elle est méritée.

Ce qui soulève la question par laquelle s’ouvre le chapitre suivant. Le dernier chapitre disait qu’une perte est une hypothèse sur le bruit, et que l’erreur quadratique suppose un bruit gaussien. Quel modèle de bruit a une réponse oui-ou-non — et quelle perte apparaît quand vous appliquez la même dérivation à ce modèle ?


La méthode est plus ancienne que toutes ces références : Cauchy l’a décrite dans une note à l’Académie des Sciences en 1847, comme une façon de résoudre des systèmes d’équations en descendant la somme de leurs résidus quadratiques. À lire aussi en parallèle de ce chapitre : An overview of gradient descent optimization algorithms de Sebastian Ruder (arXiv:1609.04747), qui couvre momentum jusqu’à Adam en quatorze pages lisibles ; le chapitre 3 de Numerical Optimization de Nocedal et Wright (2e éd., Springer, 2006), dont le théorème 3.3 donne le taux de convergence de la descente la plus raide sur une quadratique en fonction du nombre de conditionnement — c’est la théorie derrière le fait que le conditionnement décide du nombre de pas, même s’il traite la recherche linéaire plutôt que le plafond à pas fixe 2/λmax2/\lambda_{\max} mesuré ci-dessus — ou les §5.8 et §7.1 de Mathematics for Machine Learning de Deisenroth, Faisal et Ong pour le même terrain avec moins de machinerie ; le §6.1 de Understanding Deep Learning de Prince et le §4.3 de Deep Learning de Goodfellow, Bengio et Courville ; Dive into Deep Learning §12.1–12.3, qui présente l’analyse minibatch avec plus de mesures qu’il n’y a de place ici ; et le chapitre 4 de Hands-On Machine Learning de Géron (3e éd.), le traitement le plus pratique du taux d’apprentissage comme chose que l’on règle plutôt que que l’on dérive. Les notes du MIT 6.390 placent gradient descent avant la classification, comme ce cours, et pour la même raison.

  1. LeCun, Y., Bottou, L., Orr, G. B. et Müller, K.-R. Efficient BackProp, dans Neural Networks: Tricks of the Trade (Springer, 1998), p. 9–50. La section 4.3 donne la recommandation et la section 5.1 l’argument utilisé dans l’encadré de détail ci-dessus : centrer et mettre à l’échelle les entrées change les valeurs propres de la matrice des secondes dérivées, et donc le nombre d’étapes, pas seulement le confort numérique.

  2. Dauphin, Y. N., Pascanu, R., Gulcehre, C., Cho, K., Ganguli, S. et Bengio, Y. Identifying and attacking the saddle point problem in high-dimensional non-convex optimization, arXiv:1406.2572 (2014). L’argument selon lequel, en haute dimension, les points critiques sont massivement des selles plutôt que des minima locaux, puisqu’un minimum exige que chacune de milliers de directions courbe vers le haut en même temps.

  3. Robbins, H. et Monro, S. A Stochastic Approximation Method. Annals of Mathematical Statistics 22(3), p. 400–407 (1951). L’article qui a établi qu’une estimation bruitée d’un gradient suffit, avec une taille de pas qui rétrécit de la bonne manière.

  4. Polyak, B. T. Some methods of speeding up the convergence of iteration methods. USSR Computational Mathematics and Mathematical Physics 4(5), p. 1–17 (1964). La méthode heavy-ball, qui est la mise à jour momentum ci-dessus, vingt-deux ans avant que backpropagation n’atteigne ce domaine.

Prêt à laisser LIA choisir à votre place ?

Créez avec tous les modèles d'IA au même endroit — commencez gratuitement dès aujourd'hui.