Tuto Python & Keras : Les réseaux de neurones

Table des matières

Introduction

  1. Définition
  2. Pourquoi on n’utilisera pas Scikit-learn ?
  3. Keras

3.1. Prétraitement des données

3.2. Construction du modèle d’apprentissage profond

3.3. Compilation du modèle

3.4. Entrainement du modèle

3.5. Prévision

3.6. Evaluation du modèle

  1. Cas d’utilisation
  2. Exercices
  3. Correction

Conclusion

Introduction

Dans le présent tutoriel nous nous intéresserons à un sous domaine de l’apprentissage automatique qui est l’apprentissage profond (deep learning). Celui-ci présente des algorithmes inspirés de la structure du cerveau humain souvent appelé réseaux de neurones artificiels (en anglais, Artificial Neural Networks ANN).  Le deep learning est un domaine de tendance, qui a fait preuve de résultats étonnants en robotique, en reconnaissance d’images et en intelligence artificielle.

1. Définition

Le cerveau humain est l’inspiration derrière les réseaux de neurones artificiels, puisqu’il est composé d’un certain nombre de neurones et il est ainsi capable d’effectuer des calculs complexes. Le réseau en lui-même est un puissant outil de modélisation. Nous exposerons dans ce qui suit les éléments les plus importants à maîtriser dans ce sujet.

Nous commençons dans un premier temps par une définition du perceptron qui est le plus simple réseau de neurones. Sa simplicité revient à sa forme qui est constituée d’un seul neurone. Similaire aux neurones biologiques, qui ont des dendrites et des axones, le neurone artificiel unique est une structure en arbre qui a des nœuds d’entrée et un seul nœud de sortie, connecté à chaque nœud d’entrée.

Voici une représentation visuelle de ceci :

Alors un neurone artificiel est composé de :

Nœuds d’entrée : chaque nœud d’entrée est associé à une valeur numérique qui peut être un nombre réel quelconque (positif, négatif, entier ou décimal).

Connexion : chaque connexion qui part d’un nœud d’entrée est associée à un poids, qui peut être n’importe quel nombre réel.

Somme pondérée : les valeurs des nœuds d’entrée et les connexions sont rassemblées dans la formule suivante : 

 , autrement dit 
.

Fonction d’activation ou de transfert : c’est le résultat de la somme précédente. Dans le cas simple on a la fonction d’identité, où 

 qui revient à dire
, dans ce cas x est la somme pondérée des nœuds d’entrée et des connexions.

Tout comme un neurone biologique qui ne s’allume que lorsqu’un seuil est dépassé, un neurone artificiel ne s’allume que lorsque la somme des entrées dépasse un seuil. Ceci n’est pas réalisable à travers une fonction d’identité, ce qui pousse à concevoir le système suivant :

  •  si x
  •  si x = 0 ;
  •  si x > 0.

Alors il s’agit d’une fonction discontinue et dans le but d’obtenir une fonction continue, on utilise la fonction sigmoïde.

Nœud de sortie : associé à la fonction de la somme pondérée des nœuds d’entrée.

- On peut avoir un paramètre supplémentaire qui est le biais, c’est le poids associé à un nœud d’entrée supplémentaire dont la valeur est fixée à 1. Cette valeur du biais est essentielle car elle permet de déplacer la fonction d’activation vers la gauche ou la droite pour déterminer le succès de l’apprentissage.

On peut remarquer que le perceptron ne fonctionnera qu’avec des données numériques. Alors évidemment il faut convertir toutes les données nominales en format numérique avant d’utiliser ce modèle d’apprentissage.

Pour des fins de classification, le modèle perceptron accepte que toute sortie supérieure à un certain seuil indique qu’une instance appartient à une certaine classe, tandis qu’une sortie inférieure au seuil déterminera que l’entrée appartient à une autre classe. Ainsi, la limite entre les deux classes est déterminée par la sortie qui est égale au seuil.

Maintenant qu’on a vu le perceptron, on passe aux perceptrons multicouches. Ces derniers sont les réseaux de perceptron, ils sont plus compliqués que le perceptron parce qu’ils sont constitués de plusieurs neurones organisés en couches. En pratique, le nombre de couches est généralement limité à deux ou trois, mais en théorie il n’y a pas de limite. Ces couches agissent de la même manière que les neurones biologiques où les sorties d’une couche servent d’entrées pour la couche suivante. Alors, on distingue une couche d’entrée, des couches cachées (Dropout) et une couche de sortie.

