FichesCarte › Partie 08 · Calcul, numérique, tenseurs › chaîne 01

Jacobienne, chain rule, backprop

Un réseau est une composition de fonctions. Comment obtenir ∂L/∂θ pour chaque poids, et pourquoi en remontant depuis la sortie plutôt qu'en descendant depuis l'entrée ? Réponse en deux temps : une dérivée est une matrice, et l'ordre dans lequel on multiplie ces matrices décide du coût. Fil rouge : un neurone logistique, une observation — x = 2, w = 0,5, b = 0, y = 1 — puis trois couches de largeur 1 000 où l'ordre fait un facteur 500.

Ce que cette chaîne suppose acquis
  • Une matrice est une application linéaire. Le produit matriciel est la composition de deux applications, et multiplier une (m×k) par une (k×n) coûte m·k·n multiplications (p04-01, fiche à venir).
  • Le gradient est la direction de plus forte pente et on descend en −ηL (p03-02, pas 2). Cette chaîne-ci calcule le gradient ; p03-02 l'utilise. Ne pas confondre les deux moitiés d'une itération.
  • La dérivée d'une fonction RR est une pente locale : f(x + δ) ≈ f(x) + f′(x)δ. Tout le pas 2 consiste à lire ce « ≈ » en dimension quelconque.
  • La compensation −1/p contre p(1−p). Déjà rencontrée comme « pourquoi la log-loss a un gradient sain » (p03-01, pas 6) ; ici elle devient un produit de jacobiennes.
Hypothèses posées
H1Chaque bloc du réseau est dérivable, ou dérivable presque partout (ReLU en 0 : on choisit une valeur et on avance). Sans cela il n'y a pas de jacobienne à multiplier. H2La loss est un scalaire. C'est cette hypothèse, et elle seule, qui rend le passage arrière bon marché — le pas 5 en fait le cœur de la chaîne. H3Les activations intermédiaires sont gardées en mémoire pendant l'aller. Chaque jacobienne doit être évaluée au point que l'aller a produit ; sans ces points, pas de retour. C'est la facture mémoire du pas 9.

La chaîne

Le décor : des millions de θ, une seule L

Un réseau est une composition, et la loss se pose au bout :

L = loss(f3(f2(f1(x ; θ1) ; θ2) ; θ3))

On veut ∂L/∂θ pour chaque θ. L'asymétrie du problème est tout entière dans le comptage : des millions d'entrées à dériver, une seule sortie à dériver. Le pas 5 montrera que c'est exactement cette asymétrie qui dicte le sens de parcours.

« Le réseau est une composition de fonctions, donc sa dérivée se calculera par la chain rule, donc il reste à choisir dans quel ordre enchaîner les facteurs, et ce choix se fera sur le comptage : des millions de paramètres contre une seule loss. »

Fil rouge — un neurone logistique

La plus petite composition qui a toutes les pièces : x = 2, w = 0,5, b = 0, étiquette y = 1.

z = wx + b  →  p = σ(z)  →  L = −log p
étapeformulevaleur
pré-activationwx + b = 0,5·2 + 01
probabilitéσ(1) = 1/(1 + e−1)0,731
loss−log 0,7310,313

Trois fonctions emboîtées, deux paramètres. À l'échelle d'un vrai réseau, seuls les nombres changent — pas le geste.

Une dérivée est une matrice tronc

Pour f : RnRm, la jacobienne JRm×n est l'application linéaire qui approxime f au point x :

f(x + δ) ≈ f(x) + J δ     Jij = ∂fi/∂xj

Ligne i = le gradient de la sortie i. Cas m = 1 : J n'a qu'une ligne, c'est ∇. Le gradient est donc un cas particulier de jacobienne, pas un objet à part.

« Localement une fonction est linéaire, donc sa dérivée est une matrice, donc composer des fonctions revient à multiplier leurs matrices locales. »

