DEV Community

Cover image for Reconnaissance de chiffres manuscrits avec un Réseau de Neurones
mohamed ait sidi hou
mohamed ait sidi hou

Posted on

Reconnaissance de chiffres manuscrits avec un Réseau de Neurones

Introduction à la BDD MNIST

La base MNIST contient des chiffres manuscrits utilisés pour tester des algorithmes de classification d'images. Chaque image de la base MNIST est représentée par une matrice de dimension 8 x 8

XR8×8X \in \mathbb{R}^{8 \times 8}

Chaque élément XijX_{ij} de cette matrice correspond à un pixel de l'image, dont :
la valeur de gris[0,255]\text{la valeur de gris} \in [0, 255]

L'objectif principal est de reconnaître le chiffre YY à partir de cette image XX
Outils utilisés :
  • python
  • Descente de gradient (batch et stochastique) pour estimer les paramètres
  • Keras pour la régression multinomiale
  • Un réseau de neurones convolutionnel basé sur LeNet-5

1. MNIST DATA

Importation des librairies

import numpy as np  
import pandas as pd  
import matplotlib.pyplot as plt  
import random, time  
from keras.datasets import mnist  
Enter fullscreen mode Exit fullscreen mode

La BDD se télécharge via Keras comme

(X0, Y0), (X1, Y1) = mnist.load_data()
Enter fullscreen mode Exit fullscreen mode

La BDD MNIST contient 60 000 images d'apprentissage et 10 000 de test.
X0 et Y0 représentent les images d'apprentissage et de test sous forme de numpy.ndarray de taille (60000, 28, 28) et (10000, 28, 28).

On prépare la DATA comme

# Conversion en float
X0 = X0.astype('float32')
X1 = X1.astype('float32')
# Image -> Vecteur
X0 = X0.reshape(60000, 784)
X1 = X1.reshape(10000, 784) 
# Mise à l'échelle
X0 = X0 / 255.0
X1 = X1 / 255.0
Enter fullscreen mode Exit fullscreen mode

On affiche une image comme

# indice = 3
n = np.where(Y0 == 3)[0][0]

# Affichage de l'image
plt.imshow(X0[n, :].reshape(28, 28), cmap="gray")
Enter fullscreen mode Exit fullscreen mode

indice = 3

2. Régression multinomiale

La régression multinomiale attribue une classe à un individu via une variable aléatoire ( Y ) avec des étiquettes de classe. Pour chaque individu, on utilise un vecteur de régression ( x ), et la probabilité d'appartenance à une classe ( k ) est donnée par :

k1,,K \forall k \in {1, \dots, K}
P(Y=k)=exp(θ(k)x~)m=1Kexp(θ(m)x~),avecx~=(1 x) \mathbb{P}(Y = k) = \frac{\exp(\theta^{(k)} \cdot \tilde{x})}{\sum_{m=1}^K \exp(\theta^{(m)} \cdot \tilde{x})}, \quad \text{avec} \quad \tilde{x} = \begin{pmatrix} 1 \ x \end{pmatrix}

3. Descente de gradient

La descente de gradient minimise S(θ)S(\theta) en mettant à jour θ\theta :

θθρθS(θ) \theta \leftarrow \theta - \rho \nabla_\theta S(\theta)

ρ>0\rho > 0 est le pas fixe et θS\nabla_\theta S est le gradient de SS .

Le gradient de l'entropie croisée est :

θ(k)S(θ)=1ni=1n(pi(k)(θ)zi(k))x~i \nabla_{\theta^{(k)}} S(\theta) = \frac{1}{n} \sum_{i=1}^n \left( p_i^{(k)}(\theta) - z_i^{(k)} \right) \cdot \tilde{x}_i

4. Application de la descente de gradient sur la dataset MNIST

Neuronal

Fonction implémentant la descente de gradient pour la régression multinomiale

def RegMultinomialGradDesc(X_train, Y_train, X_test, Y_test, Y_train_list, Y_test_list, epochs, alpha):
    """
    Descente de gradient pour la régression multinomiale

    Params
    ----------
    X_train, Y_train : données d'entraînement
    X_test, Y_test : données de test
    epochs : nombre d'itérations
    alpha : taux d'apprentissage

    Return
    --------
    Theta : paramètres optimisés
    Learning_T : temps d'exécution par époque
    Lvals_train, Lvals_test : entropie croisée sur les données d'entraînement et de test
    scores_train, scores_test : précision sur les données d'entraînement et de test
    """

    # Initialisation
    N, p = X_train.shape
    X_train = np.c_[np.ones(N), X_train]  # Ajout de biais (colonne de 1)
    X_test = np.c_[np.ones(X_test.shape[0]), X_test]  # Ajout de biais
    K = Y_train.shape[1]
    Theta = np.zeros((p + 1, K))  # Initialisation des paramètres

    # Listes pour enregistrer les métriques
    Learning_T, Lvals_train, Lvals_test = [], [], []
    scores_train, scores_test = [], []

    for epoch in range(epochs):
        start_time = time.time()

        # Calcul de l'entropie croisée et des prédictions
        L_train = eval_L(X_train, Y_train, Theta)
        L_test = eval_L(X_test, Y_test, Theta)
        Lvals_train.append(L_train)
        Lvals_test.append(L_test)

        # Affichage des résultats
        print(f"Epoch {epoch}: Train cost: {L_train}, Test cost: {L_test}")

        # Mise à jour des paramètres
        Theta -= (alpha / N) * Gradient(X_train, Y_train, Theta)

        # Calcul des précisions
        pred_train = prediction_Label(X_train[:, 1:], Theta)
        pred_test = prediction_Label(X_test[:, 1:], Theta)

        scores_train.append(np.mean(pred_train == Y_train_list))
        scores_test.append(np.mean(pred_test == Y_test_list))

        # Temps d'exécution
        Learning_T.append(time.time() - start_time)

    return Theta, Learning_T, Lvals_train, Lvals_test, scores_train, scores_test
