Aller au contenu principal

Des couches et des neurones

Ce que ce chapitre apporte

  • Reconnaître qu'un neurone est exactement le modèle linéaire du chapitre 8.
  • Montrer qu'empiler des couches sans non-linéarité ne produit rien de nouveau.
  • Écrire la passe avant et la passe arrière d'un réseau à une couche cachée.
  • Vérifier un gradient rétropropagé par différence finie.
  • Dire ce que garantit le théorème d'approximation universelle, et ce qu'il ne garantit pas.
  • Choisir un nombre de cellules cachées et reconnaître un échec d'initialisation.
  • Nommer les difficultés propres aux réseaux profonds.
Le chapitre 8 s'est arrêté sur une impossibilité : aucune droite ne sépare deux classes disposées en damier, et sept erreurs sur vingt-huit sont le mieux qu'elle puisse faire. La réponse tient en une couche de calcul supplémentaire, et tout ce qui a été construit depuis le chapitre 5 y sert d'un coup : une erreur à minimiser, un gradient, une descente. La rétropropagation, dont le nom impressionne, n'est que la règle de dérivation des fonctions composées appliquée avec méthode.

Un neurone

Ce qu'est un neurone

Un neurone calcule une somme pondérée de ses entrées, ajoute un biais, puis applique une fonction non linéaire appelée activation :

a=f(wx+b)a = f(w \cdot x + b)

Avec la fonction logistique pour activation, c'est exactement la régression logistique du chapitre 8. Rien de plus, et le vocabulaire ne doit pas faire croire le contraire.

Un neurone est une droite, et le restera
La partie wx+bw \cdot x + b est une combinaison linéaire, donc une droite en deux dimensions, un plan en trois, un hyperplan au-delà. L'activation ne fait qu'écraser le résultat entre deux bornes : elle change la valeur rendue, jamais la forme de la frontière.
La figure ci-dessous le vérifie. Un réseau réduit à une seule cellule cachée, entraîné aussi longtemps qu'on veut sur le damier, se stabilise à sept erreurs sur vingt-huit, c'est-à-dire au plancher que le chapitre 8 avait établi pour une droite.
Ce n'est donc pas la non-linéarité seule qui débloque le problème : c'est le fait d'en avoir plusieurs, et de les combiner.
02460246température (°C)charge du processeur
3000 pas7 / 28 erreursperte 0,4788

7 erreurs sur 28. La frontière se déforme encore : chaque cellule cachée tire dans sa direction, et la sortie combine leurs avis.

Une seule cellule cachée, entraînée trois mille pas. La frontière est et reste une droite, et le compte s'arrête à sept erreurs sur vingt-huit : exactement le plancher démontré au chapitre 8. Une cellule, une direction.

Empiler ne suffit pas

main.py
Sortie
>_ Prêt à exécuter…
La non-linéarité n'est pas un raffinement, c'est la condition
Sans activation, la couche cachée applique zW1z+b1z \mapsto W_1 z + b_1 et la sortie aW2a+b2a \mapsto W_2 a + b_2. Leur composition vaut z(W2W1)z+(W2b1+b2)z \mapsto (W_2 W_1) z + (W_2 b_1 + b_2), c'est-à-dire une seule application linéaire.
Le bloc ne se contente pas de l'affirmer : il calcule W2W1W_2 W_1, obtient une droite, et vérifie qu'elle commet exactement les mêmes erreurs que le réseau à huit cellules.
Quel que soit le nombre de couches empilées, tant qu'aucune n'est non linéaire, le tout se replie sur une droite. Cent couches sans activation valent une régression linéaire, en plus lent.
C'est la raison pour laquelle l'activation est entre les couches et non seulement à la fin : c'est elle qui empêche l'effondrement.

Plusieurs cellules, et la frontière se courbe

02460246température (°C)charge du processeur
3000 pas0 / 28 erreursperte 0,0009

Aucune erreur. Une couche cachée de 4 cellules a produit une frontière qu'aucune droite ne pouvait tracer.

Quatre cellules cachées sur les mêmes points. Avancer depuis le départ montre le trajet complet : la frontière commence droite, parce qu'un réseau aux poids tirés au hasard n'est presque rien, puis se courbe, puis se referme sur les quatre coins. Ce qu'aucune droite ne pouvait faire est atteint en trois mille pas.
Comment quatre droites font une frontière courbe
Chaque cellule cachée calcule sa propre combinaison wx+bw \cdot x + b et l'écrase entre 1-1 et 11. Chacune est donc une question binaire floue : « ce point est-il d'un côté ou de l'autre de ma droite, et à quel point ».
La couche de sortie ne voit plus les attributs d'origine : elle voit les réponses des quatre cellules. Dans cet espace-là, les classes sont devenues séparables par un simple hyperplan, ce qu'elles n'étaient pas dans l'espace de départ.
C'est la seconde issue annoncée au chapitre 8 : plutôt que de changer d'espace à la main en ajoutant l'attribut x×yx \times y, on laisse le réseau construire lui-même l'espace où le problème devient facile. C'est là toute la différence, et c'est ce qui a rendu ces méthodes irremplaçables.

