Inventer un vélo ou écrire un perceptron en C ++. Partie 1
Écrivons une bibliothèque simple pour implémenter un perceptron en C ++
introduction
Bonjour à tous, dans ce post je souhaite partager avec vous ma première expérience dans l'écriture de réseaux de neurones. Il y a beaucoup d'articles sur la mise en œuvre des réseaux de neurones (réseaux de neurones dans le futur) sur Internet, mais je ne veux pas utiliser les algorithmes d'autres personnes sans comprendre l'essence de leur travail, j'ai donc décidé de créer mon propre code à partir de zéro.
Dans cette partie, je décrirai les principaux points de mate. des pièces qui nous seront utiles. Toute la théorie est tirée de divers sites, principalement de Wikipedia.
Lien vers la 3ème partie avec l'algorithme d'apprentissage: habr.com/ru/post/514626
Alors, allons-y.
Un peu de théorie
Admettons que je ne revendique pas le titre de "meilleur algorithme d'apprentissage automatique", je montre simplement ma mise en œuvre et mes idées. Aussi, je suis toujours ouvert aux critiques constructives et aux conseils sur le code, c'est important, c'est pour cela que la communauté existe.
, .
. .
. :
, (1, 2, 3), u (w1, w2, w3), :
u = x1*w1 + x2*w2 + x3*w3
:
. y(u), u – . , .
, , . , , . , – (, ). :
(0; 1), . y(u) .
, . , .
, , .
, . , , ( ).
, 2 : , .
:
8 ( n1 n8), u, «y(u)» «err», (). «err» .
, , .
, .
. , , , . , .
Eh bien, j'ai réussi à expliquer les principes de stockage des valeurs nécessaires dans les neurones. Voyons maintenant comment stocker les poids des connexions entre les neurones.
Prenons l'exemple du réseau suivant:
...Sachant déjà comment structurer les neurones en mémoire, faisons un tableau similaire pour les poids:
Sa structure n'est pas du tout compliquée: par exemple, la valeur du poids entre le neurone N1 et le neurone n1 est contenue dans la cellule w1-1, de même avec d'autres poids. Mais encore une fois, une telle matrice est adaptée pour stocker des poids uniquement entre les deux premières couches, mais il y a encore des poids dans le réseau entre les deuxième et troisième couches. Utilisons l'astuce déjà familière - ajoutez une nouvelle dimension au tableau, mais avec une mise en garde: laissez les noms de ligne afficher la couche de neurones à gauche par rapport au «faisceau» de poids, et la couche de neurones à droite s'insère dans les noms de colonne.
Ensuite, nous obtenons le tableau suivant pour le deuxième «paquet» de poids:
:
«», .. , , « » , - , . )).
, .
C++. 2
, .
, . .
, !
header —
, . header — ( «neuro.h»). :
class NeuralNet {
public:
NeuralNet(uint8_t L, uint16_t *n);
void Do_it(uint16_t size, double *data);
void getResult(uint16_t size, double* data);
void learnBackpropagation(double* data, double* ans, double acs, double k);
private:
vector<vector<vector<double>>> neurons;
vector<vector<vector<double>>> weights;
uint8_t numLayers;
vector<double> neuronsInLayers;
double Func(double in);
double Func_p(double in);
uint32_t MaxEl(uint16_t size, uint16_t *arr);
void CreateNeurons(uint8_t L, uint16_t *n);
void CreateWeights(uint8_t L, uint16_t *n);
};
, , header' ). :
// , ,
#ifndef NEURO_H
#define NEURO_H
#include <vector> //
#include <math.h> // ,
#include <stdint.h> // , .
:
NeuralNet(uint8_t L, uint16_t *n);
, , - .
void Do_it(uint16_t size, double *data);
)), .
void getResult(uint16_t size, double* data);
.
void learnBackpropagation(double* data, double* ans, double acs, double k);
, .
, :
vector<vector<vector<double>>> neurons; // ,
vector<vector<vector<double>>> weights; // ,
uint8_t numLayers; //
vector<double> neuronsInLayers; //,
/*
, , , ,
*/
double Func(double in); //
double Func_p(double in); //
uint32_t MaxEl(uint16_t size, uint16_t *arr);//
void CreateNeurons(uint8_t L, uint16_t *n);//
void CreateWeights(uint8_t L, uint16_t *n);
header — :
#endif
header . — source — ).
source —
, .
:
NeuralNet::NeuralNet(uint8_t L, uint16_t *n) {
CreateNeurons(L, n); //
CreateWeights(L, n); //
this->numLayers = L;
this->neuronsInLayers.resize(L);
for (uint8_t l = 0; l < L; l++)this->neuronsInLayers[l] = n[l]; //
}
, :
void NeuralNet::Do_it(uint16_t size, double *data) {
for (int n = 0; n < size; n++) { //
neurons[n][0][0] = data[n]; //
neurons[n][1][0] = Func(neurons[n][0][0]); //
}
for (int L = 1; L < numLayers; L++) { //
for (int N = 0; N < neuronsInLayers[L]; N++) {
double input = 0;
for (int lastN = 0; lastN < neuronsInLayers[L - 1]; lastN++) {//
input += neurons[lastN][1][L - 1] * weights[lastN][N][L - 1];
}
neurons[N][0][L] = input;
neurons[N][1][L] = Func(input);
}
}
}
Et enfin, la dernière chose dont je voudrais parler est la fonction d'affichage du résultat. Eh bien, ici, nous copions simplement les valeurs des neurones de la dernière couche dans le tableau qui nous est passé en paramètre:
void NeuralNet::getResult(uint16_t size, double* data) {
for (uint16_t r = 0; r < size; r++) {
data[r] = neurons[r][1][numLayers - 1];
}
}
Aller au coucher du soleil
Nous nous arrêterons là-dessus, la partie suivante sera consacrée à une seule fonction qui vous permet de former le réseau. En raison de la complexité et de l'abondance des mathématiques, j'ai décidé de le sortir dans une partie séparée, où nous testerons également le travail de toute la bibliothèque dans son ensemble.
Encore une fois, j'apprécie vos conseils et commentaires dans les commentaires.
Merci pour votre attention sur l'article, à bientôt!
PS: Comme promis - lien vers les sources: GitHub