Posté 03 août 2015 - 08:19
Euh, je ne comprends pas vraiment ce que tu fais.
Le principe de fonctionnement d'un réseau de neurones, c'est d'apprendre à partir d'un ensemble de données. Pour cela, on a généralement une ĥase d'apprentissage, et une phase d'utilisation du réseau ayant appris.
Avant d'aller plus loin, précisons quelque chose : il y a 3 manières d'apprendre.
- apprentissage supervisé : on dit au réseau ce qu'il doit apprendre. On lui donne des couples (entrée, sortie) pour qu'il apprenne par coeur quoi répondre à une certaine entrée. Par exemple, dans le cas du ET logique, on lui donne (entre autres) le couple ((1, 1), 1) qui signifie"si mes deux entrées sont à 1, ma sortie doit valoir 1.
- apprentissage non-supervisé : c'est un peu plus compliqué, on laisse le réseau trouver tout seul la structure des données. En général, on va donner au réseau une entrée, sa couche de sortie va être activée, et on ne garde que le neurone le plus activé. On apprend ensuite à répondre avec ce neurone pour la même entrée (ou des entrées similaires).
- apprentissage par renforcement : celui-là est entre supervisé et non-supervisé. On ne donne plus au réseau ce qu'il doit apprendre, mais on lui indique si ce qu'il a répondu est "bien ou pas bien". Pour une entrée, on renvoie au réseau un signal de renforcement qui est une valeur positive ou négative. Dans le premier cas, on incite le réseau à répondre la même chose pour des entrées similaires, ou au contraire, à ne pas répondre ça du tout.
Dans les deux premiers cas, on se base en général sur une information d'erreur entre la sortie désirée et la sortie obtenue, qu'on utilise pour corriger les poids.
Pour en revenir à ton code : faisons de l'apprentissage supervisé. Pour cela, il faut créer un ensemble de données à donner au réseau. Pour la fonction logique ET, ce seront des couples de valeurs d'entrée flottantes et la sortie binaire associée. Les quatre cas (0,0) (0,1) (1,0) (1,1) plus une dizaine de valeurs d'entrées intermédiaires, ça devrait suffire.
À chacune de ces entrées, tu vas ajouter la réponse attendue. À la fin, tu dois avoir une variable qui contient tout ça :
dataset = {((0,0), 0) ; ((0,1), 0) ; ((1,1), 1) ; ((0.2, 0.7), 0) ; ... } // Dataset sur lequel apprendre (pseudo-code).
Ensuite, tu as ton réseau. Si tu sais faire de la POO, je te conseille de créer une classe, c'est assez pratique. Dans tous les cas, il y a trois éléments principaux à ce réseau :
- ses poids (Nb d'entrée * Nb sortie = 2 * 1 dans notre cas).
- sa règle de calcul de l'activité de sortie (l'activité de chaque neurone de sortie est la somme pondérée des entrées par les poids, passée par une fonction non-linéaire ou seuillée - dans notre exemple, on seuille à 0.5)
- sa règle d'apprentissage. On peut se contenter de Widrow-Hoff, qui nous donne comment on modifie chaque poids w_i en fonction de l'erreur de prédiction (valeur de sortie désirée - valeur obtenue). C'est la partie V du tuto d'Alp.
Donc pour définir ton réseau, il te faut un tableau N*M de poids, une fonction qui l'utilise pour calculer la sortie, une fonction qui le modifie pour tenir compte de l'erreur (ou un attribut et deeux méthodes dans une classe).
Au début, on initialise les poids à des valeurs aléatoires (on peut aussi les initialiser à zéro). Cela représente le fait qu'on ne sait pas quelle fonction on va devoir apprendre. C'est normal, c'est la beauté d'un tel système : il peut apprendre à peu près tout ce qu'on veut, tant qu'on lui donne des exemples (et que le réseau est capable d'approximer la fonction, voir l'exemple du XOR du tuto de Alp).
On a donc un dataset, un réseau, on peut commencer à apprendre. Pour cela, on va tirer au hasard un exemple du dataset, et voir ce qu'il donne dans notre réseau. On appelle donc la fonction de calcul de la sortie avec l'entrée de l'exemple, et on obtient une valeur de sortie.
Au début, comme on a initialisé les poids aléatoirement, il y a de fortes chances que la sortie obtenue soit différente de la sortie désirée. Ça n'est pas grave, puisqu'on va de toute façon appeler la fonction d'apprentissage.
On calcule l'erreur, on modifie les poids, et la prochaine fois qu'on aura la même entrée, il est plus probable que le réseau réponde la bonne valeur.
Et hop, on peut recommencer ces deux étapes en tirant un nouvel exemple et en répétant les étapes précédentes.
Et quand est-ce qu'on s'arrête ? Pour ça, on va définir une valeur de convergence : tant que cette valeur est différente de zéro, on apprend (puisque ça veut dire qu'il y a e une modification à faire, et qu'on n'avait pas approximé correctement la fonction). Cette valeur, c'est simplement la somme des modifications sur les poids.
En pratique, on ne vérifie pas qu'elle vaille zéro, mais qu'elle soit inférieure à une valeur très faible "epsilon".
Maintenant qu'on a finit d'apprendre, on ne va pas arrêter notre programme tout de suite, ça serait dommage d'avoir faire apprendre notre réseau pour ne pas l'utiliser.
Ici, on va donner des entrées à notre réseau, et voir ce qu'il répond. Dans notre cas, on peut demander à l'utilisateur de saisir deux valeurs au clavier, et afficher la sortie. Si tout se passe bien, on devrait avoir une sortie qui reproduit la fonction ET.