Application — les trois jacobiennes du neurone
blocdimensionsjacobienne au point courant
z = wx + b, vue en (w, b)R2R[ x   1 ] = [ 2   1 ]
p = σ(z)RR[ p(1−p) ] = [ 0,197 ]
L = −log pRR[ −1/p ] = [ −1,368 ]

Chaque case est un nombre évalué en un point précis : 0,197 n'est la jacobienne de σ qu'en z = 1. Une couche dense h = Wa a pour jacobienne en a la matrice W elle-même — la dérivée d'une application linéaire est cette application.

Chain rule = produit de jacobiennes tronc

Composer deux applications linéaires, c'est multiplier leurs matrices. La même phrase, écrite localement, est la chain rule :

Jfg(x) = Jf(g(x)) · Jg(x)

Deux choses à lire. L'ordre : celui de la composition, donc de droite à gauche. Le point d'évaluation : Jf se prend en g(x), pas en x — d'où H3, il faut avoir gardé g(x).

« La composée de deux applications linéaires est leur produit, donc la jacobienne d'une composée est le produit des jacobiennes, chacune prise au point courant, donc il faut avoir gardé les points de l'aller. »

Application — le produit, écrit en entier

Sur le neurone, en w :

L/∂w = (∂L/∂p) · (∂p/∂z) · (∂z/∂w) = (−1,368) × 0,197 × 2

Aucun facteur n'est élidé : trois blocs, trois facteurs, dans l'ordre inverse de l'aller. Le résultat est −0,538.

Contrôle indépendant. En différence finie, L(w + h) − L(wh) sur 2h avec h = 10−6 donne −0,5378828 : le produit des trois jacobiennes tombe juste. C'est le test que micrograd fait passer à chaque nœud.

Et si l'on change le point : en z = 4, ∂p/∂z vaut 0,018 au lieu de 0,197. Le point d'évaluation n'est pas un détail, c'est ce qui rend le retour dépendant de l'aller.

Sur le neurone : chaque facteur a un sens

Trois facteurs, trois lectures. ∂L/∂p = −1/p : le log punit les petites probabilités, et d'autant plus fort qu'elles sont petites. ∂p/∂z = p(1−p) : la sigmoïde écrase aux extrêmes, au plus ¼ au centre. Leur produit vaut exactement py : les deux se compensent (p03-01, pas 6).

Reste ∂z/∂w = x : le gradient d'un poids est signal d'erreur × entrée.

« Le gradient d'un poids est le gradient de sa sortie fois son entrée, donc une entrée nulle n'apprend rien, donc un signal d'erreur nul n'apprend rien. »

