Inventar una bicicleta o escribir un perceptrón en C ++. Parte 1 y 2

Inventar una bicicleta o escribir un perceptrón en C ++. Parte 1



Escribamos una biblioteca simple para implementar un perceptrón en C ++







Introducción



Hola a todos, en este post quiero compartir con ustedes mi primera experiencia en la escritura de redes neuronales. Hay muchos artículos sobre la implementación de redes neuronales (redes neuronales en el futuro) en Internet, pero no quiero usar algoritmos de otras personas sin comprender la esencia de su trabajo, así que decidí crear mi propio código desde cero.



En esta parte describiré los puntos principales del mate. piezas que nos serán de utilidad. Toda la teoría está tomada de varios sitios, principalmente de Wikipedia.



Enlace a la tercera parte con el algoritmo de aprendizaje: habr.com/ru/post/514626



Entonces, vamos.





Un poco de teoría



Aceptemos que no pretendo el título de "el mejor algoritmo de aprendizaje automático", solo estoy mostrando mi implementación y mis ideas. Además, siempre estoy abierto a críticas constructivas y consejos sobre el código, esto es importante, para eso existe la comunidad.



, .





. .



. :






, (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» .



, , .








, .



. , , , . , .



Bueno, logré explicar los principios para almacenar los valores necesarios en las neuronas. Ahora descubramos cómo almacenar los pesos de las conexiones entre neuronas.



Tome la siguiente red, por ejemplo:

...





Sabiendo ya cómo estructurar las neuronas en la memoria, hagamos una tabla similar para pesos:









Su estructura no es nada complicada: por ejemplo, el valor del peso entre la neurona N1 y la neurona n1 está contenido en la celda w1-1, al igual que otros pesos. Pero, nuevamente, dicha matriz es adecuada para almacenar pesos solo entre las dos primeras capas, pero todavía hay pesos en la red entre la segunda y la tercera capa. Usemos el truco ya familiar: agregue una nueva dimensión a la matriz, pero con una advertencia: deje que los nombres de las líneas muestren la capa de neuronas a la izquierda en relación con el "paquete" de pesos, y la capa de neuronas a la derecha encaja en los nombres de columna.



Luego obtenemos la siguiente tabla para el segundo "paquete" de pesos:







:







«», .. , , « » , - , . )).





, .



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);
		}
	}
}


Y finalmente, lo último de lo que me gustaría hablar es de la función para mostrar el resultado. Bueno, aquí simplemente copiamos los valores de las neuronas de la última capa en la matriz que se nos pasa como parámetro:




void NeuralNet::getResult(uint16_t size, double* data) {
	for (uint16_t r = 0; r < size; r++) {
		data[r] = neurons[r][1][numLayers - 1];
	}
}


Entrando en la puesta de sol



Nos detendremos en esto, la siguiente parte estará dedicada a una única función que le permite entrenar la red. Debido a la complejidad y abundancia de las matemáticas, decidí sacarlo en una parte separada, donde también probaremos el trabajo de toda la biblioteca como un todo.



Una vez más, agradezco sus consejos y comentarios en los comentarios.



Gracias por su atención al artículo, ¡hasta pronto!



PD: como prometí - enlace a fuentes: GitHub








All Articles