Programmation pour l’IA
Florent Capelli
Université d’Artois, CRIL
Plusieurs types de fonctions d’activation :
On fait une hypothèse ici : \(e_R(\w;(\x,y))\) dépend de seulement de la valeur du réseau sur \(\x\) et de \(y\). C’est-à-dire :
\[ e_R(\w;(\x,y)) = e(R(\w,\x), y)\]
Par exemple : erreur quadratique \(e(y_0,y) = (y_0-y)^2\) et \(e_R(\w;(\x,y)) = e(R(\w;\x), y)\).
En posant \(z = R(\w;\x)\) : \[ \dpartial{e_R}{w} (\w; (\x,y)) = \dpartial{e}{w}(z,y) = \dpartial{e}{z}(z,y) \dpartial{z}{w}(\w) + \dpartial{e}{y}\dpartial{y}{w} \]
donc
\[\Delta_\w e_R = \dpartial{e}{z}(z,y) \times \Delta_\w r\]
Réseau de neurones = couches successives de neurones ayant tous la même fonction d’activation.
Tous les neurones d’une couche sont connectés à ceux de la couche suivantes.
On note \(w^i_{u,v}\) le poids entre de la sortie du neurone \(v\) de la couche \(i\) vers le neurone \(u\) de la couche \(i+1\) et \(\W^i\) la matrix associée. \(\W^i\) a une ligne par neurones de la couche \(i+1\) et une colonne par neurones de la couche \(i\).
On note \(b^i_j\) le bias du \(j\)ème neurone de la couche \(i\), \(\b^i\) le vecteur colonne associé.
La couche \(1\) sort \(ReLU(\b^0 + \W^0 \times \begin{pmatrix} x_1 \\ x_2 \\ x_3 \end{pmatrix}) \in \R^4\) (\(\W^0\) est une matrice \(4 \times 3\)).
\(\x\)
\(\b^0 + \W^0 \times \x\)
\(ReLU(\b^0 + \W^0 \times \x)\)
\(\b^1 + \W^1 \times ReLU(\W^0 \times \x)\)
\(\sigma(\b^1 + \W^1 \times ReLU(\b^0 + \W^0 \times \x))\)
\(\b^2 + \W^2 \times \sigma(\b^1 + \W^1 \times ReLU(\b^0 + \W^0 \times \x))\)
\(ReLU(\b^2 + \W^2 \times \sigma(\b^1 + \W^1 \times ReLU(\b^0 + \W^0 \times \x)))\)
\(\b^3 + \W^3 \times ReLU(\b^2 + \W^2 \times \sigma(\b^1 + \W^1 \times ReLU(\b^0 + \W^0 \times \x)))\)
\(\sigma(\b^3 + \W^3 \times ReLU(\b^2 + \W^2 \times \sigma(\b^1 + \W^1 \times ReLU(\b^0 + \W^0 \times \x))))\)
\[R(\W;\x) = f_L(\b^L+\W^{L} \times f_{L-1} (\b^{L-1}+\W^{L-1} \dots f_0(\b^0+\W^0 \x))\]
Implémentation vectorisée, parallélisable sur un GPU.
Si on veut évaluer un minibatch \(\X\) donc les colonnes sont les différents éléments du jeu de données, on a :
\[R(\W; \X) := f_L(\b^L+\W^{L} \times f_{L-1} (\b^{L-1}+\W^{L-1} \dots f_0(\b^0+\W^0 \X))\]
est un vecteur colonne dont la ligne \(i\) est l’évaluation de \(R\) sur la colonne \(i\) de \(\X\).
But :
L’enjeu est de calculer \(\dpartial{e}{\W^k}\) et \(\dpartial{e}{\b^k}\) en fonction de \(\dpartial{e}{\W^{k+1}}\) et \(\dpartial{e}{\b^{k+1}}\) !
Erreur de la forme \(e_R(\W; (\x,y)) = e(f_L(\W^{L} \times f_{L-1} (\W^{L-1} \dots f_0(\W^0 \x)), y) = e(a^L,y)\).
On note et précalcule (en évaluant le réseau, forward pass) :
On peut prouver en appliquant la dérivation de composition :
\[\Delta \W^i = \delta^{i} (a^{i})^T \text{ et } \Delta \b^i = \delta^{i}\]
où \(\delta^i = \begin{cases} \dpartial{e}{a^{L+1}}(a^{L+1},y) \times f_L'(z^L) \text{ if } i=L \\ ((\W^{i+1})^T \delta^{i+1}) \odot f'_{i}(z^{i}) \text{ if } i<L. \end{cases}\)
où \(\odot\) dénote le produit d’Hadamard (produit coordonnées par coordonnées).
On voit qu’on peut calculer les \(\Delta \W^i\) après :
Calcul vectorisable sur CPU et parallélisable sur GPU.
Bibliothèque pour :
Objets de base de Pytorch : les tenseurs,
torch.tensor.
numpy :
t.to_numpy() pour envoyer un tensor vers
numpytorch.from_numpy(t) ou tensor(t) depuis
numpyautograd)Opération sur les tenseurs peuvent stocker des informations sur le gradient :
Utilise des règles automatiques de calcul de différentielles pour calculer le gradient. Stocké dans les paramètres.
x = torch.empty(100).uniform_(-20,20)
y = 3*x+2+torch.normal(mean=0., std=3., size=(100,))
a = torch.tensor([0.], requires_grad=True)
b = torch.tensor([0.], requires_grad=True)
for e in range(1000):
loss = torch.mean((a*x+b-y)**2)
loss.backward()
with torch.no_grad():
a -= (1e-3)*a.grad
b -= (1e-3)*b.grad
a.grad_zero_()
b.grad_zero_()a et b comme des tenseurs avec
information de gradientloss, en le calculant, maintient une trace du
calcul.loss.backward() propage le gradient dans l’arbre de
calcul.import torch
from torch import nn
m = nn.Sequential(nn.Linear(2,3), nn.ReLU(),
nn.Linear(3,1), nn.Sigmoid())
x = torch.tensor([1., 2.])
y = 1
loss = (m(x)-y)**2
loss.backward()
with torch.no_grad():
for f in m.parameters():
f -= f.grad*0.01
f.grad.zero_()m expose automtatiquement ses paramètres entraînables
dans m.parameters()Exploration plus avancée de Pytorch