Application — le retour chiffré
quantitéformulevaleur
L/∂p−1/p−1,368
p/∂zp(1−p)0,197
L/∂zproduit = py−0,269
L/∂w(pyx−0,538
L/∂b(py)·1−0,269

Le produit −1,368 × 0,197 rend −0,2689 ; p − 1 = 0,7311 − 1 = −0,2689. Égalité exacte, pas numérique : (−1/pp(1−p) = −(1−p).

Les deux zéros. Si x = 0, ∂L/∂w = 0 quelle que soit l'erreur : ce poids n'apprend pas de cette observation. Si p = y, tous les poids en amont reçoivent 0. Un seul facteur nul suffit à éteindre la ligne entière.

Figure 1 — le graphe de calcul, à l'aller puis au retour

Le neurone comme graphe. « Aller » remplit les valeurs en bleu, de gauche à droite. « Retour » remplit les grad en rouge, un nœud à la fois, en partant de L où grad = 1 ; les flèches rouges vont à contresens des grises. Chaque flèche porte sa dérivée locale : traverser une flèche, c'est multiplier par elle. Lis le rouge de droite à gauche : 1, puis ×(−1/p), puis ×p(1−p), puis ×x pour w et ×1 pour b.

L'ordre des produits décide du coût tronc

Le produit J3·J2·J1 est associatif : le résultat ne dépend pas du parenthésage, le coût si. Loss scalaire (H2) ⇒ J3 est une ligne.

Depuis la sortie, ligne × matrice = ligne : on reste une ligne à chaque étape. Depuis l'entrée, matrice × matrice = matrice pleine m×n, qu'on paye m·m·n.

C'est le VJP (vecteur-jacobienne) : on ne construit jamais de jacobienne intermédiaire, on transporte un vecteur.

« La loss est scalaire, donc la dernière jacobienne est une ligne, donc en partant d'elle chaque produit reste une ligne et coûte une matrice, donc partir de la sortie coûte m² là où partir de l'entrée coûte m²n. »

Application — trois couches, m = n = 1 000

J3Rm, J2Rm×m, J1Rm×n. Une multiplication (a×b)(b×c) coûte abc.

ordrepremier produitpuistotal
depuis la sortie(J3J2) : 1·m·m = 106(·)J1 : 1·m·n = 1062,0 · 106
depuis l'entrée(J2J1) : m·m·n = 109J3(·) : 1·m·n = 1061,001 · 109

Rapport 500,5 — et il grandit avec la profondeur, chaque couche supplémentaire ajoutant un m² d'un côté contre un m²n de l'autre.

Mémoire aussi. L'ordre « depuis l'entrée » matérialise une matrice 1 000 × 1 000 ; l'ordre « depuis la sortie » ne stocke jamais qu'un vecteur de 1 000 nombres. Le facteur 500 sur le calcul se double d'un facteur 1 000 sur l'objet intermédiaire.

Figure 2 — les deux ordres, en échelle logarithmique

Nombre de multiplications des deux parenthésages, en log10, pour trois couches. Bouge m pour déplacer le curseur sur les deux courbes, bouge n pour redessiner les deux courbes : la ligne reste une ligne, la matrice explose. L'écart vertical entre les deux courbes est log10 du rapport ; à m = n = 1 000, il vaut 2,7 — soit le facteur 500,5 du tableau ci-dessus.

Backprop = VJP couche par couche

On note gk = ∂L/∂hk le gradient arrivant sur la sortie de la couche k. Deux lignes suffisent :

gk−1 = gk · Jk      ∂L/∂θk = gk · ∂hk/∂θk

La première fait passer le gradient à la couche précédente, la seconde le dépose sur les paramètres. Chaque couche ne connaît que sa jacobienne locale : aucune n'a besoin de savoir ce qu'il y a autour. C'est exactement micrograd — un nœud reçoit grad, le multiplie par sa dérivée locale, le pousse à ses enfants.

« Chaque nœud reçoit le gradient de sa sortie, le multiplie par sa dérivée locale et l'accumule sur ses entrées, donc l'ordre est topologique inverse, donc on part de la loss. »

Application — le fil rouge relu en g
nœudg reçu× dérivée localeg poussé
L1 (racine)1
p1× (−1/p) = −1,368−1,368
z−1,368× p(1−p) = 0,197−0,269
w−0,269× x = 2−0,538
b−0,269× 1−0,269

Chaque ligne est un VJP de taille 1. Sur une couche dense h = Wa, la même ligne s'écrit ga = ghW et ∂L/∂W = ghatoujours « signal × entrée », le pas 4 en dimension quelconque.

Ordre topologique inverse : un nœud ne peut pousser que lorsqu'il a reçu la totalité de ce qui lui revient. C'est la condition d'arrêt du tri, et c'est ce que le pas 7 rend nécessaire.

Accumulation : +=, jamais =

Un nœud utilisé deux fois reçoit deux contributions, une par chemin. Elles s'ajoutent :

L/∂a = Σchemins (produit des dérivées le long du chemin)

Écrire grad = … au lieu de grad += … garde la dernière contribution arrivée et jette les autres : le résultat dépend alors de l'ordre de parcours. C'est le bug classique de micrograd, et il est silencieux — le code tourne, la loss descend de travers.

« Un nœud a autant de contributions que de chemins qui le traversent, donc la chain rule sur un graphe est une somme sur les chemins, donc le gradient s'accumule au lieu d'être écrasé. »

Application — le plus petit graphe qui casse

Avec les mêmes nombres que le fil rouge, a = 2 et b = 0,5, mais a branché deux fois :

e = a·b = 1    puis    L = e + a = 3
cheminproduit des dérivées localescontribution
aeL1 × b0,5
aL11
somme1,5

Contrôle : L(a) = ab + a = a(b + 1) = 1,5a, donc ∂L/∂a = 1,5. La différence finie confirme : 1,4999999998.

Avec = au lieu de +=, on lit 0,5 ou 1 selon l'ordre du parcours — jamais 1,5. Sur le fil rouge du pas 4, le bug est invisible : chaque nœud n'y a qu'un seul consommateur. Les poids partagés (convolutions, RNN dépliés, weight tying) sont précisément le cas où il mord.

L'autre sens existe : le forward mode

Rien n'interdit de multiplier depuis l'entrée. On transporte alors une colonne : J1v, puis J2(·), puis J3(·). C'est le JVP (jacobienne-vecteur), et il donne la dérivée directionnelle dans une direction v.

Son coût est proportionnel au nombre d'entrées à couvrir, celui du VJP au nombre de sorties. Un réseau a des millions d'entrées à dériver et une sortie : le mode inverse gagne. Un problème à peu d'entrées et beaucoup de sorties inverse la conclusion.

« Le mode direct transporte une colonne et coûte un passage par entrée, donc il faut autant de passages que de paramètres, donc sur un réseau à une seule loss c'est le mode inverse qui gagne. »

Application — le même réseau, compté dans les deux modes
modecoût d'un passagepassages nécessairestotal
inverse (VJP)m² + mn = 2,0 · 1061 (la loss est scalaire)2,0 · 106
direct (JVP)mn + m² + m = 2,001 · 106n = 1 0002,001 · 109

Rapport 1 000,5 : c'est le nombre d'entrées, exactement. Un passage direct coûte autant qu'un passage inverse — ce qui départage, c'est combien il en faut.

Où le direct gagne : une seule entrée à perturber et beaucoup de sorties à suivre (sensibilité d'une simulation à un paramètre, produits hessienne-vecteur en combinant les deux modes).

Où ça casse casse

Le retour est un produit de facteurs, et un produit long est un objet fragile : il tend vers 0 ou vers l'infini selon la taille typique de ses facteurs. Le reste de la casse est la facture de H1 et de H3.

« Le gradient d'une couche profonde est un produit de beaucoup de jacobiennes, donc sa taille est une puissance de la taille typique d'un facteur, donc elle s'écroule ou diverge, et tout le travail d'architecture consiste à garder ces facteurs près de 1. »

Quatre limites
  • Gradients qui s'évanouissent. p(1−p) ≤ ¼ : vingt sigmoïdes empilées donnent au mieux (¼)20 = 9,1 · 10−13, et bien moins dès qu'une couche sature. La couche 1 n'apprend plus. C'est le pas 3 lu sur vingt couches. Remèdes : ReLU (dérivée 1 dans la zone active), initialisation soignée, batchnorm, connexions résiduelles (qui ajoutent un 1 à chaque jacobienne locale, B2).
  • Gradients qui explosent. Le même produit avec des facteurs > 1 : 1,520 = 3 325, et un pas de descente qui projette les poids à l'autre bout de l'espace. Remède nommé : clipping de la norme du gradient.
  • Mémoire. H3 impose de garder toutes les activations de l'aller jusqu'au retour. C'est ce qui limite la taille de batch bien avant le nombre de paramètres, et ce que le gradient checkpointing échange contre du recalcul : on ne garde qu'une activation sur √k et on refait l'aller par morceaux.
  • Non dérivable. max, argmax, arrondi, échantillonnage : pas de jacobienne, donc pas de chemin pour le gradient (H1 tombe). On remplace par une version lisse (softmax pour argmax, Gumbel-softmax pour un tirage catégoriel) ou on contourne par un estimateur qui ne dérive pas la loss (REINFORCE, straight-through).
Figure 3 — les trois facteurs du neurone, et leur compensation

Les trois facteurs du pas 4, en valeur absolue et en log10, en fonction de z (donc de p = σ(z)). En log, un produit est une somme : la courbe rouge |p − 1| est exactement la somme des deux autres, hauteur par hauteur. Bouge z : à droite la sigmoïde sature et p(1−p) plonge, mais 1/p remonte juste assez pour que le produit reste sain — c'est la compensation de p03-01. À gauche, les deux plongent ensemble : c'est là, et seulement là, que le facteur ¼ de la limite 1 fait son œuvre sur une couche cachée, où aucun log ne vient compenser.

Résumé

À retenir
  1. Une dérivée est une matrice : la jacobienne JRm×n est l'application linéaire locale ; le gradient en est le cas m = 1.
  2. Chain rule = produit des jacobiennes, chacune évaluée au point que l'aller a produit ⇒ garder les activations.
  3. Loss scalaire ⇒ la dernière jacobienne est une ligne ⇒ partir de la sortie garde une ligne : 2 · 106 au lieu de 1,001 · 109 sur trois couches de 1 000. C'est le VJP.
  4. Backprop : chaque nœud multiplie le gradient reçu par sa dérivée locale et l'accumule (+=) sur ses entrées, en ordre topologique inverse.
  5. Gradient d'un poids = signal d'erreur × entrée. Un facteur nul éteint toute la ligne.
  6. Le mode direct (JVP) coûte un passage par entrée ; il gagne quand les entrées sont rares et les sorties nombreuses — l'inverse d'un réseau.
  7. Casse : produits de facteurs < 1 (évanouissement), > 1 (explosion), mémoire des activations, opérations non dérivables.
« La dérivée d'une composition est le produit des jacobiennes ; comme la loss est un scalaire, la dernière est une ligne, et en multipliant depuis la sortie je transporte une ligne au lieu de construire des matrices — coût quadratique au lieu de cubique. C'est le vecteur-jacobienne, et la backprop n'est que ça, couche par couche, avec accumulation quand un nœud sert deux fois. »

Chaîne verbalisée — une prise, à voix haute

6 maillons · clique pour révéler après avoir dit
  1. Qu'est-ce qu'une jacobienne, en une phrase ?
    L'application linéaire qui approxime f localement : f(x + δ) ≈ f(x) + Jδ, de taille m×n, ligne i = gradient de la sortie i.
  2. Que dit la chain rule pour f∘g ?
    Jf(g(x))·Jg(x) : produit dans l'ordre de la composition, chaque facteur évalué au point courant — d'où la mémoire des activations.
  3. Pourquoi partir de la sortie ?
    Loss scalaire ⇒ dernière jacobienne = une ligne ⇒ ligne × matrice reste une ligne : m² + mn contre m²n + mn, facteur 500,5 à m = n = 1 000.
  4. Que fait un nœud en backprop ?
    grad_entrée += grad_sortie × dérivée locale, en ordre topologique inverse. Le += parce qu'un nœud utilisé deux fois reçoit deux chemins.
  5. Sur le neurone logistique, que vaut ∂L/∂w et pourquoi ?
    (p − y)·x = −0,538 : −1/p et p(1 − p) se compensent en p − y, puis on multiplie par l'entrée.
  6. D'où vient l'évanouissement du gradient ?
    Le gradient d'une couche profonde est un produit de beaucoup de jacobiennes ; des facteurs < 1 (p(1−p) ≤ ¼) l'écrasent : (¼)20 = 9,1 · 10−13.