C / C ++中简单神经网络的示例

大家好

在我看来,我决定在C ++中共享一个简单而功能强大的神经网络解决方案。

为什么这些信息会很有趣?

答:我试图以最小的一组对多层感知器的工作进行编程,以便可以按照我的意愿在几行代码中对其进行配置,并且用于“ C”的基本算法的实现将使您能够轻松地将面向语言的语言转换为“ C”(在以及任何其他), 而无需使用第三方库!

请看看它带来了什么


我不会告诉您神经网络用途 ,希望您没有被Google禁止,并且可以找到您感兴趣的信息(用途,功能,应用程序等)。

您将在本文的末尾找到源代码 ,但目前为止,请按顺序进行。

让我们开始分析


1)架构和技术细节


- 多层感知器 ,能够配置给定宽度的任意数量的层。 下面介绍

配置示例
myNeuero.cpp

inputNeurons = 100; //   outputNeurons =2; //   nlCount = 4; //  (    3,      1 list = (nnLay*) malloc((nlCount)*sizeof(nnLay)); inputs = (float*) malloc((inputNeurons)*sizeof(float)); targets = (float*) malloc((outputNeurons)*sizeof(float)); list[0].setIO(100,20); //  INPUTS/OUTPUTS    list[1].setIO(20,6); // -//- list[2].setIO(6,3); // -//- list[3].setIO(3,2); // -//-   


请注意,设置每一层的输入和输出的宽度是根据一定的规则执行的-当前层的输入=上一层的输出。 输入层是一个例外。

因此,您有机会在编译之前或编译之后从源文件读取数据时,手动或根据给定规则配置任何配置。

-实现错误反向传播机制并设置学习速度

myNeuero.h

  #define learnRate 0.1 

-安装初始砝码

myNeuero.h

  #define randWeight (( ((float)qrand() / (float)RAND_MAX) - 0.5)* pow(out,-0.5)) 

注意 :如果存在三层以上(nlCount> 4),则需要增加pow(out,-0.5),以便当信号直接通过时,其能量不会减少到0。示例pow(out,-0.2)

-C中代码基础。基本算法和加权系数的存储在C中作为一个结构实现,其他所有内容都是该结构的调用函数的外壳,也反映了单独使用的任何层

层结构
myNeuero.h

  struct nnLay{ int in; int out; float** matrix; float* hidden; float* errors; int getInCount(){return in;} int getOutCount(){return out;} float **getMatrix(){return matrix;} void updMatrix(float *enteredVal) { for(int ou =0; ou < out; ou++) { for(int hid =0; hid < in; hid++) { matrix[hid][ou] += (learnRate * errors[ou] * enteredVal[hid]); } matrix[in][ou] += (learnRate * errors[ou]); } }; void setIO(int inputs, int outputs) { in=inputs; out=outputs; hidden = (float*) malloc((out)*sizeof(float)); matrix = (float**) malloc((in+1)*sizeof(float)); for(int inp =0; inp < in+1; inp++) { matrix[inp] = (float*) malloc(out*sizeof(float)); } for(int inp =0; inp < in+1; inp++) { for(int outp =0; outp < out; outp++) { matrix[inp][outp] = randWeight; } } } void makeHidden(float *inputs) { for(int hid =0; hid < out; hid++) { float tmpS = 0.0; for(int inp =0; inp < in; inp++) { tmpS += inputs[inp] * matrix[inp][hid]; } tmpS += matrix[in][hid]; hidden[hid] = sigmoida(tmpS); } }; float* getHidden() { return hidden; }; void calcOutError(float *targets) { errors = (float*) malloc((out)*sizeof(float)); for(int ou =0; ou < out; ou++) { errors[ou] = (targets[ou] - hidden[ou]) * sigmoidasDerivate(hidden[ou]); } }; void calcHidError(float *targets,float **outWeights,int inS, int outS) { errors = (float*) malloc((inS)*sizeof(float)); for(int hid =0; hid < inS; hid++) { errors[hid] = 0.0; for(int ou =0; ou < outS; ou++) { errors[hid] += targets[ou] * outWeights[hid][ou]; } errors[hid] *= sigmoidasDerivate(hidden[hid]); } }; float* getErrors() { return errors; }; float sigmoida(float val) { return (1.0 / (1.0 + exp(-val))); } float sigmoidasDerivate(float val) { return (val * (1.0 - val)); }; }; 


2)申请


