Alors j'ai un léger soucis. Pour lundi, je dois mettre au point pour l'IA un perceptron multicouche en C++. Pour ceux qui connaissent pas le perceptron, bah je peux pas vraiment les aider, j'ai même pas compris moi même (on a un très mauvais prof...).
On nous a filé un algo assez approximatif, donc en fait je pense que seuls ceux qui maîtrisent le perceptron multicouches pourront m'aider...
Deja je vous poste mon code :
fichier main.cpp
#include <iostream>
#include <stdlib.h>
#include "Couche.h"
#include "Neurone.h"
#include "Apprentissage.h"
#include <math.h>
using namespace std;
double getRand();
int main()
{
cout << "Creation des couches ... ";
Couche* cDepart = new Couche();
Couche* cInter = new Couche();
Couche* cFin = new Couche();
cout << "done!" << endl;
cout << "Creation des neurones ... " << endl;
int nb = 0;
cout << "Combien de neurones d'entree ? " ;
cin >> nb;
cout << endl;
for (int i = 0; i < nb; i++)
{
Neurone* n = new Neurone(0.0);
cDepart->addNeurone(n);
}
cout << "Combien de neurones dans la couche cachee ? " ;
cin >> nb;
cout << endl;
for (int i = 0; i < nb; i++)
{
Neurone* n = new Neurone(0.0);
cInter->addNeurone(n);
}
cout << "Combien de neurones de sortie ? " ;
cin >> nb;
cout << endl;
for (int i = 0; i < nb; i++)
{
Neurone* n = new Neurone(0.0);
cFin->addNeurone(n);
}
cout << "done!" << endl;
cout << "Création de la base d'apprentissage : " << endl;
bool continuer = true;
vector<Apprentissage*> lesAppr;
while(continuer)
{
Apprentissage* a = new Apprentissage();
lesAppr.push_back(a);
cout << "-- Debut apprentissage " << lesAppr.size() << endl;
for(unsigned int i = 0 ; i < cDepart->getNbNeurones() ; i++)
{
double val;
cout << "--- Valeur neurone entree " << i << " : ";
cin >> val;
cout << endl;
a->addEntree(val);
}
for(unsigned int i = 0 ; i < cFin->getNbNeurones() ; i++)
{
double val;
cout << "--- Valeur neurone sortie " << i << " : ";
cin >> val;
cout << endl;
a->addSortie(val);
}
cout << "-- Fin apprentissage " << lesAppr.size() << endl;
char rep;
cout << "Autre jeu de donnee (o: oui, autre : non)? ";
cin >> rep;
cout << endl;
if(rep!='o')
continuer = false;
}
cout << "done!" << endl;
cout << "Initialisation aléatoire des poids... ";
for(int i = 0 ; i < cDepart->getNbNeurones() ; i++)
{
for(int j = 0 ; j < cInter->getNbNeurones() ; j ++)
{
cDepart->getNeurone(i)->addPoids(getRand());
}
}
for(int i = 0 ; i < cInter->getNbNeurones() ; i++)
{
for(int j = 0 ; j < cFin->getNbNeurones() ; j ++)
{
cInter->getNeurone(i)->addPoids(getRand());
}
}
cout << "done!" << endl;
double erreur = 1.0;
int currentDonnee = 0;
cout << "Debut du cycle d'apprentissage" << endl;
while (erreur > 0.095) {
cout << "Installation des données d'apprentissage... ";
Apprentissage* a = lesAppr.at(currentDonnee);
for(int i = 0; i < cDepart->getNbNeurones() ; i ++)
{
cDepart->getNeurone(i)->setValeur(a->getEntree(i));
cDepart->getNeurone(i)->setSigmoide(a->getEntree(i));
}
for(int i = 0; i < cFin->getNbNeurones() ; i ++)
{
cFin->getNeurone(i)->setValeur(a->getSortie(i));
}
cout << "done!" << endl;
cout << "Propagation avant 1 ... ";
for(int i=0;i<cInter->getNbNeurones();i++){
double npot =0.0;
for(int j=0;j<cDepart->getNbNeurones();j++) {
npot += cDepart->getNeurone(j)->getSigmoide() * cDepart->getNeurone(j)->getPoids(i);
}
cInter->getNeurone(i)->setPotentiel(npot);
cInter->getNeurone(i)->setSigmoide(1/(1+exp((-1)*npot)));
}
cout << "Propagation avant 2 ... ";
for(int i=0;i<cFin->getNbNeurones();i++){
double npot =0.0;
for(int j=0;j<cInter->getNbNeurones();j++) {
npot += cInter->getNeurone(j)->getSigmoide() * cInter->getNeurone(j)->getPoids(i);
}
cFin->getNeurone(i)->setPotentiel(npot);
cFin->getNeurone(i)->setSigmoide(1/(1+exp((-1)*npot)));
}
cout << "done!" << endl;
cout << "Calcul de l'erreur ... ";
a->setErreur(cFin->calculErreur());
cout << "done!" << endl;
cout << "Signal d'erreur couche sortie ... ";
for(int i=0;i<cFin->getNbNeurones();i++){
cFin->getNeurone(i)->calculErreur();
}
cout << "done!" << endl;
cout << "Signal d'erreur couche cachee ... ";
for(int i=0;i< cInter->getNbNeurones();i++) {
Neurone* n = cInter->getNeurone(i);
double phy = n->getSigmoide() * (1 - n->getSigmoide());
double del = 0.0;
for(int j=0;j < n->getNbPoids();j++) {
del += cFin->getNeurone(j)->getErreur() * n->getPoids(j);
}
n->setErreur(phy * del);
}
cout << "done!" << endl;
cout << "Correction poids sortie... ";
for(int i=0;i<cInter->getNbNeurones();i++){
for(int j=0;j<cInter->getNeurone(i)->getNbPoids();j++) {
cInter->getNeurone(i)->setPoids(j,cInter->getNeurone(i)->getPoids(j) + cInter->getNeurone(i)->getSigmoide() * cFin->getNeurone(j)->getErreur());
}
}
/cout << "done!" << endl;
cout << "Correction poids cachee... ";
for(int i=0;i<cDepart->getNbNeurones();i++){
for(int j=0;j<cDepart->getNeurone(i)->getNbPoids();j++) {
cDepart->getNeurone(i)->setPoids(j,cDepart->getNeurone(i)->getPoids(j) + cDepart->getNeurone(i)->getSigmoide() * cInter->getNeurone(j)->getErreur());
}
}
cout << "done!" << endl;
currentDonnee = ( currentDonnee + 1 ) % lesAppr.size();
double totErr = 0;
for(int i = 0 ; i < lesAppr.size(); i++)
{
totErr += lesAppr.at(i)->getErreur();
}
erreur = totErr / lesAppr.size();
cout << "Moyenne erreur : " << erreur << endl;
}
cout << "Fin de l'apprentissage !" << endl;
//Affichage de la sortie
cout << "Valeur de sortie finale : " << endl;
for (int i = 0; i < cFin->getNbNeurones(); i++)
{
cout << cFin->getNeurone(i)->getSigmoide() << endl;
}
cout << "OK" << endl;
//Demarrage de test
cout << "*********************************" << endl << endl;
cout << "Test de l'apprentissage : " << endl;
continuer = true;
while(continuer)
{
for (int i = 0 ; i < cDepart->getNbNeurones() ; i++)
{
double val;
cout << "--- Valeur neurone entree " << i << " : ";
cin >> val;
cout << endl;
cDepart->getNeurone(i)->setValeur(val);
cDepart->getNeurone(i)->setSigmoide(val);
}
cout << "Passage dans le reseau de neurone... ";
for(int i=0;i<cInter->getNbNeurones();i++){
double npot =0.0;
for(int j=0;j<cDepart->getNbNeurones();j++) {
npot += cDepart->getNeurone(j)->getSigmoide() * cDepart->getNeurone(j)->getPoids(i);
}
cInter->getNeurone(i)->setPotentiel(npot);
cInter->getNeurone(i)->setSigmoide(1/(1+exp((-1)*npot)));
}
for(int i=0;i<cFin->getNbNeurones();i++){
double npot =0.0;
for(int j=0;j<cInter->getNbNeurones();j++) {
npot += cInter->getNeurone(j)->getSigmoide() * cInter->getNeurone(j)->getPoids(i);
}
cFin->getNeurone(i)->setPotentiel(npot);
cFin->getNeurone(i)->setSigmoide(1/(1+exp((-1)*npot)));
}
cout << "done!" << endl;
cout << "Affichage des resultats : " << endl;
for (int i = 0; i < cFin->getNbNeurones(); i++)
{
cout << "Valeur de sortie neurone " << i << " : " << cFin->getNeurone(i)->getSigmoide() << endl;
}
char rep;
cout << "Autre test (o: oui, autre : non)? ";
cin >> rep;
cout << endl;
if(rep!='o')
continuer = false;
}
return 0;
}
double getRand()
{
return (double)rand()/(RAND_MAX)-0.5;
}
Donc au début je demande combien il faut de neurones en entrée, dans la couche cachée, puis en sortie (pour l'instant j'ai codé que pour une seule couche cachée).
Ensuite je démarre la saisie de la base d'apprentissage, donc pour chaque exemple, les valeurs des neurones en entrée, et les resultats désirés en sortie.
Lorsque tous les jeux d'exemple sont entrés, on initialise les poids entre les neurones avec des valeurs aléatoires entre -0.5 et +0.5.
Ensuite on entre dans la boucle d'apprentissage :
-Propagation avant
-Calcul de l'erreur
-Calcul du signal d'erreur sur la couche de sortie
-Calcul du signal d'erreur sur la couche cachée
-Correction des poids synaptiques de la couche de sortie
-Correction des poids synaptiques de la couche cachée
On le répète pour chaque donnée d'apprentissage, et ce jusqu'a ce que la moyenne des erreurs constatée par jeu passe en dessous d'un certain seuil.
Ensuite, on demande des valeurs d'entrée afin de tester ce réseau, et la réponse doit converger vers ce qu'on attend.
Alors l'exercice prévoit que le test se fera avec la fonction XOR. Donc les jeux d'apprentissage sont au nombre de 4 :
0 . 0 -> 0
0 . 1 -> 1
1 . 0 -> 1
1 . 1 -> 0
Je test ca avec donc 2 neurones en entrée, 1 seul en sortie, et 1 dans la couche cachée.
Et c'est la qu'intervient le problème.
La boule d'apprentissage étant terminée, je teste 0 . 0 en entrée, et j'ai un résultat qui converge bien vers 0.
En revanche, pour les 3 autres tests (0.1, 1.0, 1.1) le neurone de sortie converge a chaque fois vers 0.5.
Donc je comprend pas pourquoi ca me fait ca...
Si quelqu'un de calé la dessus peut m'aider, je lui en serait reconnaissant!
PS : désolé pour tous les cout MAis comme ca ils servent aussi de commentaire presque
EDIT : je viens de me rendre compte que ce soucis (convergence vers 0.5) n'est valable que quand je choisis moins de 3 neurones dans la couche cachée. Quand j'en mets au moins , tout se déroule pour le mieux. WTF????
Excellente implémentation. C'est rare de voir un code source bien ficelé ! Souvent dans la théorie ça l'est... dans la pratique, sauf les bibliothèques d'envergure, sinon c'est le bordel.
Deux choses par contre :
Ajoute le pas êta lors de la correction des poids synaptiques
Il te faudra détruire tes objets dans les conteneurs (Vector) car tu stocke des pointeurs. Attention à la fuite mémoire ! Vous pouvez faire
for(vector<Neurone*>::iterator i = listNeurones.begin(); i != listNeurones.end();++i)
delete *i;
Pour bien faire, il faudrait étendre la fonction d'activation de tes neurones à autre chose que la fonction sigmoïde
Bravo en tout cas
- Edité par Spleeen 27 octobre 2013 à 16:38:45
Il n'y pas de questions stupides, juste des gens stupides !
Pourquoi tout faire par allocation dynamique ? (qui plus est, sans pointeur intelligents ?). Un peu de const-correctness ne ferait pas de mal non plus.
il te faut des biais, fait les limites de ta sigmoïde celle-ci tend vers 0.5 pour x=0 , il faut donc modifier le biais dans ton cas.
edit: bon j'ai répondu rapidement donc je reviens un peut plus en profondeur.
Je reviendrais sur les biais après, pour le moment il te manque un neurone en couche caché. En effet tu pourras pas résoudre un XOR que avec un seul neurone caché, il t'en faut dans le plus simple des cas avec 2 entrées au moins 2 aussi en couche caché..
Ensuite pour mieux comprendre un XOR représente le sur un plan 2D, alors tu verra que pour l'isolé il te faudra 2 courbes , donc 2 neurone caché accompagné de un biais chacun , comme tu en a qu'un il est sur une position indéterminé (0.5) .
dans ton cas tu utilise un apprentissage, et celui-ci en plus de mettre a jour, les poids peut aussi mettre a jours les biais.
En général pour plus de souplesse on attribut les biais aux neurones caché mais, ils peuvent aussi être attribué aux sorties.
Tu devrais utilisé un fonction Heaviside ou sigmoïde avec un biais de 0.5 sur ta sortie
Et enfin comme dit précédemment ajoute le pas êta lors de la correction des poids synaptiques qui est essentiel pour contrôler la décente de gradient et évite les apprentissages trop long ou instable
ici un pas de 0.1 voir 0.5 est suffisant
maintenant petit conseil, pense sous forme de matrice, car le jour ou tu voudra avancer dans ce domaine c'est en réalisant les calcules par matrice grâce a la gpu qu'il faudra faire, ton cpu meme avec multi thread vas très vite galérer a traiter les réseaux de neurones surtout en apprentissage . Oui car on est sur un progression du temps de calcul qui dépend du nombre de connexions mais de manière exponentiel.
- Edité par Matheau85gyque 23 février 2018 à 18:50:24
Perceptron multicouches
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Il n'y pas de questions stupides, juste des gens stupides !
Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C
Il n'y pas de questions stupides, juste des gens stupides !
Posez vos questions ou discutez informatique, sur le Discord NaN | Tuto : Preuve de programmes C