Mauvais resultats

a marqué ce sujet comme résolu.

Bonjour ! Je viens a vous car j’ai implémenté mon premier reseau de neurones suite a ce tutoriel bien foutu : https://www.youtube.com/watch?v=KkwX7FkLfug.

Par contre … que du mauvais resultats. Que j’itere 200 ou 20 000 fois sur mon dataset j’ai toujours les meme resultats : mes 4 neurones de sorties qui valent -9.25596e+64 ..

Avec quelques std::cout , je remarque que chacune de mes neurones d’une couche ont la meme valeur … ce qui est étrange vu que mes poids sont initialisées aléatoirement .... je continu de chercher la cause du probleme…

Pour commencer voici mon main :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
#include <iostream>
#include "E:/NeuralNetwork/NeuralNetwork.hpp"

int main(void) {

    // Topologie du réseau
    NeuralNetwork::Topology topologie{ 4, 4, 4 , 8 , 4 };

    NeuralNetwork Brain{ topologie };

    // Dataset d'entrée
    std::vector<NeuralNetwork::DataSet> dataset_;

    NeuralNetwork::DataSet datas_p1{0,0,
                                    0,0}; // bloc plein

    NeuralNetwork::DataSet datas_p2{1,1,
                                    1,1}; // bloc plein

    NeuralNetwork::DataSet datas_v1{1,0,
                                    1,0}; // Vertical

    NeuralNetwork::DataSet datas_v2{0,1,
                                    0,1}; // Vertical

    NeuralNetwork::DataSet datas_h1{1,1,
                                    0,0}; // Horizontal

    NeuralNetwork::DataSet datas_h2{0,0,
                                    1,1}; // Horizontal

    NeuralNetwork::DataSet datas_d1{1,0,
                                    0,1}; // Diagonal

    NeuralNetwork::DataSet datas_d2{0,1,
                                    1,0}; // Diagonal

    dataset_.push_back(datas_p1);
    dataset_.push_back(datas_p2);
    dataset_.push_back(datas_v1);
    dataset_.push_back(datas_v2);
    dataset_.push_back(datas_h1);
    dataset_.push_back(datas_h2);
    dataset_.push_back(datas_d1);
    dataset_.push_back(datas_d2);

    std::vector<NeuralNetwork::DataSet> expected_output{ 
        { 1,0,0,0 },
        { 1,0,0,0 },
        { 0,1,0,0 },
        { 0,1,0,0 },
        { 0,0,1,0 },
        { 0,0,1,0 },
        { 0,0,0,1 },
        { 0,0,0,1 },
    };

    for (size_t i = 0; i < 500 ; i++) {
        for (size_t d = 0; d < dataset_.size(); d++) {
            Brain.feedForward(dataset_[d]);
            Brain.backPropagate(expected_output[d]);
        }
    }

    NeuralNetwork::ResultSet results;

    Brain.feedForward(datas_d1);
    Brain.getResults(results);

    for (auto const & r : results) {
        std::cout << r << std::endl;
    }

    std::cin.get();

    return 0;
}

Puis : NeuralNetwork.hpp

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#ifndef NEURALNETWORK_HPP
#define NEURALNETWORK_HPP

#include <vector>
#include <utility>
#include <random>
#include <assert.h>

class NeuralNetwork{

    public:

        class Node;

        // definition de quelques types
        using DataSet = std::vector<double>;
        using ResultSet = std::vector<double>;
        using Topology = std::vector<size_t>; // Nombre de couches ( layer ) et de neurones par couche
        using Layer = std::vector<Node>; // Couche de Node ( neurone )

        // Defini une connection jusqu'a une neurone de la couche suivante par un poid
        struct Connection{

            // Constructeur par defaut
            Connection() :
                Connection{0.0}
            {}

            // Constructeur
            Connection(double w) :
                weigth_{w},
                delta_weigth_{0.0}
            {}

            double weigth_; // Poid de la connection
            double delta_weigth_;
        };

        // Defini une neurone
        class Node{

            public:
                double eta_; //training rate
                double alpha_;

                // Constructeur
                Node(size_t output_size, size_t num){
                    // output_size = nombre de connection a la couche suivante
                    for(size_t i = 0; i < output_size; i++){
                        // Pour chaque neurone de la couche suivante , on ajoute une connection a la neurone
                        connections_.push_back(Connection(randomWeigth()));
                    }

                    nodenum_ = num;
                    eta_ = 0.15;
                    alpha_ = 0.5;
                };

                void feedForward(const NeuralNetwork::Layer & prev_layer) {

                    double sum = 0.0;

                    for (size_t n = 0; n < prev_layer.size(); n++) {
                        sum += (prev_layer[n].getOutput() * prev_layer[n].connections_[nodenum_].weigth_);
                    }

                    output_ = Node::activationFunction(sum);
                }

                void setOutput(double value) { output_ = value; }
                double getOutput() const { return output_; } 