Les types de couches pour les modèles d’apprentissage profond sont nombreux. On retrouve, d’une part, les CNN qui sont les réseaux de neurones convolutifs ou à convolution (en anglais, Convolutional Neural Networks) et qui classifient les images ou font de la détection d’objets. D’autre part, il y a les RNN qui sont les réseaux de neurones récurrents (en anglais, Recurrent Neural Networks) et qui sont utilisés dans le traitement du langage naturel et la reconnaissance vocale.

2. Pourquoi on n’utilisera pas Scikit-learn ?

La bibliothèque Sckit-learn n’est pas adaptée aux réseaux de neurones vu que son interface ne permet pas la construction de réseaux de neurones complexes, ni de contrôler les détails de fonctionnement de ces réseaux. Ainsi cette bibliothèque n’est pas adaptée au deep learning.

3. Keras

Keras est l’une des bibliothèques Python les plus puissantes et les plus faciles à utiliser pour les modèles d’apprentissage profond et qui permet l’utilisation des réseaux de neurones de manière simple. Keras englobe les bibliothèques de calcul numérique Theano et TensorFlow. Pour définir un modèle de deep learning, on définit les caractéristiques suivantes :

  • Nombre de couches ;
  • Types des couches ;
  • Nombre de neurones dans chaque couche;
  • Fonctions d’activation de chaque couche ;
  • Taille des entrées et des sorties.

Le type de couches qu’on va utiliser est le type dense. Les couches denses sont les plus populaires car il s’agit d’une couche de réseau neuronal ordinaire où chacun de ses neurones est connecté aux neurones de la couche précédente et de la couche suivante.

Comme mentionné précédemment, il existe plusieurs types de fonctions d’activation, chacune d’entre elles relie l’entrée et le poids du neurone d’une manière différente ce qui influe sur le comportement du réseau. Les fonctions les plus utilisées sont la fonction Unité Linéaire Rectifiée (ReLU en anglais, Rectified Linear Unit), la fonction Sigmoïde et la fonction linéaire.

Pour ce qui est du nombre de neurones dans la couche d’entrée il est le même que le nombre de caractéristiques de l’ensemble de données. Dans la suite de ce tutoriel, nous allons essayer de construire un modèle d’apprentissage profond.

 3.1. Prétraitement des données

Tout d’abord, nous devons nettoyer l’ensemble des données que nous utiliserons pour le modèle d’apprentissage.

On importe les bibliothèques nécessaires, à savoir : matplotlib, numpy, pandas, seaborn et tensorflow.

  • Code :

import matplotlib.pyplot as plt
from matplotlib import ticker
import numpy as np
import pandas as pd
import seaborn as sns
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers

  • Résultat de l’exécution :

On importe ensuite l’ensemble des données sur lequel on va travailler sous forme de data-frame.

  • Code :

#import de la dataset
df = pd.read_csv('C:/Users/LENOVO/Desktop/coursGratuit/AmesHousing.csv')
df.head()

  • Résultat de l’exécution :

On affiche dans la partie du code suivante le nombre de valeurs manquantes pour chaque colonne de notre ensemble de données ainsi que le pourcentage de celles-ci.

  • Code :

#Valeurs manquantes
VM = pd.DataFrame({
'Colonne': df.columns.values,
'nbr de VM': df.isna().sum().values,
'% de VM': 100 * df.isna().sum().values / len(df),})
VM = VM[VM['nbr de VM'] > 0]
print(VM.sort_values(by='nbr de VM', ascending=False).reset_index(drop=True))

  • Résultat de l’exécution :

On supprime les colonnes contenant un pourcentage élevé de valeurs manquantes.

  • Code :

#suppression des colonnes contenant un pourcentage élevé de VM
df.drop(['Pool QC', 'Misc Feature', 'Alley', 'Fence', 'Fireplace Qu'],
axis = 1, inplace = True)

  • Résultat de l’exécution :

Dans le code suivant on affiche les variables catégorielles et les variables numériques de la dataset afin de pouvoir traiter les valeurs manquantes qui restent dans la dataset.

  • Code :

#on affiche les variables catégorielles et les variables numériques
colonnes_avec_VM = df.columns[df.isna().sum() > 0]
for col in colonnes_avec_VM :
print(col)
print(df[col].unique()[:5])
print('*'*30)

  • Résultat de l’exécution :

