TP 10 : Perceptron multi-couches

Dans ce TP, nous abordons les perceptrons multi-couches. Attention : ceux-ci ne sont pas adaptés aux exemples que nous traitons dans ce cours concernant classification supervisée pour des données tabulaires. Néanmoins, il nous paraît indispensable qu'une présentation de ces méthodes soit effectuée dans le cadre du cours.

À l'issue de ce TP, vous m'envoyez par email un compte-rendu (format pdf) indiquant la réponse aux questions qui sont posées. Vous m'envoyez également un fichier python réalisant toutes les manipulations de ce TP dénommé TP10.nom-prénom.py : je dois pouvoir exécuter ce fichier en tapant python3 TP10.nom-prénom.py et reproduire vos résultats. Cette exécution ne doit pas provoquer d'erreur de python. Remarque : un notebook ne convient pas.

Utilisation d'un réseau de neurones en python pour la classification supervisée

Préliminaires

On commence par importer la bibliothèque adéquate par : from sklearn.neural_network import MLPClassifier.
On suppose que l'on a chargé des exemples et que l'on a centré-réduit la valeur des attributs. Rappelons que centrer un attribut consiste à lui retirer sa valeur moyenne. Réduire un attribut consiste à le diviser par son écart-type. Une fois centré et réduit, un attribut est de moyenne nulle et de variance égale à 1. Pour utiliser un PMC, il faut impérativement centrer et réduire les attributs.
Comme toujours en apprentissage supervisé, il faut constituer un jeu d'entraînement et un jeu de test. On les note comme d'habitude X_train et Y_train les exemples d'entraînement, X_test et Y_test les exemples de test.

Création d'un PMC

Il y a de très nombreuses options possibles. Supposons que je veuille créer un PMC avec deux couches cachées de taille 5 et 3, fonction d'activation tangente hyperbolique. On fera :
PMC = MLPClassifier (hidden_layer_sizes = (5, 3), activation='tanh', solver = "lbfgs", random_state = rs).
Tout comme pour les arbres de décision, il faut spécifier un générateur de nombres pseudo-aléatoires. Voir les TPs sur les arbres de décision pour créer l'objet rs.
Comme c'est un petit réseau de neurones, j'indique solver = "lbfgs" qui calcule les poids du réseau de manière bien plus efficace qu'une descente de gradient stochastique (mais n'est utilisable que si le nombre de paramètres à optimiser est assez petit).

Entraînement du PMC

Pour calculer les poids sur les données d'entraînement, on fait comme suit :
PMC. fit (X_train, Y_train)

Prédiction avec un PMC

Pour prédire la classe d'un ensemble de données, on fait comme suit :
PMC. predict (X_test)
Comme pour les autres méthodes, cela fournit un vecteur contenant la prédiction pour chacune des lignes de X_test.
On peut ensuite calculer le taux de succès, une table de contingence, etc.
On peut aussi obtenir une estimation de la probabilité d'appartenir à chacune des classes avec PMC. predict_proba (X_test). Pour un problème ayant 2 classes, on obtient une matrice à 2 colonnes, chaque colonne indiquant la probabilité pour la donnée d'appartenir à l'une ou l'autre classe.

Les poids d'un PMC

On peut obtenir les biais et les poids du PMC avec PMC.coefs_ et les biais par PMC.intercepts_ :

Malheureusement, ces poids ne nous disent rien de qualitatif sur la manière dont le PMC prédit la classe d'une donnée. Certes on peut faire le calcul à la main (c'est bien de le faire pour une donnée pour vérifier que l'on a bien compris comment la sortie du PMC est calculée), mais c'est tout : les réseaux de neurones sont des boîtes noires.

Et enfin

Comme toujours en science des données, on cherche le plus petit modèle qui obtient de bonnes performances.
Une règle générale est de créer le PMC le plus petit possible qui donne un bon taux de succès. Pour cela, il faut tester plusieurs architectures. Pour les exemples traités dans ce TP, il faut prendre une ou deux couches cachées de petites tailles (5 perceptrons par couche par exemple, ou 5 et 3 en diminuant le nombre de perceptrons par couche quand on s'approche de la couche de sortie).
Et bien sûr, le calcul des poids étant stochastique, il faut réaliser plusieurs entraînements différents et conserver le paramétrage qui donne le meilleur taux de succès (sur le jeu de test). « Plusieurs entraînements différtents » signifie effectuer des entraînements avec des graines différentes, mais aussi des partitionnements jeu d'entraînement/jeu de test différents.

Travail en autonomie

Les iris

Le jeu de données des iris est un jeu de données célèbre. Il est constitué de 150 individus, chacun décrivant une fleur d'iris. Chaque individu est décrit par 4 attributs qui sont la longueur et la largeur des sépales (attributs 0 et 1 respectivement), la longueur et la largeur des pétales (attributs 2 et 3 respectivement). Chaque individu est d'une certaine espèce parmi 3, ce qui constitue la classe de la donnée. Les trois classes sont Setosa, Versicolor et Virginica.
Le jeu de données des iris est disponible en faisant :

from sklearn.datasets import load_iris
iris = load_iris (). data
classe = load_iris (). target

iris et target sont des tableaux numpy (pas des data.frames pandas). iris est donc un tableau de 150 lignes sur 4 colonnes.
Comme on l'a dit plus haut, il faut centrer et réduire les attributs. Pour cela, on peut centrer et réduire les attributs « à la main », avec des boucles. On peut aussi utiliser une fonction qui le fait  :

from sklearn.preprocessing import StandardScaler  
scaler = StandardScaler ()
scaler.fit (iris. data)
entrées = scaler.transform (iris. data)

entrées contient les mêmes données que iris. data mais elles sont maintenant centrées et réduites.

Les iris sont un problème à 3 classes : setosa (0), versicolor (1) et virginica (2). On le transforme en un problème à deux classes (en fait, 3 problèmes à 2 classes, un problème par classe originale) et on applique un PMC.

À faire :

Retour sur les olives

On reprend le jeu de données sur les olives que l'on a rencontré précédemment. Les données sont là : https://philippe-preux.codeberg.page/ensg/miashs/datasets/olives.csv.

À faire :