                void calculateGradients(double value) {
                    double delta = value - output_;
                    gradient_ = delta * Node::activationFunction(output_);
                };

                double sumDOW(Layer const & layer) {

                    double sum = 0.0;
                    for (size_t n = 0; n < layer.size() - 1; n++) {
                        sum += connections_[n].weigth_;
                    }

                    return sum;
                }

                void updateWeigths(Layer &prev_layer) {

                    for (size_t n = 0; n < prev_layer.size(); n++) {

                        Node & node = prev_layer[n];

                        double oldDelta = node.connections_[nodenum_].delta_weigth_;
                        double newDelta = (eta_ * node.getOutput() * gradient_) + (alpha_ * oldDelta);

                        node.connections_[nodenum_].delta_weigth_ = newDelta;
                        node.connections_[nodenum_].weigth_ = newDelta;
                    }
                }

                void calculateHiddenGradients(Layer const & layer) {
                    double dow = sumDOW(layer);
                    gradient_ = dow * Node::activationFunction(output_);
                }

            private:

                double output_; // Valeur de sorti de la neurone
                size_t nodenum_;
                std::vector<Connection> connections_;
                double gradient_;

                static double randomWeigth() { return rand() / double(RAND_MAX); }

                static double activationFunction(double sum) {
                    return tanh(sum);
                }

        }; // Une neurone

        // Constructeur du réseau
        NeuralNetwork(Topology const &topo){

            for(size_t j = 0; j < topo.size(); j++){

                // Nombre de connections pour les neurones de cette couche
                // si c'est la derniere couche il y a 0 connections
                // sinon le nombre de connections = le nombre de neurones sur la prochaine couche
                auto conn_size = ( j == topo.size() - 1 ? 0 : topo[j+1]);

                // On ajoute une couche
                layers_.push_back(Layer());

                auto & last_layer_added = layers_.back();

                auto layer_size = topo[j];

                //Pour chaque nombre de neurone dans la couche
                for(size_t i = 0; i <= layer_size ; i++){
                    // On ajoute une neurone avec le nombre de connections necessaire
                    last_layer_added.push_back(Node(conn_size,i));
                }

                last_layer_added.back().setOutput(1.0);
            }
        };

        // FONCTION FEEDFORWARD
        void feedForward(DataSet const & inputs){

            // Nombre de données == nombre entré premiere couche
            assert(inputs.size() == layers_[0].size() - 1);

            // Entrée des données dans la premiere couche
            for (size_t i = 0; i < inputs.size(); i++) {
                layers_[0][i].setOutput(inputs[i]);
            }

            // Feed des couches cachées
            for (size_t layer_num = 1; layer_num < layers_.size() - 1; layer_num++) {

                // Récuperation de la couche précédente
                Layer &prev_layer = layers_[layer_num - 1];

                // Feed forward des données 
                for (size_t n = 0; n < layers_[layer_num].size() - 1; n++) {
                    layers_[layer_num][n].feedForward(prev_layer);
                }
            }
        }

        // FONCTION BACKPROPAGATE
        void backPropagate(DataSet const & inputs){

            // derniere couche = couche de sortie
            Layer &outputLayer = layers_.back();

            error_ = 0.0;

            for (size_t n = 0; n < outputLayer.size() - 1; n++) {
                double delta = inputs[n] - outputLayer[n].getOutput();
                error_ += delta*delta;
            }

            error_ /= outputLayer.size() - 1;
            error_ = sqrt(error_);

            recentAverageError_ = (recentAverageError_ * error_) / (1.0);

            for (size_t n = 0; n < outputLayer.size() - 1; n++) {
                outputLayer[n].calculateGradients(inputs[n]);
            }

            for (size_t layer = layers_.size() - 2; layer = 0; layer++) {

                Layer & hiddenLayer = layers_[layer];
                Layer & nextLayer = layers_[layer + 1];

                for (size_t n = 0; n < hiddenLayer.size()-1; n++) {
                    hiddenLayer[n].calculateHiddenGradients(nextLayer);
                }
            }

            for (size_t layer_num = layers_.size() - 1; layer_num > 0; layer_num--) {
                Layer &layer = layers_[layer_num];
                Layer &prev_layer = layers_[layer_num - 1];

                for (size_t n = 0; n < layer.size()-1; n++) {
                    layer[n].updateWeigths(prev_layer);
                }

            }
        }
        void getResults(ResultSet & results) const {
            results.clear();

            for (size_t n = 0; n < layers_.back().size() - 1; n++) {
                results.push_back(layers_.back()[n].getOutput());
            }
        }

    private:
        std::vector<Layer> layers_; // layers_[LayerNum][NodeNum]
        double error_;
        double recentAverageError_;
};

#endif

