← Retour

Introduction à la rétropropagation

Je vais supposer qu'il est connu le fonctionnement de base d'un réseau de neuronne c'est-à-dire non seulement la représentation matricielle de ses poids et biais, la notation de fonction d'activation et également l'algorithme d'évalutation. Je vais toute de même profiter de l'introduction des notation pour rappeler le fonctionnement de l'évaluation d'un réseau de neuronnes.

Rappels

On note $L$ le nombre de couches, donc 1 couche d'entrée, $L-1$ couches cachées et 1 couche de sorties. On note $n_0, \dots, n_L \in \mathbb{N}$ leurs tailles. On note $W^1, \dots, W^L$ les matrices des poids. $W^l \in \mathcal{M}_{n_{l}, n_{l-1}}(\mathbb{R})$ représente les poids entre la couche $l-1$ et $l$. À chaque étape de l'évaluation, on note $$ z^l = W^l a^{l-1} \in \mathbb{R}^{n_l} $$ Avec $a^{0}=x \in \mathbb{R}^{n_0}$ l'entrée. On note $f^1, \dots, f^L$ tel que $f^l:\mathbb{R}^{n_l} \rightarrow \mathbb{R}^{n_l}$ les fonctions d'activation si bien que $$ a^l = f^l(z^l) \in \mathbb{R}^{n_{l}} $$

Avec ces notations, $a^{l-1}$ représente l'entrée de la couche $l$ et $a^l$ sa sortie. On notera $$ h(x) = a^L = f^L(W^Lf^{L-1}(W^{L-1} \cdots f^1(W^1 x) \cdots )) $$

Convention sur les biais. Pour ne pas alourdir les notations, je n'introduis pas explicitement de biais $b^l \in \mathbb{R}^{n_l}$ pour chaque couche mais convient que chaque couche d'indice $l < L$ contient une neuronne de biais dont la valeur est forcée, c'est à dire que $a_0^l=1$. Ainsi, la première colonne de $W^{l+1}$ contient les biais de la couche $l$ puisque $$ z_i^l = \sum_{k=0}^{n_{l-1}} w^l_{ik} a_k^l = w^l_{i0} + \sum_{k=1}^{n_{l-1}} w^l_{ik} a_k^l = b_i^l + \sum_{k=1}^{n_{l-1}} w^l_{ik} a_k^l $$ Il est donc bon à prendre en compte que pour avoir par exemple 3 neurones pratiques sur une couche, il faut en réalité prendre $n_l=4$

Pour l'entrainement, on considère connu un ensemble $\{(x_i, y_i), 0 \leq i \leq N-1\}$ d'entrainement. La qualité d'une sortie sera alors calculée via une fonction de coût $C(y_i, h(x_i))$.

La backpropagation répond alors à la question suivante: comment en fonction de l'erreur commise lors d'une évaluation, modifier les poids de manière à minimiser l'erreur qui sera commise lors de l'évaluation suivante.

La retropropagation

On aimerait calculer $\nabla_{W^l} C$ pour mettre à jour les poids de la couche $l$, le problème étant que $C$ dépend de $W^l$ via la chaine $z^l \rightarrow a^l \rightarrow z^{l+1} \rightarrow \dots \rightarrow a^L \rightarrow C$. C'est là que la magie de la rétropropagation intervient.