La rétropropagation

Passe avant, passe arrière

Avant : a=tanh(W1z+b1)a = \tanh(W_1 z + b_1) puis p=σ(W2a+b2)p = \sigma(W_2 \cdot a + b_2).

Arrière : partir de l'écart à la sortie, δ2=py\delta_2 = p - y, et le ramener derrière la couche cachée par la règle de dérivation des fonctions composées :

δ1=(δ2W2)(1a2)\delta_1 = (\delta_2 \, W_2) \odot (1 - a^2)

Les gradients s'en déduisent : W2=δ2a\nabla_{W_2} = \delta_2\, a, W1=δ1z\nabla_{W_1} = \delta_1 z^{\top}.

Le terme 1a21 - a^2 est la dérivée de tanh\tanh, exprimée à partir de sa propre valeur.

main.py
Sortie
>_ Prêt à exécuter…
Pourquoi ce nom, et pourquoi il intimide à tort
« Rétropropagation » désigne un simple ordre de calcul. Pour obtenir la dérivée de la perte par rapport à un poids de la première couche, il faut multiplier les dérivées locales de tout ce qui vient après. Les calculer de la sortie vers l'entrée permet de réutiliser à chaque étage ce qui a été calculé à l'étage précédent.
La différence de coût est décisive : par différence finie, obtenir le gradient d'un modèle à mm paramètres demande 2m2m passes avant. Par rétropropagation, une passe avant et une passe arrière suffisent, quel que soit mm.
Sur un modèle à un milliard de paramètres, le premier est impensable et le second est une routine. C'est cette seule différence qui rend l'apprentissage profond possible, et c'est pourquoi la découverte de 1986 a compté.

Combien de cellules, et l'initialisation

main.py
Sortie
>_ Prêt à exécuter…
02460246température (°C)charge du processeur
3000 pas12 / 28 erreursperte 0,3472

12 erreurs sur 28. La frontière se déforme encore : chaque cellule cachée tire dans sa direction, et la sortie combine leurs avis.

Deux cellules cachées, le minimum théorique pour ce problème, avec une initialisation malheureuse. La descente s'installe dans un creux dont elle ne sortira pas : douze erreurs sur vingt-huit après trois mille pas, et la frontière ne se referme jamais. Le même réseau à deux cellules y arrive sans peine avec d'autres départs. C'est le minimum local du chapitre 6, sous une nouvelle forme.
Trois raisons de ne pas viser le minimum de cellules
La fiabilité. Deux cellules suffisent en théorie et échouent une fois sur trois en pratique, faute d'une bonne initialisation. Trois y arrivent à tous les coups.
Le paysage. Ajouter des cellules ne rend pas la surface d'erreur plus lisse par magie, mais multiplie les chemins qui descendent. Un modèle un peu large trouve plus facilement une issue qu'un modèle exactement dimensionné.
La régularisation existe. Le chapitre 7 l'a montré : ce n'est pas le nombre de paramètres qui nuit à la généralisation, c'est leur liberté. Un réseau plus large et pénalisé se comporte mieux qu'un réseau étroit et laissé libre.
Le réflexe qui consiste à réduire la taille du modèle quand il sur-apprend n'est donc pas le premier à avoir. Pénaliser, ou collecter davantage, viennent avant.

Ce qu'une seule couche cachée peut faire

main.py
Sortie
>_ Prêt à exécuter…
Théorème d'approximation universelle

Une seule couche cachée, avec assez de cellules et une activation non polynomiale, approche n'importe quelle fonction continue sur un domaine borné, avec la précision que l'on veut.

Ce que ce théorème ne dit pas, et c'est l'essentiel
Il est souvent cité comme si toute la question était réglée. Il laisse pourtant ouvert tout ce qui compte.
Il ne dit pas combien de cellules. « Assez » peut vouloir dire un nombre astronomique, croissant exponentiellement avec la dimension d'entrée.
Il ne dit pas qu'on trouvera ces poids. Il affirme qu'ils existent. Rien ne garantit qu'une descente de gradient les atteigne, et la figure aux deux cellules montre qu'elle échoue déjà sur un damier.
Il ne dit rien de la généralisation. Approcher parfaitement la fonction sur les points connus est exactement ce que le chapitre 7 appelle apprendre le bruit.
Il ne justifie pas la profondeur. Il porte sur une seule couche cachée. Si la profondeur s'est imposée, c'est parce que certaines fonctions demandent exponentiellement moins de cellules quand on empile les couches, et parce que les couches successives apprennent des représentations réutilisables. Ce n'est pas une question de pouvoir d'expression, c'est une question d'économie.

