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.
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.
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).
Pour calculer les poids sur les données d'entraînement, on fait comme suit :
PMC. fit (X_train, Y_train)
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.
On peut obtenir les biais et les poids du PMC avec PMC.coefs_ et les biais par PMC.intercepts_ :
PMC.intercepts_ est une liste ayant autant d'éléments qu'il y a de couches, donc ici 3 (2 couches cachées et une couche de sortie). Le premier élément contient les biais de la première couche cachée (donc 5 valeurs), etc. jusqu'au dernier élément qui contient les biais de la couche de sortie (1 valeur).PMC.coefs_ est également une liste ayant autant d'éléments qu'il y a de couches. Cette fois-ci, chaque élément de la liste est une matrice contenant le poids reliant la couche précédente à la couche courante. Ainsi le premier élément de la liste est la matrice des poids reliant les entrées du PMC aux perceptrons de la première couche cachée ; le deuxième élément est la matrice des poids reliant la première couche cachée à la seconde ; le dernier élément fournit les poids entre les perceptrons de la deuxième couche cachée et la couche de sortie. Étant donnée l'architecture du PMC (4 entrées, puis 5 perceptrons puis 3 perceptrons puis une sortie), les matrices sont de taille 4x5, 5x3, 3x1.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.
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.
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 :
fit(), si un message indiquant l-bfgs failed to converge, ajouter l'option max_iter=500 lors de la création du MLP (dans MLPClassifier ()). La valeur par défaut de ce paramètre est 200, il correspond au nombre d'itérations réalisées lors de la recherche d'un optimum de la valeur des poids. Si 500 ne suffit pas, essayer une valeur plus élevée.PMC. predict_proba (X_test), déterminer s'il y a des exemples pour lesquels la prédiction est incertaine. Pour cela, on prendra comme critère une probabilité estimée entre 0,1 et 0,9 d'appartenance à une classe. Faire un scatter plot des iris dans le plan des attributs longueur et largeur des pétales en représentant les iris de chacune des 3 espèces avec 3 couleurs différentes, et en indiquant avec un caractère particulier (une croix assez grande par exemple) les données dont la classe est incretaine. Si votre PMC prédit de manière certaine la classe de tous les exemples, utilisez un PMC plus petit pour qu'il ne soit pas certain. L'idée est d'obtenir un graphique dans ce genre-là :
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 :
region tout d'abord, l'attribut area ensuite.