Perceptron multi-couches

Ce TP consiste à faire quelques expérimentations avec le perceptron multi-couches (PMC). On utilise la bibliothèque nnet de R.

Classification supervisée

La bibliothèque nnet permet de manipuler des perceptrons à une couche cachée en R pour réaliser des tâches de classification supervisée binaire. Pour pouvoir l'utiliser, on tape library (nnet).

Entraînement d'un PMC pour la classification supervisée

Pour entraîner un PMC, on a besoin d'un jeu d'exemples. On doit ensuite déterminer le nombre d'unités dans la couche cachée. Ensuite, on peut commencer à utiliser la fonction nnet qui entraîne un PMC. Cette fonction renvoie un PMC entraîné à l'aide des exemples.

Par exemple, pour apprendre à disciminer les iris Setosa des autres iris, on pourra taper :

> iris.nnet <- nnet (iris[,1:4], ifelse (iris[,5] == "setosa", 1, 0), size = 10)

ce qui signifie :

Une fois l'entraînement réalisé, iris.nnet contient le PMC qui a été entraîné.

L'entraînement s'arrête si l'un de ces 3 critères est rempli :

Il existe d'autres paramètres à la fonction nnet, mais ceux mentionnés ici suffisent pour ce TP.

Prédiction à l'aide d'un PMC

Une fois l'entraînement réalisé, on peut utiliser le PMC entraîné pour rédire la classe de données. Par exemple, pour continuer avec l'exemple ci-dessus, on peut taper :

> predict (iris.nnet, iris[,1:4])

ce qui affiche la valeur prédite pour chacun des 150 exemples. Aux 50 premiers (qui sont de classe setosa) est associée une valeur proche de 1, alors qu'une valeur proche de 0 est prédite pour les 100 autres. Cela est conforme à la classe que nous avons spécifiée pour entraîner le PMC.

Notez bien que la manière dont nous venons d'utiliser la fonction nnet est incorrecte. En effet, il faut toujours découper l'ensemble d'exemples en un ensemble d'entraînement et un ensemble de test que l'on utilise séparément, le premier pour entraîner le PMC, le second pour estimer la probabilité qu'il a de prédire correctement la classe d'une donnée.
Nous avons fait ainsi car nous voulions seulement montrer comment s'utilisent les fonctions nnet() et predict(). Dans la suite du TP, il faudra procéder comme il se doit.

À faire

  1. Refaire la manipulation expliquée ci-dessus mais de manière correcte, i.e. en découpant l'ensemble d'exemples en un sous-ensemble d'entraînement contenant 80 % des exemples et un sous-ensemble de test contenant les autres exemples. Prenez garde à avoir la même proportion d'exemples de chacune des classes dans chaque sous-ensemble. Notez que les setosa sont les exemples numérotés de 1 à 50.
  2. Faites le même genre de manipulation pour chacune des deux autres classes, virginica et versicolor.
  3. Déterminez le nombre minimal d'unités à utiliser dans la couche cachée pour que les classes soient bien discriminées.

Régression

On s'intéresse maintenant à une tâche de régression, c'est-à-dire une tâche d'apprentissage supervisé dans laquelle l'étiquette est un nombre réel.

nnet() s'utilise exactement de la même manière que pour la classification supervisée sauf qu'il faut utiliser un neurone à fonction d'activation linéaire en sortie. Pour cela, on met linout = TRUE lors de l'appel à nnet().

Fonction en 1 dimension

On cherche à mieux comprendre comment un perceptron multi-couches apprend une fonction. Pour pouvoir illustrer graphiquement cet apprentissage, on s'intéresse à l'apprentissage d'une fonction à une seule variable.

Dans tout ce suit concernant une fonction à 1 dimension, on utilise les exemples suivants :

Variabilité des performances d'une architecture donnée

Tout d'abord, ayant choisi une architecture de réseau, on se demande si un apprentissage des poids donne toujours le même, ou à peu près le même, résultat.

  1. Entraîner un PMC ayant 5 neurones cachés pendant au maximum 1000 itérations avec les 30 premiers exemples.
  2. Mesurer la RMSE de ce réseau sur les exemples de tests.
  3. Répéter cela 10 fois. Stocker la RMSE de chacun des 10 réseaux.
  4. Observer ces 10 RMSE. Qu'en pensez-vous ?

Combien de neurones cachés ?

On va faire varier le nombre de neurones cachés pour voir l'effet que cela a sur la fonction qui est estimée et aussi pour déterminer un nombre de neurones cachés ayant les meilleures performances.

Pour cela, vous allez faire une boucle pour tester des PMC ayant de 1 à 20 neurones cachés. N'oubliez pas ce que vous avez constaté lors de l'exercice précédent. Pour chaque nombre de neurones cachés, vous faites un graphique avec tous les exemples et les valeurs prédites dans une autre couleur. Prenez le temps d'observer les changements et comment la fonction est, en général, de mieux en mieux approchée quand le nombre de neurones cachés augmente. Par exemple, j'ai obtenu les graphiques en utilisant 1, puis 2, puis 3, ... jusque 5 neurones cachés :

Stockez la plus petite RMSE obtenue pour chaque nombre de neurones cachés. Nommons rmse le vecteur contenant toutes ces valeurs. Par exemple, rmse [1] contiendra la RMSE du PMC ayant un neurone caché, rmse [2] contiendra la RMSE du PMC ayant deux neurones cachés et ainsi de suite.
Conservez le PMC ayant la plus petite RMSE (quelle que soit sa taille).
À la fin, vous affichez le nombre de neurones cachés de ce meilleur PMC. (Si pmc est le résultat d'un appel à nnet(), donc un PMC, pmc$n fournit une liste contenant le nombre de neurones de chacune des 3 couches.)
Répétez tout cela plusieurs fois. Le meilleur PMC trouvé a-t-il toujours le même nombre de neurones cachés ?

Le meilleur PMC est-il vraiment celui-là ? Faites un plot (rmse). Que constatez-vous ?
Vous obtenez un graphique qui ressemble à celui-ci :

Le meilleur réseau est celui qui est au niveau du coude.

Influence du bruit

Jusqu'à maintenant, nous avons travaillé sur un jeu de données non bruitées. Quelle est l'influence de ce bruit sur le réseau de neurones. Refaites les mêmes manipulations en utilisant ces fichiers de exemples à la place :

Fonction en 2 dimensions

Refaites rapidement le même genre d'étude sur un jeu de données correspondant à une fonction bidimensionnelle.
Les données sont disponibles en suivant ce lien.