用C ++发明自行车或编写感知器。第1部分和第2部分

用C ++发明自行车或编写感知器。第1部分



让我们编写一个简单的库,用于在C ++中实现感知器







介绍



大家好,在这篇文章中,我想与您分享我在编写神经网络方面的第一次经验。互联网上有很多关于神经网络(未来的神经网络)实现的文章,但是我不想在不了解他人工作本质的情况下使用其他人的算法,因此我决定从头开始创建自己的代码。



在这一部分中,我将介绍配合的要点。对我们有用的部分。整个理论取自各个站点,主要来自维基百科。



使用学习算法链接到第三部分:habr.com/ru/post/514626



所以,让我们开始吧。





一点理论



让我们同意,我并没有要求“最佳机器学习算法”的称号,只是在展示我的实现和想法。另外,我总是对代码有建设性的批评和建议,这很重要,这是社区存在的目的。



, .





. .



. :






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



, , .








, .



. , , , . , .



好吧,我设法解释了在神经元中存储必要值的原理。现在让我们弄清楚如何存储神经元之间连接的权重。



以这个网络为例:

...





已经知道如何在记忆中构造神经元,让我们为重量创建一个类似的表:









它的结构一点也不复杂:例如,神经元N1和神经元n1之间的权重值与其他权重类似地包含在单元w1-1中。但是同样,这样的矩阵仅适用于存储前两层之间的权重,但是第二层和第三层之间的网络中仍然存在权重。让我们使用已经熟悉的技巧-向数组添加新维度,但要注意一点:让行名称显示相对于权重“束”的左侧神经元层,而右侧的神经元层适合列名称。



然后我们获得下表中的第二个“捆绑”重量:







:







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





, .



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


最后,我要谈的最后一件事是显示结果的功能。好了,这里我们只是将最后一层神经元的值复制到作为参数传递给我们的数组中:




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


夕阳西下



我们将在此止步,下一部分将专门介绍一个功能,可让您训练网络。由于数学的复杂性和丰富性,我决定将其分开进行,我们还将在其中测试整个图书馆的整体工作。



再次,我欢迎您在评论中提出建议和意见。



谢谢您对本文的关注,很快见!



PS:如所承诺-链接到源:GitHub








All Articles