On remplace ensuite les valeurs manquantes numériques d’une colonne par la moyenne de celles-ci.

  • Code :

#on remplace les VM numériques par la moyenne de la colonne
num_VM = ['Lot Frontage', 'Mas Vnr Area', 'BsmtFin SF 1', 'BsmtFin SF 2',
'Bsmt Unf SF', 'Total Bsmt SF', 'Bsmt Full Bath',
'Bsmt Half Bath', 'Garage Yr Blt', 'Garage Cars', 'Garage Area']
for n_col in num_VM:
df[n_col] = df[n_col].fillna(df[n_col].mean())

  • Résultat de l’exécution :

On remplace également les valeurs manquantes catégorielles d’une colonne par le mode de celles-ci.

  • Code :

#On remplace les VM nominales par le mode variable
nom_VM = [x for x in colonnes_avec_VM if x not in num_VM]
for nom_col in nom_VM:
df[nom_col] = df[nom_col].fillna(df[nom_col].mode().to_numpy()[0])

  • Résultat de l’exécution :

Pour appliquer un modèle de deep learning, l’ensemble de données ne doit contenir que des variables numériques. Alors, on va encoder les variables catégorielles. Dans un premier temps, on affiche le type de données de chaque colonne de la dataset.

  • Code :

#encodage des variables catégorielles
#on affiche tout d'abord les types de données pour chaque colonne
types = pd.DataFrame({
'Colonne': df.select_dtypes(exclude='object').columns.values,
'Type': df.select_dtypes(exclude='object').dtypes.values})
print(types)

  • Résultat de l’exécution :

Même si Pandas ne considère pas les données de la colonne MS SubClass étant nominaux, le descriptif de la dataset en dit autrement. Il arrive des fois que Pandas retourne incorrectement le type d’une colonne.

Alors, on va sélectionner des variables qui représenteront notre dataset (pour ne pas avoir beaucoup de colonnes après l’encodage des valeurs catégorielles ce qui pourrait impliquer le sur-apprentissage du modèle) puis on transforme ces données catégorielles en utilisant la fonction get_dummies() de la bibliothèque pandas.

  • Code :

#encodage des variables catégorielles,
#selon le descriptif de la dataset la colonne MS SubClass est nominale,
#Pandas a du mal à retourner son vrai type
df['MS SubClass'] = df['MS SubClass'].astype(str)
selection_de_variables = ['MS SubClass', 'MS Zoning', 'Lot Frontage', 'Lot Area',
'Neighborhood', 'Overall Qual', 'Overall Cond',
'Year Built', 'Total Bsmt SF', '1st Flr SF', '2nd Flr SF',
'Gr Liv Area', 'Full Bath', 'Half Bath', 'Bedroom AbvGr',
'Kitchen AbvGr', 'TotRms AbvGrd', 'Garage Area',
'Pool Area', 'SalePrice']
df = df[selection_de_variables]
#la dataset comprendera maintenant que 67 variables
df = pd.get_dummies(df)

  • Résultat de l’exécution :

Maintenant on fractionne l’ensemble de données en des données d’entrainement et des données de test.

  • Code :

#fractionner dataset en des données de test et de train
train = df.sample(frac = 0.8, random_state = 9)
test = df.drop(train.index)
#variable cible
train_cible = train.pop('SalePrice')
test_cible = test.pop('SalePrice')

  • Résultat de l’exécution :

On standardise ensuite les variables afin de les avoir sur la même échelle.

  • Code :

#Standardisation
variables_pred = train.columns
for col in variables_pred:
col_moyenne = train[col].mean()
col_ecart_type = train[col].std()
if col_ecart_type == 0:
col_ecart_type = 1e-20
train[col] = (train[col] - col_moyenne) / col_ecart_type
test[col] = (test[col] - col_moyenne) / col_ecart_type

  • Résultat de l’exécution :

3.2. Construction du modèle d’apprentissage profond

Maintenant que les données sont prétraitées, nous allons commencer à construire notre modèle.

On crée une instance du Sequential() de la bibliothèque Keras, celle-ci superpose un ensemble de couches pour en créer un seul modèle. On lui passe en paramètre une liste de couches qu’on souhaite utiliser pour notre modèle.

Comme vous allez le remarquer dans ce modèle, on a créé plusieurs couches denses et une seule couche de type Dropout. La première couche est la couche d’entrée, son nombre de neurones est égal au nombre de caractéristiques de l’ensemble de données.