使用mnist集测试该项目成功,我们成功实现了0.9795的条件手写识别概率(nlCount = 4,learnRate = 0.03和几个时代)。 该测试的主要目的是测试与之应对的神经网络的性能。

下面我们考虑有关“条件任务”的工作

源数据:

-2个100个值的随机输入向量
随机生成权重的神经网络
-2设定目标

main()函数中的代码

 { //!!!________    qDebug()   std::cout  std::cerr myNeuro *bb = new myNeuro(); //----------------------------------INPUTS----GENERATOR------------- /!  2    qsrand((QTime::currentTime().second())); float *abc = new float[100]; for(int i=0; i<100;i++) { abc[i] =(qrand()%98)*0.01+0.01; } float *cba = new float[100]; for(int i=0; i<100;i++) { cba[i] =(qrand()%98)*0.01+0.01; } //---------------------------------TARGETS----GENERATOR------------- //  2   float *tar1 = new float[2]; tar1[0] =0.01; tar1[1] =0.99; float *tar2 = new float[2]; tar2[0] =0.99; tar2[1] =0.01; //--------------------------------NN---------WORKING--------------- //    bb->query(abc); qDebug()<<"_________________________________"; bb->query(cba); //  int i=0; while(i<100000) { bb->train(abc,tar1); bb->train(cba,tar2); i++; } //   (   ) qDebug()<<"___________________RESULT_____________"; bb->query(abc); qDebug()<<"______"; bb->query(cba); } 

神经网络的结果

图片

总结


如您所见,在训练每个向量之前调用查询(输入)函数并不能使我们判断它们之间的差异。 此外,通过调用训练(输入,目标)函数来进行训练,目的是安排权重系数,以便神经网络随后可以区分输入向量。

完成训练后,我们发现将向量“ abc”映射到“ tar1”,将“ cba”映射到“ tar2”的尝试失败。

您有机会使用源代码来独立测试性能并尝试配置!

PS:这段代码是从QtCreator编写的,希望您可以轻松替换输出,留下您的评论。

PPS:如果有人对struct nnLay {} write的工作的详细分析感兴趣,将会有一篇新文章。

PPPS:我希望有人可以使用面向“ C”的代码移植到其他工具。

源代码
main.cpp

 #include <QCoreApplication> #include <QDebug> #include <QTime> #include "myneuro.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); myNeuro *bb = new myNeuro(); //----------------------------------INPUTS----GENERATOR------------- qsrand((QTime::currentTime().second())); float *abc = new float[100]; for(int i=0; i<100;i++) { abc[i] =(qrand()%98)*0.01+0.01; } float *cba = new float[100]; for(int i=0; i<100;i++) { cba[i] =(qrand()%98)*0.01+0.01; } //---------------------------------TARGETS----GENERATOR------------- float *tar1 = new float[2]; tar1[0] =0.01; tar1[1] =0.99; float *tar2 = new float[2]; tar2[0] =0.99; tar2[1] =0.01; //--------------------------------NN---------WORKING--------------- bb->query(abc); qDebug()<<"_________________________________"; bb->query(cba); int i=0; while(i<100000) { bb->train(abc,tar1); bb->train(cba,tar2); i++; } qDebug()<<"___________________RESULT_____________"; bb->query(abc); qDebug()<<"______"; bb->query(cba); qDebug()<<"_______________THE____END_______________"; return a.exec(); } 

myNeuro.cpp

 #include "myneuro.h" #include <QDebug> myNeuro::myNeuro() { //-------- inputNeurons = 100; outputNeurons =2; nlCount = 4; list = (nnLay*) malloc((nlCount)*sizeof(nnLay)); inputs = (float*) malloc((inputNeurons)*sizeof(float)); targets = (float*) malloc((outputNeurons)*sizeof(float)); list[0].setIO(100,20); list[1].setIO(20,6); list[2].setIO(6,3); list[3].setIO(3,2); //----------------- // inputNeurons = 100; // outputNeurons =2; // nlCount = 2; // list = (nnLay*) malloc((nlCount)*sizeof(nnLay)); // inputs = (float*) malloc((inputNeurons)*sizeof(float)); // targets = (float*) malloc((outputNeurons)*sizeof(float)); // list[0].setIO(100,10); // list[1].setIO(10,2); } void myNeuro::feedForwarding(bool ok) { list[0].makeHidden(inputs); for (int i =1; i<nlCount; i++) list[i].makeHidden(list[i-1].getHidden()); if (!ok) { qDebug()<<"Feed Forward: "; for(int out =0; out < outputNeurons; out++) { qDebug()<<list[nlCount-1].hidden[out]; } return; } else { // printArray(list[3].getErrors(),list[3].getOutCount()); backPropagate(); } } void myNeuro::backPropagate() { //-------------------------------ERRORS-----CALC--------- list[nlCount-1].calcOutError(targets); for (int i =nlCount-2; i>=0; i--) list[i].calcHidError(list[i+1].getErrors(),list[i+1].getMatrix(), list[i+1].getInCount(),list[i+1].getOutCount()); //-------------------------------UPD-----WEIGHT--------- for (int i =nlCount-1; i>0; i--) list[i].updMatrix(list[i-1].getHidden()); list[0].updMatrix(inputs); } void myNeuro::train(float *in, float *targ) { inputs = in; targets = targ; feedForwarding(true); } void myNeuro::query(float *in) { inputs=in; feedForwarding(false); } void myNeuro::printArray(float *arr, int s) { qDebug()<<"__"; for(int inp =0; inp < s; inp++) { qDebug()<<arr[inp]; } } 

myNeuro.h

 #ifndef MYNEURO_H #define MYNEURO_H #include <iostream> #include <math.h> #include <QtGlobal> #include <QDebug> #define learnRate 0.1 #define randWeight (( ((float)qrand() / (float)RAND_MAX) - 0.5)* pow(out,-0.5)) class myNeuro { public: myNeuro(); struct nnLay{ int in; int out; float** matrix; float* hidden; float* errors; int getInCount(){return in;} int getOutCount(){return out;} float **getMatrix(){return matrix;} void updMatrix(float *enteredVal) { for(int ou =0; ou < out; ou++) { for(int hid =0; hid < in; hid++) { matrix[hid][ou] += (learnRate * errors[ou] * enteredVal[hid]); } matrix[in][ou] += (learnRate * errors[ou]); } }; void setIO(int inputs, int outputs) { in=inputs; out=outputs; hidden = (float*) malloc((out)*sizeof(float)); matrix = (float**) malloc((in+1)*sizeof(float)); for(int inp =0; inp < in+1; inp++) { matrix[inp] = (float*) malloc(out*sizeof(float)); } for(int inp =0; inp < in+1; inp++) { for(int outp =0; outp < out; outp++) { matrix[inp][outp] = randWeight; } } } void makeHidden(float *inputs) { for(int hid =0; hid < out; hid++) { float tmpS = 0.0; for(int inp =0; inp < in; inp++) { tmpS += inputs[inp] * matrix[inp][hid]; } tmpS += matrix[in][hid]; hidden[hid] = sigmoida(tmpS); } }; float* getHidden() { return hidden; }; void calcOutError(float *targets) { errors = (float*) malloc((out)*sizeof(float)); for(int ou =0; ou < out; ou++) { errors[ou] = (targets[ou] - hidden[ou]) * sigmoidasDerivate(hidden[ou]); } }; void calcHidError(float *targets,float **outWeights,int inS, int outS) { errors = (float*) malloc((inS)*sizeof(float)); for(int hid =0; hid < inS; hid++) { errors[hid] = 0.0; for(int ou =0; ou < outS; ou++) { errors[hid] += targets[ou] * outWeights[hid][ou]; } errors[hid] *= sigmoidasDerivate(hidden[hid]); } }; float* getErrors() { return errors; }; float sigmoida(float val) { return (1.0 / (1.0 + exp(-val))); } float sigmoidasDerivate(float val) { return (val * (1.0 - val)); }; }; void feedForwarding(bool ok); void backPropagate(); void train(float *in, float *targ); void query(float *in); void printArray(float *arr,int s); private: struct nnLay *list; int inputNeurons; int outputNeurons; int nlCount; float *inputs; float *targets; }; #endif // MYNEURO_H 



UPD:

检查mnist的来源是
链接
1)项目
Github.com/mamkin-itshnik/simple-neuro-network
还有作品的图形描述。 简而言之,当使用测试数据轮询网络时,会为您提供每个输出神经元的值(10个神经元对应于从0到9的数字)。 要决定所描绘的图形,您需要知道最大神经元的索引。 位=索引+ 1(不要忘记数组中的数字从何处开始编号)
2)MNIST
Www.kaggle.com/oddrationale/mnist-in-csv ”(如果您需要使用较小的数据集,请在读取PS的CSV文件时限制while计数器:有一个git示例)

Source: https://habr.com/ru/post/zh-CN440162/


All Articles