`

Et la sortie :

1
2
3
4
-9.2559e+61
-9.2559e+61
-9.2559e+61
-9.2559e+61

Si vous avez une idée d’ou cela peux venir …

+0 -0

Tu as toujours -9.2559e+61, même lors de la première itération ? Tes poids ne sont pas initialisés de façon aléatoire ?

Vayel

Oui ils le sont , je viens de verifier. Maintenant je vais verifier la valeur d echaques couches de neurones / neurones jusqu’a la derniere.

Avant meme la premiere iteration , ma derniere couche ( couche de sortie ) vaut deja -9.xxxxxx .... probablbment une erreur d’indice pendant une initialisation

EDIT :: Probleme regler , comme de fait c’est dans ma fonction feedForward , derniere double for : je bouclais jusqua < layers_size() - 1 donc je manquais la derniere couche !

Pour ce probleme c’est regler , mais le resultat toujours faux ..

Pour la sortie de mon main :

0 0 -0.98 0

Ce qui devrait etre 0 0 0 1

edit 3294432 : maintenant mes valeurs sont : soit 0 0 0 0 ou des valeur negatives aleatoires ( -6.xxxxx+e10 ) en dessous de 100 iteration je me retrouve avec des valeur astronomiques et apres avec que des 0 .....

+0 -0

J’ai largement pas le temps de regarder. Le plus probable est un bug dans ton code. Sinon une mauvaise fonction de coût mais bon.

Ceci dit j’ai un peu de mal à voir l’intérêt de coder ça soit même. Il y a pleins de problèmes d’implémentation bien chiant sur ce genre de système. Vu que tu fais du c++, autant utiliser une lib existente comme tensorflow.

Il y a déjà bien des choses à régler quand tu utilise une lib, tu va pas t’ennuyer.

J’ai largement pas le temps de regarder. Le plus probable est un bug dans ton code. Sinon une mauvaise fonction de coût mais bon.

Ceci dit j’ai un peu de mal à voir l’intérêt de coder ça soit même. Il y a pleins de problèmes d’implémentation bien chiant sur ce genre de système. Vu que tu fais du c++, autant utiliser une lib existente comme tensorflow.

Il y a déjà bien des choses à régler quand tu utilise une lib, tu va pas t’ennuyer.

Kje

Bien d’accord , mais l’interet est d’apprendre et passer le temps pendant mes temps libre ;) si deja je peut faire fonctionner un reseau de neurone simple .. je me pencherai sur une lib pour un probleme plus gros.

Ce que tu pourrais faire, c’est coder ton réseau avec Tensorflow pour voir si tu obtiens le même problème. En outre, avec Tensorboard tu pourras analyser le comportement du réseau et peut-être en déduire ce qui cloche dans ton implémentation.

Comme ça, tu apprends à utiliser Tensorflow et tu as les deux points de vue : haut niveau avec la bibliothèque et plus bas niveau avec ton code.

+1 -0

Ce que tu pourrais faire, c’est coder ton réseau avec Tensorflow pour voir si tu obtiens le même problème. En outre, avec Tensorboard tu pourras analyser le comportement du réseau et peut-être en déduire ce qui cloche dans ton implémentation.

Comme ça, tu apprends à utiliser Tensorflow et tu as les deux points de vue : haut niveau avec la bibliothèque et plus bas niveau avec ton code.

Vayel

Super idée ! Merci je met ca sur ma liste ! Car pour l’instant tout mes poids/valeur tendent vers 0 sans aller vers la bonne solution ....

edit en fait si !!!!! Dans mon main , mes expected outputs sont entre 1 et 0 mais la sortie de mon reseaux entre -1 et 1 donc il tend .. vers les 0 j’imagine.

+0 -0

Je comprends l’idée de faire pour comprendre mais il y a plusieurs niveaux. Tu pourrais déjà utiliser une lib de calcul pour faire à ta place tous les calculs d’algèbre linéaire. Fondamentalement une grosse partie ce n’est que du calcul matriciel.

C’est simplement que le sujet est déjà vaste et complexe quand tu utilise des lib pensé pour, alors se taper en plus tous les problèmes de l’implémentation bas niveau…

Tu n’es pas obligé d’utiliser keras qui est très haut niveau. Même en utilisant tensorflow tu n’es pas obligé toutes les briques preconstruites. Mais au moins utiliser se genre de lib t’évitera les bugs, les problèmes de conditionnement, de saturation des flottants, etc.

Pour moi il est plus formateur de construire un premier réseau qui traite de vrai donnés que de vouloir coder très bas niveau avec si peu de donnés. Car aujourd’hui tout l’intérêt de ce genre de système c’est pour résoudre des combinatoires complexes. Sinon il y a beaucoup plus simple. C’est d’autant plus vrai qu’en plus tu a une implémentation pas du tout réaliste. On modélise pas les neurones indépendamment. Chaque couche est constitué de matrices et de vecteurs de poids. Et le calcul est représenté par un graph pour pouvoir calculer le gradient de façon générique.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte