Nouvelles Chroniques d'Amethyste

Penser au Sens, pas au Verbe

Réseau de neurones: le Perceptron multicouche

Poster un commentaire

L’article précédent a permis de poser les bases de ce que nous allons poursuivre maintenant.

Le problème du Perceptron simple sont ses limitations que nous allons essayer de dépasser avec le Perceptron multicouche.

Le Perceptron multicouche se caractérise par:

  • Il est basé sur le modèle du Perceptron
  • Il a plusieurs couches de neurones liées entre elles
  • Chaque couche a un ou plusieurs neurones

Le Perceptron simple ne pouvait classifier que des données séparées par un hyperplan. Nous allons passer à des données plus complexes séparables par des hyper-surfaces.

Nous allons donc travailler avec une couche d’entrée, une couche de sortie et une ou plusieurs couches cachées. En pratique un réseau 3 couches suffit presque toujours. Le théorème de Cybenko assure en effet que :

N’importe quelle fonction peut être approximée avec une précision quelconque à l’aide d’un réseau 3 couches.

Présentation

Vous trouverez le code dans mon espace Github:

https://github.com/DeLeneMirouze/NeuralNetwork

Le projet s’appelle Multicouche.

Cette fois encore nous avons affaire à une application Console. Le démarrage de l’application est un peu particulier car il nous propose deux options:

2015-09-06_20-41-04

Dans l’immédiat nous sélectionnerons l’option 1 (Cartographie). L’option 2 (Fleur d’iris) sera étudiée en deuxième partie de cet article lorsque nous examinerons un exemple plus réel.

 

Le jeu d’essai se situe dans le fichier zones2.txt. Il est constitué d’une série de coordonnées (X,Y) suivit d’un codage 0 ou 1 qui distingue deux zones. Graphiquement les choses ressemblent à ceci:

2015-09-06_20-51-58

On dispose de deux zones dont la ligne de séparation n’est pas linéaire. Le Perceptron simple ne pourrait être entraîné pour les distinguer.

Les données sont extraites de cet article:

http://dynamicnotions.blogspot.fr/2008/09/single-layer-perceptron.html

Pour cet exemple nous allons utiliser un neurone de topologie 2x2x1. Cela signifie qu’il a 3 couches.

  • La couche d’entrée à 2 neurones
  • La couche cachée à 2 neurones également
  • La couche de sortie a un seul neurone

Une fois lancée, l’application rappelle tout cela. Chaque couche est représentée avec les valeurs des poids et les biais. Nous détaillerons le reste plus loin.

2015-09-13_11-07-44

On initialise le réseau avec des valeurs aléatoires des poids, on laisse à 0 les biais, mais ils évoluerons lors de la phase d’apprentissage.

Fonctionnement

Les mathématiques du Perceptron multicouche sont similaires à celles du Perceptron. La phase d’entrainement également.

On présente donc un essai à la couche d’entrée et on calcule les sorties de chaque neurone jusqu’à la couche de sortie en utilisant la formule bien connue maintenant:

2015-09-06_16-57-09

Oui mais comment passe t’on d’une couche à l’autre?

La géométrie du réseau ressemble à celle-ci dans laquelle chaque cercle représente un neurone formel complet (avec la fonction de transfert):

2015-09-06_21-34-29

Comme on le voit sur le schéma, chaque sortie de la couche N constitue une entrée de la couche N+1. Chaque neurone de la couche cachée par exemple, a deux entrées.

Nous utilisons la classe DeepNetwork qui modélise notre réseau. Cette classe abrite pour l’essentiel une collection de Couche qui modélise une couche particulière. On remarque que chaque couche peut avoir sa propre fonction de transfert.

Couche abrite à son tour une collection de Neurone qui modélise un neurone.

L’entrainement du réseau est réalisé dans DeepNetwork.Entrainer. La structure du code est similaire à celle examinée dans le cas du Perceptron simple.

La méthode Calculer() calcule un état du réseau, c’est à dire les sorties des neurones de la couche de sortie étant donnée un état de la couche d’entrée.

Comme dans le cas du Perceptron simple on obtient une sortie que l’on compare avec les valeurs ciblées. On calcule de même un ecart.

Le problème est alors de savoir redistribuer l’erreur obtenue sur les neurones et les biais de la couche de sortie, mais également des couches cachées.

L’algorithme standard s’appelle rétro-propagation (back-propagation) découvert dans les années 70. Il est appelé ainsi car il est composé de deux mouvements:

  1. Calcul des états du réseau en partant de la couche d’entrée
  2. Correction des états étant donnée l’erreur du réseau en partant de la couche de sortie

Nous avons vu la première phase. Voyons la suivante.

Les mathématiques de la phase d’apprentissage de ce réseau sont plus complexe que ce qui a été vu dans le cas du Perceptron simple. On a cette fois besoin de la dérivée de la fonction de transfert qui doit donc être dérivable.

On peut trouver les formules avec un exemple de calcul ici:

Et un exemple de démonstration ici:

http://alp.developpez.com/tutoriels/intelligence-artificielle/reseaux-de-neurones/

Si vous décidez d’écrire votre propre code (et je vous le conseille), ces liens vont beaucoup vous aider à comprendre les calculs.

 

Les opérations sont orchestrées ainsi:

CalculerDeltaO(cibles[t]);
// calculer des gradiants de la couche cachée
CalculerDeltaH(coucheCachee, coucheSortie);
 
// recalculer les poids et les biais de la couche de sortie
Propager(coucheCachee, coucheSortie, pas);
// recalculer les poids et les biais de la couche cachée
Propager(coucheEntree, coucheCachee, pas);

On calcule d’abord les gradients (les erreurs) des couches de sortie et de la couche cachée. Les deux méthodes en CalculerXXXX() alimentent la propriété Neurone.Erreur avec la contribution de ce neurone à l’erreur totale.

Ces erreurs sont ensuite rétro-propagées d’abord sur la couche de sortie, puis sur la couche cachées avec la méthode Propager():

private void Propager(Couche couche1, Couche couche2, double pas)
{
   for (int c2 = 0; c2 < couche2.Neurones.Length; c2++)
   {
      Neurone neurone2 = couche2.Neurones[c2];
      for (int c1 = 0; c1 < couche1.Neurones.Length; c1++)
      {
         neurone2.Poids[c1] += pas * neurone2.Erreur * couche1.Neurones[c1].Sortie;
      }
 
      neurone2.Biais += pas * neurone2.Erreur;
   }
}

La ligne 8 est la ligne importante, 11 est son adaptation pour le biais. Cette ligne effectue la rétro-propagation de couche1 vers couche2 et ajuste le poids à une nouvelle valeur compte tenu de l’erreur commise sur la sortie.

Résultats

La phase d’entraînement génère une sortie similaire à celle-ci:

2015-09-13_11-09-41

5000 itération max, mais un test arrête l’entrainement une fois que l’erreur descend en dessous d’un certain seuil. Cela arrive en général avant!

Cette méthode d’arrêt est importante. Si le réseau est trop entraîné il pourrait devenir trop spécialisé sur le jeu d’essai et ne plus être capable de prédire d’autres valeurs.

 

Notre jeu d’essai contient 25 essais.

On affiche les valeurs calculées pour les biais et les poids. Somme, sortie et gradient sont les valeurs obtenues pour la dernière entrée du test. C’est utile pour déboguer.

Nous procédons ensuite à des essais, de préférence avec un jeu de test qui n’a pas servit à l’entraînement du réseau. Voici les résultats:

2015-09-13_11-11-07

Comme on le voit, le réseau sait classer tous les exemples de test.

Commentaires

Nous avons appris à un réseau à classifier des données. Notre exemple peut sembler abstrait, mais tout dépend du sens que l’on donne aux sorties.

On pourrait imaginer par exemple que les deux entrées représentent le poids et la taille d’une personne tandis que la sortie seraient le sexe (H/F). Le réseau essayerai alors de trouver le genre d’un individu. Bien sûr ces critères ne sont pas suffisamment précis, mais au moins le réseau fera mieux que le hasard.

Voici un article qui aborde la question de la standardisation des données:

https://visualstudiomagazine.com/articles/2014/01/01/how-to-standardize-data-for-neural-networks.aspx

 

Ce qui est intéressant est de remarquer qu’un tel problème ne saurait être résolu de façon analytique. C’est dans ce type de problème qu’un réseau de neurone peut exceller.

Exemple des fleurs d’iris

Essayons de faire fonctionner un exemple plus sophistiqué. Cet exemple correspond à l’option 2 du menu:

2015-09-06_20-41-04

Le jeu de test est un ensemble de données très utilisées dans le domaine de l’intelligence artificielle. Il peut être trouvé sur ce site avec d’autres exemples:

http://archive.ics.uci.edu/ml/datasets/Iris

On connaît 220 espèces d’iris. Dans les années 30, Ronald Fisher a mesuré les dimensions des pétales et des sépales de 3 espèces:

  • Iris-virginica
  • Iris-setosa
  • Iris-versicolor

 

 Iris-virginica  Iris-setosa  Iris-versicolor

Fisher a réalisé 4 mesures par individu. Nous utiliserons donc un réseau 4x7x3.

Fonctionnement

Mis à part les données et la topologie, le code est le même. On a tout de même besoin de coder l’espèce de chaque fleur. Le codage est le suivant dans l’ordre des neurones de sortie:

  • 1,0,0 => Iris-virginica
  • 0,0,1 => Iris-setosa
  • 0,1,0 => Iris-versicolor

On code également les entrées, par ordre d’apparition des neurones:

  • Largeur pétale
  • Longueur pétale
  • Largeur sépale
  • Longueur sépale

Pour les tests nous avons extrait deux exemplaires de chaque espèce qui n’ont pas servit de jeu d’entraînement. La sortie donne:

2015-09-13_11-13-39

Comme on peut le voir, le réseau fonctionne comme prévu.

Bibliographie

Pour finir une bibliographie imposante:

http://web.ift.uib.no/Fysisk/Teori/NEURO/neurons.html

 

 

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s