Dans chaque couche on retrouve 64 neurones, ce nombre est le résultat optimal de plusieurs tests. En effet, 64 neurones par couches pour l’exemple de cet ensemble de données donnent un résultat assez précis.

Remarque :

Il est recommandé d’essayer plusieurs chiffres jusqu’à l’obtention de résultats précis.

Pour la couche Dropout, on a diminué de 30% le nombre des données d’entrée afin d’éviter le phénomène du overfitting. La graine prend une valeur de 2 pour avoir des résultats plus reproductibles.

Finalement, la dernière couche dense avec un seul neurone est la couche de sortie. Par défaut, elle prend la fonction d’activation linéaire.

  • Code :

modele = keras.Sequential([
layers.Dense(64, activation = 'relu', input_shape = [train.shape[1]]),
layers.Dropout(0.3, seed = 2),
layers.Dense(64, activation = 'swish'),
layers.Dense(64, activation = 'relu'),
layers.Dense(64, activation = 'swish'),
layers.Dense(64, activation = 'relu'),
layers.Dense(64, activation = 'swish'),
layers.Dense(1)])

  • Résultat de l’exécution :

3.3. Compilation du modèle

L’étape suivante est la compilation du modèle. Pour ce faire, il faut choisir :

  • La fonction de perte : utilisée pour diminuer l’erreur. Plus l’erreur est faible plus le modèle est précis ;
  • L’optimiseur : aide à obtenir de meilleurs résultats pour la fonction de perte ;
  • Métriques : utilisées pour évaluer le modèle.

Dans le code suivant nous avons utilisé la fonction de perte comme étant l’erreur quadratique moyenne avec l’optimiseur RMSprop qu’on lui donne un taux d’apprentissage de 0,001. Et pour la métrique nous avons utilisé l’erreur absolue moyenne qui va avec la fonction de perte.

  • Code :

optimiseur = tf.keras.optimizers.RMSprop(learning_rate = 0.001)
modele.compile(loss = tf.keras.losses.MeanSquaredError(),
optimizer = optimiseur,
metrics = ['mae'])

  • Résultat de l’exécution :

3.4. Entrainement du modèle

Dans ce qui suit, on entraine le modèle sur le jeu de données train. Celui-ci passe 70 fois sur l’ensemble de données pour en tirer le meilleur résultat possible. Ensuite, 20% des données d’entrainement sont utilisées pour la validation.

La fonction fit() affiche la valeur de la fonction de perte et la métrique pour chaque époque. 

  • Code :

training = modele.fit (train, train_cible, epochs = 70, validation_split = 0.2)

  • Résultat de l’exécution :

3.5. Prévision

Avant de commencer d’utiliser le modèle pour nos prévisions, nous allons illustrer comment la fonction de perte et la mae vont changer.  Remarquons que ces valeurs ont diminué au fil du temps, alors le modèle est devenu plus précis.

  • Code :

historique = pd.DataFrame(training.history)
historique['epoque'] = training.epoch
figure, axe = plt.subplots(figsize = (14,8))
num_epoque = historique.shape[0]
axe.plot(np.arange(0, num_epoque), historique["mae"],
label = "Training MAE", lw = 3, color = 'red')
axe.plot(np.arange(0, num_epoque), historique["val_mae"],
label = "Validation MAE", lw = 3, color = 'blue')
axe.legend ()
plt.tight_layout()
plt.show()

  • Résultat de l’exécution :

On prend une unité des valeurs de test pour avoir sa prédiction en utilisant notre modèle. Ensuite, on affiche la valeur de la prédiction et la valeur actuelle pour les comparer.

  • Code :

test1 = test.iloc[[10]]
test_prediction = modele.predict(test1).squeeze()
test_label = test_cible.iloc[10]
print("Prédiction du modèle = {:.2f}".format(test_prediction))
print("Valeur actuelle = {:.2f}".format(test_label))

  • Résultat de l’exécution :

3.6. Évaluation du modèle

L’étape finale dans ce tutoriel est l’évaluation du modèle qu’on a créé.

  • Code :

score = modele.evaluate( test, test_cible, verbose = 0)
print(score)

  • Résultat de l’exécution :

4. Cas d’utilisation

Le deep learning est utilisé dans différents domaines de la vie réelle, notamment :

  • Reconnaissance d’image ;
  • Traduction automatique ;
  • Diagnostic médical ;
  • Prédiction financière ;
  • Détection de fraudes ;
  • Chatbots ;
  • Robots intelligents.