$$ \begin{align*} \frac{dC}{dx} &= \frac{dC}{da^L} \cdot \frac{da^L}{dz^L} \cdot \frac{dz^L}{da^{L-1}} \cdots \underbrace{\;\frac{da^l}{dz^l}}_{=(f^l)'} \cdot \underbrace{\;\frac{dz^l}{da^{l-1}}\;}_{W^l} \cdots \frac{da^1}{dz^1} \cdot \frac{dz^1}{dx}\\ &= \frac{dC}{da^L} \cdot (f^L)' \cdot W^L \dots \; (f^l)' \cdot W^l \;\dots\; (f^1)' \cdot W^1 \end{align*} $$

Juste pour être clair sur la notation, $(f^l)'$ désigne bien $\operatorname{Jac}_{f^l}(z^l)$. Ainsi, $(f^l)'$ n'est donc dans la suite pas une fonction à valeurs matricielle mais bien une constante. Cette constante est calculée et mémorisée lors du fordward pass.

Par ailleurs, puisque $C$ est à valeurs dans $\mathbb{R}$, on a: $ \nabla_x C = (d_xC)^\top$. Remarquons également que puisque l'application de la fonction d'activation à une couche du réseau se fait composantes par composantes c'est à dire que $a_i^l = f^l(z_i^l)$. Alors $\operatorname{Jac}_{f^l}(z^l)$ est diagonale. Ainsi: $$ \nabla_x C = (W^1)^\top \cdot (f^1)' \; \dots \; (W^l)^\top \cdot (f^l)' \; \dots \; (W^L)^\top \cdot (f^L)' \cdot \nabla_{a^L} C $$

Vous aurez peut être remarqué que à priori le calcul de $\nabla_x C$ n'a pas de lien direct avec notre problème. En effet, ce que l'on aurait aimé faire c'est $W^l \leftarrow W^l - \eta \nabla_{W^l}C$, ce qui fait intervenir le calcul de $\nabla_{W^l} C$ et pas $\nabla_x C$

Ce qu'il convient de bien avoir en tête dorénavent, c'est que chaque couche conçois la couche précedente comme une entrée. Pour la première couché, l'entrée est bien $x$, mais pour une couche $l>1$ quelconque, l'entrée est $a^{l-1}$. De manière informelle:

$$ \underbrace{(W^1, \dots, W^L)}_{\text{réseau complet}}(x) = \underbrace{(W^l, \dots, W^L)}_{\text{sous réseau}}(a^{l-1}) $$ On pose alors $ \delta^l = \nabla_{z^l}C= (f^l)' \cdot (W^{l+1})^\top \; \dots \; (W^L)^\top \cdot (f^L)' \cdot \nabla_{a^L} C $ également défini de manière récursive par: $$ \begin{cases} \delta^{l-1} = (f^{l-1})' \cdot (W^{l})^\top \cdot \delta^{l} \quad\text{pour}\; l>1 \\ \delta^L = (f^L)' \cdot \nabla_{a^L} C \end{cases} $$ Donc $\delta^l$ correspond à la variation de la fonction de coût en fonction de la sortie de la couche $l$ avant le passage par la fonction d'activation. Maintenant, nous allons utiliser la relation qui permet de relier la sortie de la couche $l$ aux poids de $W^l$. En effet, $$ \begin{align*} z^l = W^l a^{l-1} \quad\text{donc}\quad& z_i^l = \sum_{k=1}^{n_{l-1}} w_{ik}^l a_k^{l-1} \\ \text{donc}\quad& \frac{\partial z_i^l}{\partial w_{ij}^l} = a_j^{l-1} \\ \text{donc}\quad& \frac{\partial C}{\partial w_{ij}^l} = \frac{\partial C}{\partial z_i^l} a_j^{l-1} \;\text{(règle de la chaine)}\\ \text{donc}\quad& \boxed{\nabla_{W^l} C = \delta^l \cdot (a^{l-1})^\top} \end{align*} $$ Et maintenant on comprend enfin le nom de backpropagation. car on va commencer par calculer $\delta^L$ ce qui va donner $\nabla_{W^L} C$ puis on va propager ce calcul via la définition récursive de $\delta^{L-1}$, etc.

Aussi, puisque comme l'a déjà remarqué, $(f^{l-1})'$ est diagonal, la multiplication matricielle dans $\delta^l$ n'en est pas vraiment une, puisque l'on peut écrire plus simplement que $$ [(f^{l-1})' \cdot v]_{i} = (f^l)'(z_i^l) \,v_i= \left(\operatorname{diag}\left[(f^l)'\right] \odot v\right)_i $$ Où $\operatorname{diag}$ est l'opérateur qui retourne un vecteur des éléments diagonaux d'une matrice. Ainsi en considérant dorénavants les matrices $(f^l)'$ comme des vecteurs, on obtient la formule suivante, qui permet de réduire grandement la complexité du calcul de $\delta^l$ $$ \begin{cases} \delta^{l-1} = (f^{l-1})' \odot \left[ (W^l)^\top \cdot \delta^l \right] \\ \delta^L = (f^L)' \odot \nabla_{a^L}C \end{cases} $$ où $(\odot)$ désigne le produit de Hadamard.

Exemple

Passons maintenant à un exemple, pas des plus originaux, la reconnaissance de chiffre. On a un réseau qui possède une image $16 \times 16 = 256$ valeurs de nuance de gris donc entre $0$ et $1$ et $9$ sorties $\hat{y}_1, \dots, \hat{y}_9$ telle que $\hat{y}_k$ est la probabilité que l'entrée soit le nombre $k$

On va choisir $L=3$, $f^1=f^2=\tan^{-1}$ puis pour $f^3$ la fonction softmax. La fonction softmax est une fonction $\sigma: \mathbb{R}^{n_L} \rightarrow [0, 1]^{n_L}$ telle que pour tout $x \in \mathbb{R}^{n_L}$, $\sum_{i}\sigma_i(x)=1$. Concrètement la fonction softmax permet de convertir un vecteur en une probabilité. C'est exactement ce que l'on veut pour la sortie de notre réseau. On a: $$ \sigma(x)_i = \frac{e^{x_i}}{\sum_{j=1}^{n_L} e^{x_j}} $$

Pour la fonction de coût, on utilisera l'entropie croisée. En notant $\hat{y}$ la sortie obtenue et $y$ la sortie attendue, c'est à dire $y=(0, 1, 0, \dots, 0)$ pour le nombre 2 par exemple, on a: $$ C(\hat{y}, y) = -\sum_{i=1}^{n_L} y_i \log \hat{y}_i $$ Pour composition particulière (softmax+entropie croisée), on trouve une formule pour $\delta^L$ très agréable. Démontrons la. Remarquons dans un premier temps que: $$ C(\hat{y}, y) = C(a^L, y) = C(\sigma(z^L), y) = C(\underbrace{\sigma_1(z^L), \dots, \sigma_{n_L}(z^L)}_{\text{tous dépendent de } z^L_i}, y) $$ Ainsi, par la règle de chaine $$ \begin{align*} \frac{\partial C}{\partial z_i^L}(a^L, y) &= \sum_{j=1}^{n_L} \frac{\partial a_j^L}{\partial z_i^L}(z^L) \frac{\partial C}{\partial a_j^L}(\sigma(z^L), y) \\ &= \sum_{j=1}^{n_L} \sigma_i(z^L)\left(\delta_{ij} - \sigma_j(z^L) \right) \left( \frac{y_j}{a^L_j}\right) \\ &= y_i - a_i^L \underbrace{\sum_{j=1}^{n_L}y_j}_{=1} \end{align*} $$ Ainsi, on a tout simplemenent $\delta^L = \nabla z^L C = y - a^L = y - \hat{y}$

Démonstration interactive

Dessinez un chiffre (0–9) dans le cadre ci-dessous. Le réseau prédit en temps réel la probabilité de chaque classe.

Chargement des poids…

Ce que voit le réseau :