Ce travail est dédié à la mise en place d'un premier réseau de neurones et la compréhension des concepts mis en jeu.
Prerequisite
This practical work relies on the Python language, Pytorch and the MatPlotLib library.
Matplotlib setup
The MatPlotLib library can be installed with the command:
pip install matplotlib
or for conda installation:
conda install -c conda-forge matplotlib
The MatPlotLib library displays geometric rendering in an independent interactive window. Depending on the Python environment, this window may be rendered as a frozen image, preventing user interaction. To correct this problem, here are a few solutions:
PyCharm
Go to Settings / Tool / Python Plot and uncheck the option Show plots in tool
windows.
Spyder
Go to Tools / Preferences / IPython console / Graphics / Backend:Inline and
change "Inline" to "Automatic". Click OK button and restart the IDE.
Jupyter Notebook
Execute following code within the Jupyter Notebook:
%matplotlib qt
Pytorch setup
The Pytorch installation can be done by following the instructions here according to your configuration.
A simple Neural Network (NN)
Un réseau de neurones permet d'engendrer une famille de fonctions paramétrées par les valeurs des coefficients du réseau (ou poids
synaptiques). L'objectif de la phase d'apprentissage des réseaux de neurones est de trouver, parmi toutes ces fonctions, celle qui s'approche le plus possible de la régression (fonction génératrice des exemples).
Dans le cas d'un réseau à 1 couche, la famille de fonction engendrée est de type affine.
Avant d'aller plus loin, il est conseillé de (re)découvrir les réseaux de neurones grâce à la lecture suivante: https://www.miximum.fr/blog/introduction-au-deep-learning-1/
Exercice 1
Nous considérons un réseau simple, prenant un scalaire $x$ en entrée, et renvoyant un autre scalaire $y$ en sortie. Nous voulons apprendre au réseau à simuler la fonction affine $y=3x+2$.
Commençons par faire tourner le code d'entrainement du réseau de neurones ci-dessous:
import torch
dtype = torch.float
device = torch.device("cpu")
# N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 1000, 1, 10, 1
# Create random input and output data
x = torch.randn(N, D_in, device=device, dtype=dtype)
y = 3*x+2
# 0) Randomly initialize weights
w1 = torch.randn(D_in, H, device=device, dtype=dtype)
b = torch.randn(1, H, device=device, dtype=dtype)
w2 = torch.randn(H, D_out, device=device, dtype=dtype)
learning_rate = 1e-5
for t in range(10001):
# 1) Forward pass: compute predicted y
z = x.mm(w1)+b.repeat(N,1)
z_relu = z.clamp(min=0)
y_pred = z_relu.mm(w2)
# 2) Compute and print loss
loss = (y_pred - y).pow(2).sum().item()
if t % 500 == 0:
print(t, loss)
# 3) Backpropagate to compute gradients of w1, w2 and b with respect to loss
grad_y_pred = 2.0 * (y_pred - y)
grad_w2 = z_relu.t().mm(grad_y_pred)
grad_z_relu = grad_y_pred.mm(w2.t())
grad_z = grad_z_relu.clone()
grad_z[z < 0] = 0
grad_w1 = x.t().mm(grad_z)
grad_b = grad_z.sum(0)
# 4) Update weights using gradient descent
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w2
b -= learning_rate * grad_b
Il imprime à l'écran la valeur moyenne de la fonction de coût sur un batch de points d'entrée. Faites tourner le code plusieurs fois pour observer la convergence du réseau. Que remarquez vous ?
Lorsque vous êtes satisfaits de votre réseau, ajouter la portion de code suivante au premier programme:
# 5) Examining the learnt function
M = 20
x_test = torch.linspace(0,1,M).unsqueeze(1)
x_test = x_test.repeat(1,D_in)
z_test = x_test.mm(w1)+b.repeat(M,1)
z_relu_test = z_test.clamp(min=0)
y_pred_test = z_relu_test.mm(w2)
plt.plot(list(x[:,0]), list(y_pred[:,0]))
plt.show()
y_test = 3*x_test + 2
print(torch.cat((x_test,y_test,y_pred_test), 1))
pour que l'affichage fonctionne, il faut ajouter l'appel à la librairie matplotlib au début du programme:
import matplotlib.pyplot as plt
Quelle conclusion peut-on tirer à partir de l'affichage ?
Pour parvenir à approximer la fonction souhaitée, il faut définir une fonction de coût qui mesure l'écart entre la sortie du modèle (fonction réalisée par le réseau de neurones) et la sortie désirée. La fonction de coût est une fonction scalaire qui dépend du vecteur de paramètres (noté $\theta$) du modèle, et des individus de l'ensemble d'apprentissage. Dans le cas des réseaux de neurones, le vecteur de paramètres est constitué par les poids du réseau. Plus la valeur de la fonction de coût est petite, plus le modèle reproduit fidèlement les observations utilisées pour l'apprentissage. Les différents algorithmes d'apprentissage cherchent donc à trouver le point, dans l'espace des paramètres, pour lequel la fonction de coût est minimale
Exercice 2
Quelle est la fonction de cout utilisée par le programme de l'exercice 1 ?
Exercice 3
A partir du code ci-dessus de l'exercice 1, dessinez sur papier la structure de notre réseau de neurones, et écrivez les équations associées pour une entrée $x$ (scalaire simple).
Aide: Si les 'tableaux' ou 'tenseurs' de PyTorch vous semblent déroutants, vous pouvez vous référer à cette introduction : https://pytorch.org/tutorials/beginner/blitz/tensor_tutorial.html#sphx-glr-beginner-blitz-tensor-tutorial-py
Exercice 4
Comment sont organisés les coefficients dans les matrices $W^1$, $W^2$ et $B$ ?
Vérifiez que pour un batch d'entrée multiples $X=\begin{bmatrix}x_0 \\ \vdots \\ x_N\end{bmatrix}$ les équations de l'étape 1 renvoient bien un vecteur de sorties $Y=\begin{bmatrix}y_0 \\ \vdots \\ y_N\end{bmatrix}$. Faites attention à la dimension des tableaux pour vous convaincre que les équations de feed forward sont bien respectées.
Exercice 5
Calculez (à la main) les équations de back propagation pour notre réseau de neurones, dans le cas d'un batch de 1000 entrées/sorties.
Aide: Vous pouvez trouver de la documentation à l'adresse https://www.miximum.fr/blog/introduction-au-deep-learning-2/
Exercice 6
Vérifiez que les étapes 3 et 4 du programme suivent bien les équations obtenues en exercice 4. Faites toujours attention aux dimensions des tableaux. Remarquez comme le calcul des gradients en pratique se fait progressivement : à chaque étape du feed forward est associée un calcul de gradient.
Exercice 7
Reprendre le code de l'exercice 1 et le modifier (nombre de neurones, d'entrées par batch, dimension de l'entrée (individuelle) x, nombre d'itérations, vitesse d'apprentissage...) pour essayer de lui faire apprendre une autre fonction et voir l'impact de changement de paramètres dans la qualité ou la vitesse d'apprentissage.