5. Exercices

Exercice 1 :

  1. Reprenez la même dataset utilisée dans ce tutoriel.
  2. Créez un modèle d’apprentissage profond qui utilise l’optimiseur ‘Adam’.
  3. Modifiez dans les étapes précédemment suivies pour obtenir de meilleurs résultats.

6. Solution des exercices

Exercice 1 :

Nous avons dans cet exemple suivi les mêmes étapes de ce tutoriel pour construire un modèle d’apprentissage profond, on a juste apporté quelques modifications aux fonctions d’activation de chaque couche et on a changé l’optimiseur.

La première étape est de créer une instance du Sequential() de la bibliothèque Keras, celle-ci superpose un ensemble de couches pour en créer un seul modèle. On lui passe en paramètres une liste de couches qu’on souhaite utiliser pour notre modèle.

Puis la deuxième étape est de compiler le modèle. Pour ce faire, nous avons utilisé la fonction de perte comme étant l’erreur quadratique moyenne avec l’optimiseur Adam avec un taux d’apprentissage de 0,001. Et pour la métrique, nous avons utilisé l’erreur absolue moyenne qui va avec la fonction de perte.

  • Code :

modele2 = keras.Sequential([
layers.Dense(64, activation = 'swish', input_shape = [train.shape[1]]),
layers.Dropout(0.3, seed = 2),
layers.Dense(64, activation = 'relu'),
layers.Dense(64, activation = 'swish'),
layers.Dense(64, activation = 'relu'),
layers.Dense(64, activation = 'swish'),
layers.Dense(64, activation = 'relu'),
layers.Dense(1)])
optimiseur = tf.keras.optimizers.Adam(learning_rate=0.001)
modele2.compile(loss = tf.keras.losses.MeanSquaredError(),
optimizer = optimiseur,
metrics = ['mae'])

  • Résultat de l’exécution :

La troisième étape est d’entrainer le modèle sur le jeu de données train. Celui-ci passe 70 fois sur l’ensemble de données pour en tirer le meilleur résultat possible. Ensuite, 20% des données d’entrainement sont utilisées pour la validation.

La fonction fit() affiche la valeur de la fonction de perte et la métrique pour chaque époque.

  • Code :

training = modele2.fit (train, train_cible, epochs = 70, validation_split = 0.2)

  • Résultat de l’exécution :

Cette étape est facultative, mais elle nous permet d’illustrer comment la fonction de perte et la mae vont changer.  Remarquons que ces valeurs ont diminué au fil du temps, alors le modèle est devenu plus précis.

  • Code :

historique = pd.DataFrame(training.history)
historique ['epoque'] = training.epoch
figure, axe = plt.subplots(figsize = (14,8))
num_epoque = historique.shape[0]
axe.plot(np.arange(0, num_epoque), historique["mae"],
label = "Training MAE", lw = 3, color = 'red')
axe.plot(np.arange(0, num_epoque), historique["val_mae"],
label = "Validation MAE", lw = 3, color = 'blue')
axe.legend ()
plt.tight_layout ()
plt.show ()

  • Résultat de l’exécution :

L’avant dernière étape est celle du test,on prend une unité des valeurs de test pour avoir sa prédiction en utilisant notre modèle. Ensuite, on affiche la valeur de la prédiction et la valeur actuelle pour les comparer.

  • Code :

test1 = test.iloc[[10]]
test_prediction = modele2.predict(test1).squeeze()
test_label = test_cible.iloc[10]
print("Prédiction du modèle = {:.2f}".format(test_prediction))
print("Valeur actuelle = {:.2f}".format(test_label))

  • Résultat de l’exécution :

Finalement, on évalue le modèle en utilisant la méthode evaluate().

  • Code :

score = modele2.evaluate (test, test_cible, verbose = 0)
print(score)

  • Résultat de l’exécution :

Conclusion

Dans le présent tutoriel, nous avons vu les différents éléments de base à savoir pour s’initier à l’apprentissage profond. Nous avons ensuite prétraité un ensemble de données, puis construit un modèle d’apprentissage profond en utilisant Keras pour compiler le modèle par la suite et l’ajuster afin qu’il soit convenable à notre ensemble de données. Finalement, on a fait de prédictions en se basant sur ce que le modèle a développé en terme d’apprentissage.

Article publié le 08 Décembre 2020par Imane BENHMIDOU