Ce qui va mal en profondeur

Quatre difficultés, et leurs remèdes
Les gradients qui s'évanouissent. La rétropropagation multiplie les dérivées locales couche après couche. Avec une sigmoïde, dont la dérivée ne dépasse jamais 0,25, dix couches multiplient par au plus 0,25100{,}25^{10}, soit un millionième : les premières couches n'apprennent plus rien. Le remède principal a été de remplacer la sigmoïde par ReLU, max(0,z)\max(0, z), dont la dérivée vaut 1 sur toute la partie active.
La saturation. Un neurone dont l'entrée est très grande en valeur absolue a une dérivée quasi nulle et cesse d'apprendre. C'est la même raison qui, au chapitre 8, disqualifiait la perte carrée sur une sigmoïde.
L'initialisation. Des poids trop grands saturent d'emblée, des poids trop petits laissent le signal s'éteindre. Les recettes usuelles règlent l'écart-type initial en fonction du nombre d'entrées de chaque couche.
Le déséquilibre entre couches. La normalisation par lots et les connexions résiduelles, qui laissent le signal court-circuiter une couche, sont les deux réponses qui ont rendu les réseaux très profonds entraînables.
Ce que ferait une bibliothèque
import torch, torch.nn as nn

modele = nn.Sequential(nn.Linear(2, 4), nn.Tanh(), nn.Linear(4, 1))
perte, opt = nn.BCEWithLogitsLoss(), torch.optim.Adam(modele.parameters(), lr=0.05)

for _ in range(3000):
    opt.zero_grad()
    e = perte(modele(Z).squeeze(), y)
    e.backward()          # la retropropagation, ecrite a la main plus haut
    opt.step()

e.backward() fait exactement ce que le bloc de vérification a écrit en dix lignes, à ceci près qu'il le déduit tout seul de la suite des opérations. BCEWithLogitsLoss combine sigmoïde et perte logarithmique en un seul calcul, plus stable numériquement que les deux séparés. Et Adam est l'optimiseur décrit au chapitre 6.

Exercices type

Un réseau à dix couches, sans aucune activation, est-il plus puissant qu'une régression linéaire ?

Non, il lui est exactement équivalent.

La composition de deux applications linéaires est une application linéaire : W2(W1z+b1)+b2=(W2W1)z+(W2b1+b2)W_2(W_1 z + b_1) + b_2 = (W_2 W_1) z + (W_2 b_1 + b_2). Le raisonnement se répète pour dix couches comme pour deux.

Le chapitre le vérifie par le calcul plutôt que de l'affirmer : le réseau à huit cellules sans activation est réduit à sa droite équivalente, et cette droite commet exactement les mêmes erreurs.

Dix couches sans activation, c'est donc une régression linéaire avec beaucoup plus de paramètres, beaucoup plus de calcul, et pas un gramme de pouvoir en plus.

Qu'est-ce que la rétropropagation, en une phrase ?

La règle de dérivation des fonctions composées, appliquée de la sortie vers l'entrée pour réutiliser les calculs.

Pour obtenir la dérivée de la perte par rapport à un poids de la première couche, il faut multiplier les dérivées locales de tout ce qui vient après. Les calculer en partant de la fin permet de réutiliser à chaque étage le résultat de l'étage précédent.

L'intérêt est le coût. Par différence finie, un modèle à mm paramètres demande 2m2m passes avant. Par rétropropagation, une passe avant et une passe arrière suffisent, quel que soit mm.

Sur un milliard de paramètres, le premier est impensable et le second est une routine.

Un réseau apprend mal. Comment savoir si le gradient est faux ?

Par différence finie centrée, sur quelques poids pris au hasard.

Comparer E(θ+h)E(θh)2h\frac{E(\theta + h) - E(\theta - h)}{2h} avec la valeur rétropropagée, pour h=106h = 10^{-6}. Les deux doivent coïncider sur six ou sept décimales, comme le montre le bloc du chapitre.

C'est indispensable parce qu'un gradient faux ne lève aucune erreur. Il produit un apprentissage qui converge mal, ou vers autre chose, et l'on cherche alors le problème dans le pas, dans les données, dans l'architecture, partout sauf là où il est.

Quinze lignes de vérification épargnent des jours, et c'est pour cela que toute implémentation à la main commence par là.

