- Pour comprendre les réseaux de neurones, il n’est pas indispensable, contrairement aux idées reçues, de disposer d’un background mathématique très élevé. Le niveau du Lycée suffit. Si vous avez compris l’équation de la droite et la régression linéaire, alors vous comprendrez les réseaux de neurones.
C’est ce que cet article tente de démontrer.
Cet article s’appuie sur le cours (GCP) de Jeremy Howard @jeremyphoward, sur celui de Rachel Thomas @math_rachel et celui de la Khan Academy.
L’équation d’une droite est : y = ax + b
a est la pente de la droite (ou son coefficient directeur) et b est l’ordonnée à l’origine.
On va dans cet exercice, après avoir créé un nuage de points ayant approximativement la forme d’une droite, chercher les paramètres de la droite (a, b) qui approchent au mieux ce nuage de points par une droite des moindres carrés.
y = ax + b, on remplace a par a1 et b par a2 (a2=b), donc on obtient y = a1x + a2 (on sait que a2 = a2*1) donc si X est une matrice dont la 2ème colonne est à 1 (pour b) alors on peut écrire : [latex] y = a_{}1 x_{}1 + a_{}2 x_{}2 [/latex] et même ou plus généralement [latex] \vec{y} = X \vec{a} [/latex] (produit matriciel)
Quelques notions
Multiplication de matrices
Python a introduit l’opérateur @
dans la version 3.5. Cet opérateur permet de multiplier des matrices.
PEP 465 introduced the
https://alysivji.github.io/python-matrix-multiplication-operator.html@
infix operator that is designated to be used for matrix multiplication. The acceptance and implementation of this proposal in Python 3.5 was a signal to the scientific community that Python is taking its role as a numerical computation language very seriously
import numpy as np
A = np.matrix('3 1; 8 2')
B = np.matrix('6 1; 7 9')
A @ B
matrix([[25, 12],
[62, 26]])
Tenseur
n=100
x = torch.ones(n,2)
Returns a tensor filled with the scalar value 1, with the shape defined by the variable argument
https://pytorch.org/docs/stable/torch.htmlsizes
.
Tout d’abord on crée un tenseur de shape (forme) 2 (en clair, une matrice). Ce tenseur est initialisé à 1.
Ensuite, on remplace la colonne 1 par un nombre aléatoire compris entre -1 et 1.
x[:,0].uniform_(-1.,1)
Fills
https://pytorch.org/docs/stable/tensors.html#torch.Tensor.uniform_self
tensor with numbers sampled from the continuous uniform distribution:
x[:5]
tensor([[ 0.1205, 1.0000],
[ 0.1748, 1.0000],
[ 0.9000, 1.0000],
[-0.4400, 1.0000],
[ 0.6781, 1.0000]])
Bruit
Returns a tensor filled with random numbers from a uniform distribution on the interval [0,1]https://pytorch.org/docs/stable/torch.html?highlight=torch%20rand#torch.rand
Pour créer un vecteur de n (100) nombres aléatoires.
torch.rand(n)
Nuage de points
On crée le nuage de points par calcul matriciel. On initialise a (notre paramètre). C’est un vecteur de 2 lignes * 1 colonne. On a donc un vecteur [3, 2}
a = tensor(3.,2);
On fait le produit de la matrice de dimension (100,2) par le vecteur (2,1), ce qui nous donne un vecteur de dimension (100)
y = x@a + torch.rand(n)

Notre nuage a cette forme :
plt.scatter(x[:,0], y);

Gradient
On cherche la valeur des paramètres de la droite, c’est à dire les poids (a et b). Ici c’est plus exactement [latex]a_{1}[/latex] et [latex]a_{2}[/latex] (car il comprend b, il a 2 valeurs)
Nous allons chercher la valeur des paramètres par itération en comparant l’écart entre les résultats calculés et les valeurs connues. Pour cela, il nous faut une fonction de coût. On choisit comme fonction, la moyenne des carrés des écarts.
fonction de coût
def mse(y_hat, y): return ((y_hat-y)**2).mean()
Par exemple si :
a = tensor(-1.,1)
y_hat = x@a
mse(y_hat, y)
alors MSE (mean square error) =
tensor(6.2704)
graphiquement on a :
plt.scatter(x[:,0],y)
plt.scatter(x[:,0],y_hat);

calcul du gradient
On initialise a :
a = nn.Parameter(a)
On définit la fonction update() de la façon suivante :
def update():
y_hat = x@a
loss = mse(y, y_hat)
if t % 10 == 0: print(loss)
loss.backward()
with torch.no_grad():
a.sub_(lr * a.grad)
a.grad.zero_()
- tout d’abord on calcule y_hat (la droite avec les nouveaux paramètres)
- ensuite on calcule l’erreur (la perte) loss
- on affiche cette perte toutes les 10 fois
- on calcule le gradient, c’est ce que permet backward()
- puis on on remplace a par sa valeur réduire du gradient multiplié par le learning rate
- et on remet à zero le gradient
gradient : Computes the gradient of current tensor
https://pytorch.org/docs/stable/autograd.html?highlight=backward#torch.Tensor.backward
no.grad() Context-manager that disabled gradient calculation.
https://pytorch.org/docs/stable/autograd.html?highlight=no_grad#torch.autograd.no_grad
L’instruction
https://docs.python.org/fr/3/reference/compound_stmts.html#the-with-statementwith
est utilisée pour encapsuler l’exécution d’un bloc avec des méthodes définies par un gestionnaire de contexte
La fonction étant définie, il ne reste qu’à boucler :
lr = 1e-1
for t in range(100): update()
résultat
plt.scatter(x[:,0],y)
plt.scatter(x[:,0],x@a);

Animation
L’objet n’est pa de décrire ici matplotlib. Les informations sont minimales.
from matplotlib import animation, rc
rc('animation', html='jshtml')
On initialise a, on définit la figure, on affiche le nuage de points, et la première droite.
a = nn.Parameter(tensor(-1.,1))
fig = plt.figure()
plt.scatter(x[:,0], y, c='orange')
line, = plt.plot(x[:,0], x@a)
plt.close()
puis on itère
def animate(i):
update()
line.set_ydata(x@a)
return line,
animation.FuncAnimation(fig, animate, np.arange(0, 100), interval=20)
Résumé
Si vous avez compris, la descente du gradient, alors sachez qu’un réseau de neurones fonctionne de la même façon. On compare le résultat calculé avec celui qu’il devrait être grâce à une fonction de coût, on cherche de nouveaux paramètres qui minimise la fonction de coût, avec le calcul du gradient, puis on itère.
On a les étapes suivantes :
- intialisation des paramètres de notre modèle (ici une droite, a et b sont les paramètres)
- calcul des valeurs y_hat avec ces paramètres
- calcul du coût (erreur)
- minimisation de la fonction de coût, calcul du gradient
- mise à jour des paramètres a.sub_(lr * a.grad) en soustrayant le gradient multiplié par le learning rate (pas d’apprentissage)
- itération