Enter fullscreen mode Exit fullscreen mode

Appliquons l'algo de descente de gradient comme

# Params
alpha = 0.01
epochs = 100

# DATA d'entraînement et de test
X_train = X0[:50000]
Y_train = Y0_[:50000]
X_test = X1
Y_test = Y1_

# Conversion en listes pour précision
Y_train_list = Y0[:50000] 
Y_test_list = Y1

# Application de la descente de gradient
Theta, Learning_T, Lvals_train, Lvals_test, scores_train, scores_test = RegMultinomialGradDesc(
    X_train, Y_train, X_test, Y_test, Y_train_list, Y_test_list, epochs, alpha
)
Enter fullscreen mode Exit fullscreen mode

Résultat

Epoch

5. Régression multinomiale en keras

Nous mettons en œuvre la régression multinomiale en Keras pour classifier les chiffres du dataset MNIST

# Importation des librairies
import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow.keras.datasets import mnist
from tensorflow.keras.utils import to_categorical

# Chargement des données MNIST
(X_train_full, Y_train_full), (X_test, Y_test) = mnist.load_data()

# Reshape et normalisation des images
X_train_full = X_train_full.reshape(-1, 784) / 255.0
X_test = X_test.reshape(-1, 784) / 255.0

# Transformation des labels en format one-hot
Y_train_full_one_hot = to_categorical(Y_train_full)
Y_test_one_hot = to_categorical(Y_test)

# Séparation en sous-ensembles d'entraînement, validation et test
X_train = X_train_full[:50000]
Y_train = Y_train_full_one_hot[:50000]
X_valid = X_train_full[50000:60000]
Y_valid = Y_train_full_one_hot[50000:60000]

Enter fullscreen mode Exit fullscreen mode

D'où le modèle de régression multinomiale sous keras

# Dimensions du modèle
input_dim = X_train.shape[1]  # 784 variables
output_dim = Y_train.shape[1]  # 10 classes

# Définition du modèle de régression multinomiale
def create_model():
    model = tf.keras.Sequential([
        tf.keras.layers.Dense(output_dim, input_dim=input_dim, activation='softmax')
    ])
    model.compile(loss='categorical_crossentropy', optimizer='sgd', metrics=['accuracy'])
    return model
Enter fullscreen mode Exit fullscreen mode
Entrainement
# Entrainement du modèle
keras_model = classification_model()
keras_model.fit(X_train, Y_train, epochs=300, verbose=0)
Enter fullscreen mode Exit fullscreen mode
Précision
# Calcul du degré de précision ou accuracy sur X_valid
classes = np.argmax(keras_model.predict(X_valid), axis = 1)
N_valid = X_valid.shape[0]
numClassCorrect = 0
for i in range(N_valid):
    if classes[i]==Y_valid[i]:
        numClassCorrect+=1

accuracy = numClassCorrect/N_valid
print('Accuracy sur X_valid : ' + str(accuracy))

Enter fullscreen mode Exit fullscreen mode
Résultat de Précision : (93%)
Accuracy sur X_valid : 0.9348
Enter fullscreen mode Exit fullscreen mode

 GIF

Entraînement d'un nouveau modèle pour comparaison.

On teste avec un exemple diffèrent si on apprend le modèle sur un ensemble train réduit. Prenons N_train = 1000 par exemple.

X_train = X0[:1000]
Y_train = Y0_[:1000]
X_valid = X0[:400]
Y_valid = Y0[:400]
Enter fullscreen mode Exit fullscreen mode
Entrainement
# Entrainement du modèle
keras_model = classification_model()
keras_model.fit(X_train, Y_train, epochs=300, verbose=0)
Enter fullscreen mode Exit fullscreen mode
Précision
# Calcul du degré de précision ou accuracy sur X_test
N_test = X_test.shape[0]
numClassCorrect = 0
for i in range(N_test):
    if classes[i]==Y_test[i]:
        numClassCorrect+=1

accuracy = numClassCorrect/N_test
print('Accuracy sur X_test : ' + str(accuracy))

Enter fullscreen mode Exit fullscreen mode
Résultat de Précision : (86%)
Accuracy sur X_test : 0.8662
Enter fullscreen mode Exit fullscreen mode

6. Conclusion

Le modèle a montré une performance parfaite sur les données de validation (100%) avant même la 5ème itération, et une amélioration de 0.6% sur les données de test par rapport au précédent. En comparant l'apprentissage sur un réseau dense et convolutionnel, ce dernier a montré une légère amélioration (+0.6%) malgré la réduction des données, contrairement au réseau dense où la performance avait chuté de 5%. Le modèle convolutionnel est donc plus efficace en termes de données et de temps d'entraînement, ce qui en fait un choix optimal lorsque les ressources sont limitées.

Top comments (0)