Deux cellules cachées suffisent en théorie pour le damier. Faut-il s'en tenir là ?

Non. Le chapitre le mesure : avec deux cellules, une initialisation sur trois échoue et reste bloquée. Avec trois, les trois initialisations réussissent.

Trois raisons de garder de la marge. La fiabilité, puisque le minimum théorique est fragile. Le paysage d'erreur, où des cellules supplémentaires multiplient les chemins qui descendent. Et le fait que la régularisation existe : le chapitre 7 a montré que ce n'est pas le nombre de paramètres qui nuit à la généralisation, mais leur liberté.

Le réflexe de rétrécir le modèle dès qu'il sur-apprend n'est donc pas le bon premier geste. Pénaliser, ou collecter plus de données, viennent avant.

Le théorème d'approximation universelle règle-t-il la question de l'architecture ?

Non, il ne règle presque rien.

Il affirme qu'une couche cachée suffit à approcher toute fonction continue, avec assez de cellules. Il laisse ouvert : combien de cellules, un nombre qui peut croître exponentiellement avec la dimension ; comment les trouver, puisqu'il prouve une existence et non l'atteignabilité par descente de gradient ; et la généralisation, puisque approcher parfaitement les points connus est précisément ce que le chapitre 7 appelle apprendre le bruit.

Il ne justifie pas non plus la profondeur, dont il ne parle pas. Celle-ci s'est imposée pour une raison d'économie : certaines fonctions demandent exponentiellement moins de cellules quand on empile les couches.

Pourquoi les gradients s'évanouissent-ils en profondeur, et qu'y fait ReLU ?

Parce que la rétropropagation multiplie les dérivées locales couche après couche.

La dérivée de la sigmoïde ne dépasse jamais 0,25. Dix couches multiplient donc par au plus 0,25100{,}25^{10}, soit environ un millionième : le gradient qui atteint les premières couches est nul en pratique, et elles cessent d'apprendre.

ReLU, max(0,z)\max(0, z), a une dérivée qui vaut exactement 1 sur toute sa partie active. Les produits ne s'écrasent plus, et le signal traverse. C'est le changement qui a rendu les réseaux profonds entraînables.

Le prix est qu'un neurone poussé du mauvais côté a une dérivée nulle et peut ne jamais revenir. D'où les variantes qui laissent passer une petite pente négative.

Vérification rapideon peut se reprendre

1.Un neurone à activation logistique, c'est…

2.Empiler dix couches sans aucune activation donne…

3.Qu'est-ce que la rétropropagation ?

4.Comment vérifier qu'un gradient rétropropagé est juste ?

5.Le théorème d'approximation universelle garantit…

6.Pourquoi ReLU plutôt que la sigmoïde dans les couches cachées profondes ?

La méthode

  1. Standardiser les entrées, comme pour toute descente de gradient.
  2. Vérifier le gradient par différence finie avant tout apprentissage sérieux.
  3. Commencer petit, une couche cachée, quelques cellules, et voir si le problème cède.
  4. Garder de la marge sur le nombre de cellules plutôt que de viser le minimum théorique.
  5. Relancer depuis plusieurs initialisations et comparer, comme au chapitre 6.
  6. Employer ReLU dans les couches cachées dès qu'il y en a plusieurs.
  7. Pénaliser plutôt que rétrécir quand le modèle sur-apprend.
  8. Mesurer sur des données non vues, en se rappelant qu'un réseau atteint zéro erreur d'apprentissage sans difficulté.

Synthèse

  • Un neurone est la régression logistique du chapitre 8 : sa frontière est une droite.
  • Une seule cellule cachée reste donc au plancher de sept erreurs sur le damier.
  • Sans activation, empiler des couches donne une seule application linéaire, ce que le bloc vérifie en calculant la droite équivalente.
  • Plusieurs cellules construisent un espace où les classes deviennent séparables, au lieu de le fabriquer à la main.
  • La rétropropagation est la règle des composées appliquée de la sortie vers l'entrée ; son intérêt est le coût.
  • Une passe avant et une passe arrière suffisent, quel que soit le nombre de paramètres, là où la différence finie en demanderait 2m2m.
  • Un gradient faux ne lève aucune erreur ; il se vérifie par différence finie centrée.
  • Deux cellules suffisent en théorie et échouent une fois sur trois ; trois réussissent à tous les coups.
  • Le théorème d'approximation universelle prouve une existence, et ne dit ni combien, ni comment, ni si cela généralise.
  • La profondeur ne s'est pas imposée pour le pouvoir d'expression mais pour l'économie.
  • Les gradients s'évanouissent parce que la rétropropagation multiplie les dérivées ; ReLU est la réponse principale.