Entrainement d'un classifieur binaire sur MNIST¶
Quelques rappels¶
Nous voulons entraîner un réseau de neuronnes à couches denses (ou multi-layer perceptron (MLP)) dont le but est de classifier des nombres écrits à la main suivant q'ils sont plus grand ou plus petit que $4$.
Nous prenons pour fonction d'activation $\sigma(x) = x\mathbf{1}_{x\geq 0}$, et appliquons la fonction softmax en sortie de la dernière couche, à la place de $\sigma$. La fonction de coût considérée est l'entropie croisée $$c(\hat y,y) = - \log(\hat y_{i_y})$$ où $y$ est l'étiquette, de la forme $(0,0, \cdots,0,1,0,\cdots,0)$ d'une data $x$, et où $i_y$ est l'indice de la valeur $1$. Nous fixerons la dimension des couches à $(784,500,75,2)$. On rappelle la formule du mode forward (évaluation) d'un tel MLP :
$$ z_0 = x $$ $$ a^{(i)} = W^{(i)}z^{(i-1)}+b^{(i)}, ~~ \forall i \in \{1,2,3 \}$$ $$z^{(i)} = \sigma(a^{(i)}), ~~ \forall i \in \{1,2 \}$$ $$ \hat y = \mathrm{softmax}(a^{(3)})$$ $$ \text{coût} = c(\hat y,y)$$
Nous avons donc : $z^{(0)}\in \R^{784}$, $a^{(1)} \in \R^500$, $a^{(2)} \in \R^{75}$ et $a^{(3)} \in \R^2$.
Pour $\theta := (W^{(1)}, b^{(1)}, W^{(2)}, b^{(2)}, W^{(3))}, b^{(3)})$, on note $g_\theta(x)$ la quantité $\hat y$ obtenue par ce calcul direct. C'est le vecteur de prédiction de $x$, représentant un probabilité la probabilité de chaque label pour cette image.
Par "entraîner le réseau", on entend minimiser la fonction $$\phi : \theta \mapsto \mathbb{E}_{x,y \sim \mathbb{P}}\left [ c(g_\theta(x),y) \right ]$$ où $\mathbb P$ est la distribution des données. Nous appliquons pour cela l'algorithme de descente de gradient stochastique à passage unique:
- Cela signifie que pour le calcul de $\theta_{k+1}$, la direction $\nabla_\theta c(g_{\theta_k}(x_k),y_k)$ est considérée comme une bonne approximation de $\nabla_\theta \phi(\theta_k)$ en moyenne.
- Ici le couple $(x_k,y_k)$ désigne une donnée d'entrainement prise au hasard dans
X_train$\times$Y_train
Nous devons donc être capable d'écrire le mode backward du graphe computationel de la fonction $\theta \mapsto c(g_{\theta_k}(x),y)$ à donnée $(x,y)$ fixée.
Remarque : Pour se simplifier la vie ici :
- on ne remélange pas les données entre deux epochs
- on prend un pas constant pour le gradient
Questions préliminaires :¶
- Quelles sont les dimensions des matrices $W^{(i)}$ ? (Nombre de ligne et de colonnes)
- Calculer sur papier la Jacobienne de la fonction softmax.
- En déduire la formule backward sur l'étape softmax de notre classifieur (i.e. de l'étape $a^{(3)} \mapsto \hat y$)
- Partant du noeud $\delta a^{(3)}$, calculer $\delta z^{(2)}$, $\delta b^{(3)}$, $\delta W^{(3)}$.
- En déduire le mode backward du graphe computationnel de la fonction coût.
! pip install keras
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from keras.datasets import mnist
# Chargement de MNIST
(X_train,Y_train),(X_test,Y_test) = mnist.load_data()
# Affichage d'une donnée de MNIST
plt.figure()
plt.imshow(X_train[27],cmap = "binary")
plt.colorbar()
print("Label_27 = ",Y_train[27])
## Transformer les images en vecteurs colonnes
(X_train,Y_train),(X_test,Y_test) = mnist.load_data()
dim_input = X_train.shape[1]*X_train.shape[2]
X_train = X_train.reshape((X_train.shape[0],dim_input))
X_test = X_test.reshape((X_test.shape[0],dim_input))
X_train = X_train/255
X_test = X_test/255
## Shuffle des données
np.random.seed(238)
nbr_training_data = X_train.shape[0]
shuffle_index = np.random.permutation(nbr_training_data)
X_train, Y_train = X_train[shuffle_index], Y_train[shuffle_index]
plt.imshow(X_train[27].reshape(28,28),cmap = "binary")
print("Label_27 = ",Y_train[27])
Entrainement¶
dimensions = [784,500,75,2]
rate = 10**(-3)
nbr_epoch = 3
width = 0.1 #width of first initialization
## Definition de la fonction softmax
# def softmax(x):
## Initialisation : choisir des poids de l'ordre de grandeur de "width"
# W1 = ...
## Training with single pass SGD
k=0
print(nbr_training_data*nbr_epoch)
while k < nbr_training_data*nbr_epoch :
# récupérer la donnée/label et adapter le label à notre classifieur binaire
# créer une étiquette adaptée à la classification voulue
# Calcul du mode forward : ATTENTION A LA SHAPE de array. Utiliser np.matmul...
# Calcul du mode backward : Encore plus ATTENTION
# Mise à jour des coefficients par la descente de gradient stochastique
W3 = W3 - rate*dW3
b3 = b3 - rate*db3
W2 = W2 - rate*dW2
b2 = b2-rate*db2
W1 = W1 - rate*dW1
b1 = b1 - rate*db1
k = k +1
# Print de la progression..
## Test du classifieur
#definition de la fonction de classification
def classification(X):
#return np.argmax(z3)
# Calcul du score moyen sur les données de test
size_test = X_test.shape[0]
success_rate = 0
for test in range(1,size_test):
#if .....:
#success_rate += 1/size_test
print("success rate = ", success_rate)