[置顶] 基于梯度下降法的三层神经网络的C++实现(支持保存和读取)
2017-08-12 13:26
771 查看
前言:这也是我根据很久之前看到一本关于游戏AI设计的书里的代码写的,改了一点点,支持保存和读取神经网络。关于什么是神经网络已经有很多博客都有详细的介绍了,这里主要是写写代码的实现。把整个神经网络封装成类,用起来超方便!
┅┅┅┅┅┅┅┅┅┅┅┅┅
这里简单介绍几个重要的函数和用法。
首先我们要定义训练数据的输入向量和输出向量。我的代码采用vector<float>的形式,如。
vector<float> input1;
input1.push_back(1.0);
input1.push_back(1.0);
vector<float> output1;
output1.push_back(0.0);
vector<float> input4;
input4.push_back(-1.0);
input4.push_back(1.0);
vector<float> output4;
output4.push_back(1.0);
然后我们要新建一个数据类,把该训练数据插入进去
Data* MyData=new Data(2,1);//2个输入,一个输出
MyData->AddData(input1,output1);//添加数据,第一个参数是输入向量,第二个数据是输出向量,向量大小必须和定义的一样!
MyData->AddData(input4,output4);
然后我们就可以新建一个神经网络
NeuralNet* Brain=new NeuralNet(2,1,4,0.1,NeuralNet::ERRORSUM,true);//新建一个神经网络,输入神经元个数,输出神经元个数,隐藏层神经元个数,学习率,停止训练方法(次数或误差最小),是否输出误差值(用于观察是否收敛)
然后可以设置我们的网络
Brain->SetErrorThrehold(0.01);//设置误差,默认0.01
//Brain->SetCount(10000);设置次数,默认10000
接下来我们就开始训练,训练函数需要一个数据类
Brain->Train(MyData);//通过数据,开始训练
训练完毕后我们就可以输入我们的测试数据了。我们自己定义好一个输入向量后,通过前向函数,我们会返回一个输出向量,然后我们就可以读取里面的结果了。
cout<<Brain->Update(input1)[0]<<endl;//通过输入得到输出
本代码还支持保存和读取网络,我们可以将训练好的网络保存,和通过重载的构造函数读取。
Brain->saveNet("D:\\1.txt");//保存网络
NeuralNet* Brain2=new NeuralNet("D:\\1.txt");//通过文件读取网络
cout<<Brain2->Update(input1)[0]<<endl;
以上就是最基本的用法。当然这只是个最简单的模板,跟那些专门的库肯定没法比,但是对于初学者来说,帮助很大,也可以用于完成一些简单的识别任务。
附上GitHub地址:一个三层神经网络模板,采用梯度下降算法,支持保存和读取训练好的网络(记得给小星星支持下喔^_^)
附上代码
NeuralNet.h
NeuralNet.cpp
main.cpp
BP-Neural-Network
一个三层神经网络模板
采用梯度下降算法,和sigmoid单元,因此输出应限定在0~1
支持保存和读取训练好的网络
main.cpp里有详细的用法介绍
main.cpp里的例子是训练出一个可以识别线性不可分的数据,即
┇ 1 ┇ 0 ┇ ┇
┅┅┅┅┅┅┅┅┅┅┅┅┅
┇ ┇ 0 ┇ 1 ┇ ┇
这里简单介绍几个重要的函数和用法。
首先我们要定义训练数据的输入向量和输出向量。我的代码采用vector<float>的形式,如。
vector<float> input1;
input1.push_back(1.0);
input1.push_back(1.0);
vector<float> output1;
output1.push_back(0.0);
vector<float> input4;
input4.push_back(-1.0);
input4.push_back(1.0);
vector<float> output4;
output4.push_back(1.0);
然后我们要新建一个数据类,把该训练数据插入进去
Data* MyData=new Data(2,1);//2个输入,一个输出
MyData->AddData(input1,output1);//添加数据,第一个参数是输入向量,第二个数据是输出向量,向量大小必须和定义的一样!
MyData->AddData(input4,output4);
然后我们就可以新建一个神经网络
NeuralNet* Brain=new NeuralNet(2,1,4,0.1,NeuralNet::ERRORSUM,true);//新建一个神经网络,输入神经元个数,输出神经元个数,隐藏层神经元个数,学习率,停止训练方法(次数或误差最小),是否输出误差值(用于观察是否收敛)
然后可以设置我们的网络
Brain->SetErrorThrehold(0.01);//设置误差,默认0.01
//Brain->SetCount(10000);设置次数,默认10000
接下来我们就开始训练,训练函数需要一个数据类
Brain->Train(MyData);//通过数据,开始训练
训练完毕后我们就可以输入我们的测试数据了。我们自己定义好一个输入向量后,通过前向函数,我们会返回一个输出向量,然后我们就可以读取里面的结果了。
cout<<Brain->Update(input1)[0]<<endl;//通过输入得到输出
本代码还支持保存和读取网络,我们可以将训练好的网络保存,和通过重载的构造函数读取。
Brain->saveNet("D:\\1.txt");//保存网络
NeuralNet* Brain2=new NeuralNet("D:\\1.txt");//通过文件读取网络
cout<<Brain2->Update(input1)[0]<<endl;
以上就是最基本的用法。当然这只是个最简单的模板,跟那些专门的库肯定没法比,但是对于初学者来说,帮助很大,也可以用于完成一些简单的识别任务。
附上GitHub地址:一个三层神经网络模板,采用梯度下降算法,支持保存和读取训练好的网络(记得给小星星支持下喔^_^)
附上代码
NeuralNet.h
#ifndef NEURALNET_H #define NEURALNET_H #define ACTIVATION_RESPONSE 1.0 #include<string> #include<vector> #include<math.h> #include<stdlib.h> #include<iostream> using namespace std; class Data{ private: vector<vector<float> > SetIn;//记录训练用的数据 vector<vector<float> > SetOut;//记录训练用数据的目标输出 int NumPatterns;//数据量 int InVectorSize;//数据大小 int OutVectorSize; public: Data(int invectorSize,int outvectorsize):InVectorSize(invectorSize),OutVectorSize(outvectorsize){ } void AddData(vector<float> indata,vector<float> outdata){ SetIn.push_back(indata); ++NumPatterns; SetOut.push_back(outdata); } vector<vector<float> > GetInputSet() {return SetIn;} vector<vector<float> > GetOutputSet(){return SetOut;} }; struct Neuron{ int NumInputs;//神经元的输入量 vector<float> vecWeight;//权重 float Activation;//这里是根据输入,通过某个线性函数确定,相当于神经元的输出 float Error;//误差值 float RandomClamped(){ return -1+2*(rand()/((float)RAND_MAX+1)); } Neuron(int inputs){ NumInputs=inputs+1; Activation=0; Error=0; for(int i=0;i<NumInputs+1;i++) vecWeight.push_back(RandomClamped());//初始化权重 } }; struct NeuronLayer{ int NumNeurons;//每层拥有的神经元数 vector<Neuron> vecNeurons; NeuronLayer(int neurons, int perNeuron):NumNeurons(neurons){ for(int i=0;i<NumNeurons;i++) vecNeurons.push_back(Neuron(perNeuron)); } }; typedef vector<float> iovector; class NeuralNet{ private: int NumInputs;//输入量 int NumOutputs;//输出量 int NumHiddenLayers;//隐藏层数 int NeuronsPerHiddenLayer;//隐藏层拥有的神经元 float LearningRate;//学习率 float ErrorSum;//误差总值 bool Trained;//是否已经训练过 int NumEpochs;//代数 float ERROR_THRESHOLD; //误差阈值(什么时候停止训练) long int Count; //训练次数(什么时候停止训练) vector<NeuronLayer> vecLayers;//层数 bool NetworkTrainingEpoch(vector<iovector > &SetIn,vector<iovector > &SetOut);//训练神经网络 void CreateNet();//生成网络 void InitializeNetwork();//初始化 inline float Sigmoid(float activation, float response); float RandomClamped(){ return -1+2*(rand()/((float)RAND_MAX+1)); } bool Debug;//是否输出误差值 public: bool Train(Data* data);//开始训练 enum STOPTYPE{COUNT,ERRORSUM}StopType; NeuralNet(int inputs,int outputs,int hiddenneurons,float learningRate,STOPTYPE type,bool debug=0);//初始化网络 void SetErrorThrehold(float num){ERROR_THRESHOLD=num;}//设置误差 void SetCount(long int num){Count=num;}//设置训练次数 vector<float> Update(vector<float> inputs);//得到输出 NeuralNet(string filename);//通过文件地址打开一个已经训练好的网络 void saveNet(string filename);//保存已经训练的网络 }; #endif // NEURALNET_H
NeuralNet.cpp
#include "NeuralNet.h" bool NeuralNet::NetworkTrainingEpoch(vector<iovector> &SetIn, vector<iovector> &SetOut){ vector<float>::iterator curWeight;//指向某个权重 vector<Neuron>::iterator curNrnOut,curNrnHid;//指向输出神经元和某个隐藏神经元 ErrorSum=0;//置零 //对每一个输入集合调整权值 for(unsigned int vec=0;vec<SetIn.size();vec++){ vector<float> outputs=Update(SetIn[vec]);//通过神经网络获得输出 //根据每一个输出神经元的输出调整权值 for(int op=0;op<NumOutputs;op++){ float err=(SetOut[vec][op]-outputs[op])*outputs[op]*(1-outputs[op]);//误差的平方 ErrorSum+=(SetOut[vec][op]-outputs[op])*(SetOut[vec][op]-outputs[op]);//计算误差总和,用于暂停训练 vecLayers[1].vecNeurons[op].Error=err;//更新误差(输出层) curWeight=vecLayers[1].vecNeurons[op].vecWeight.begin();//标记第一个权重 curNrnHid=vecLayers[0].vecNeurons.begin();//标记隐藏层第一个神经元 //对该神经元的每一个权重进行调整 while(curWeight!=vecLayers[1].vecNeurons[op].vecWeight.end()-1){ *curWeight+=err*LearningRate*curNrnHid->Activation;//根据误差和学习率和阈值调整权重 curWeight++;//指向下一个权重 curNrnHid++;//指向下一个隐藏层神经元 } *curWeight+=err*LearningRate*(-1);//偏移值 } curNrnHid=vecLayers[0].vecNeurons.begin();//重新指向隐藏层第一个神经元 int n=0; //对每一个隐藏层神经元 while(curNrnHid!=vecLayers[0].vecNeurons.end()-1){ float err=0; curNrnOut=vecLayers[1].vecNeurons.begin();//指向第一个输出神经元 //对每一个输出神经元的第一个权重 while(curNrnOut!=vecLayers[1].vecNeurons.end()){ err+=curNrnOut->Error*curNrnOut->vecWeight ;//某种计算误差的方法(BP) curNrnOut++; } err*=curNrnHid->Activation*(1-curNrnHid->Activation);//某种计算误差的方法(BP) for(int w=0;w<NumInputs;w++) curNrnHid->vecWeight[w]+=err*LearningRate*SetIn[vec][w];//根据误差更新隐藏层的权重 curNrnHid->vecWeight[NumInputs]+=err*LearningRate*(-1);//偏移值 curNrnHid++;//下一个隐藏层神经元 n++;//下一个权重 } } return 1; } void NeuralNet::CreateNet(){ if(NumHiddenLayers>0){ vecLayers.push_back(NeuronLayer(NeuronsPerHiddenLayer,NumInputs)); for(int i=0;i<NumHiddenLayers-1;++i) vecLayers.push_back(NeuronLayer(NeuronsPerHiddenLayer,NeuronsPerHiddenLayer)); vecLayers.push_back(NeuronLayer(NumOutputs,NeuronsPerHiddenLayer)); } else{ vecLayers.push_back(NeuronLayer(NumOutputs,NumInputs)); } } void NeuralNet::InitializeNetwork(){ for(int i=0;i<NumHiddenLayers+1;++i) for(int n=0;n<vecLayers[i].NumNeurons;++n) for(int k=0;k<vecLayers[i].vecNeurons .NumInputs;++k) vecLayers[i].vecNeurons .vecWeight[k]=RandomClamped();//随机生成权重 ErrorSum=9999; NumEpochs=0; } float NeuralNet::Sigmoid(float activation, float response){ return ( 1 / ( 1 + exp(-activation / response))); } NeuralNet::NeuralNet(int inputs, int outputs, int hiddenneurons, float learningRate, STOPTYPE type, bool debug): NumInputs(inputs), NumOutputs(outputs), NumHiddenLayers(1), NeuronsPerHiddenLayer(hiddenneurons), LearningRate(learningRate), ERROR_THRESHOLD(0.01), Count(10000), StopType(type), Debug(debug), ErrorSum(9999), Trained(false), NumEpochs(0){ CreateNet(); } vector<float> NeuralNet::Update(vector<float> inputs){ vector<float> outputs; int cWeight = 0; if (inputs.size()!=NumInputs) return outputs; for(int i=0;i<NumHiddenLayers+1;++i){ if(i>0) inputs=outputs; outputs.clear(); cWeight = 0; for(int n=0;n<vecLayers[i].NumNeurons;++n){ float netinput=0; int numInputs=vecLayers[i].vecNeurons .NumInputs; for (int k=0;k<numInputs-1;++k) netinput+=vecLayers[i].vecNeurons .vecWeight[k]*inputs[cWeight++]; netinput+=vecLayers[i].vecNeurons .vecWeight[numInputs-1]*(-1); vecLayers[i].vecNeurons .Activation=Sigmoid(netinput,ACTIVATION_RESPONSE); outputs.push_back(vecLayers[i].vecNeurons .Activation);//即输出 cWeight = 0; } } return outputs; } NeuralNet::NeuralNet(string filename){ FILE *sourcefile=fopen(filename.c_str(),"rb"); fread(&this->NumInputs,sizeof(int),1,sourcefile); fread(&this->NumOutputs,sizeof(int),1,sourcefile); fread(&this->NumHiddenLayers,sizeof(int),1,sourcefile); fread(&this->NeuronsPerHiddenLayer,sizeof(int),1,sourcefile); fread(&this->LearningRate,sizeof(float),1,sourcefile); fread(&this->ErrorSum,sizeof(float),1,sourcefile); fread(&this->Trained,sizeof(bool),1,sourcefile); fread(&this->NumEpochs,sizeof(int),1,sourcefile); fread(&this->ERROR_THRESHOLD,sizeof(float),1,sourcefile); fread(&this->Count,sizeof(long int),1,sourcefile); this->CreateNet(); for(int i=0;i<this->vecLayers.size();i++){ fread(&this->vecLayers[i].NumNeurons,sizeof(int),1,sourcefile); for(int j=0;j<this->vecLayers[i].vecNeurons.size();j++){ fread(&this->vecLayers[i].vecNeurons[j].NumInputs,sizeof(int),1,sourcefile); fread(&this->vecLayers[i].vecNeurons[j].Activation,sizeof(float),1,sourcefile); fread(&this->vecLayers[i].vecNeurons[j].Error,sizeof(float),1,sourcefile); for(int k=0;k<this->vecLayers[i].vecNeurons[j].vecWeight.size();k++) fread(&this->vecLayers[i].vecNeurons[j].vecWeight[k],sizeof(float),1,sourcefile); } } fclose(sourcefile); } void NeuralNet::saveNet(string filename){ FILE *sourcefile=fopen(filename.c_str(),"wb"); fwrite(&this->NumInputs,sizeof(int),1,sourcefile); fwrite(&this->NumOutputs,sizeof(int),1,sourcefile); fwrite(&this->NumHiddenLayers,sizeof(int),1,sourcefile); fwrite(&this->NeuronsPerHiddenLayer,sizeof(int),1,sourcefile); fwrite(&this->LearningRate,sizeof(float),1,sourcefile); fwrite(&this->ErrorSum,sizeof(float),1,sourcefile); fwrite(&this->Trained,sizeof(bool),1,sourcefile); fwrite(&this->NumEpochs,sizeof(int),1,sourcefile); fwrite(&this->ERROR_THRESHOLD,sizeof(float),1,sourcefile); fwrite(&this->Count,sizeof(long int),1,sourcefile); for(int i=0;i<this->vecLayers.size();i++){ fwrite(&this->vecLayers[i].NumNeurons,sizeof(int),1,sourcefile); for(int j=0;j<this->vecLayers[i].vecNeurons.size();j++){ fwrite(&this->vecLayers[i].vecNeurons[j].NumInputs,sizeof(int),1,sourcefile); fwrite(&this->vecLayers[i].vecNeurons[j].Activation,sizeof(float),1,sourcefile); fwrite(&this->vecLayers[i].vecNeurons[j].Error,sizeof(float),1,sourcefile); for(int k=0;k<this->vecLayers[i].vecNeurons[j].vecWeight.size();k++) fwrite(&this->vecLayers[i].vecNeurons[j].vecWeight[k],sizeof(float),1,sourcefile); } } fclose(sourcefile); } bool NeuralNet::Train(Data *data){ cout<<"Training..."<<endl; if(Trained==1) return false; vector<vector<float> > SetIn=data->GetInputSet(); vector<vector<float> > SetOut=data->GetOutputSet(); InitializeNetwork(); if(StopType==COUNT){ long int i=Count; while(i--){ if(Debug) cout<<"ErrorSum:"<<ErrorSum<<endl; NetworkTrainingEpoch(SetIn,SetOut); } } else{ while(ErrorSum>ERROR_THRESHOLD){ if(Debug) cout<<"ErrorSum:"<<ErrorSum<<endl; NetworkTrainingEpoch(SetIn,SetOut); } } Trained=true; cout<<"Done!!!"<<endl; return true; }
main.cpp
#include"NeuralNet.h" int main(){ //训练数据 vector<float> input1; input1.push_back(1.0); input1.push_back(1.0); vector<float> output1; output1.push_back(0.0); vector<float> input2; input2.push_back(1.0); input2.push_back(-1.0); vector<float> output2; output2.push_back(1.0); vector<float> input3; input3.push_back(-1.0); input3.push_back(-1.0); vector<float> output3; output3.push_back(0.0); vector<float> input4; input4.push_back(-1.0); input4.push_back(1.0); vector<float> output4; output4.push_back(1.0); //建立一个数据类 Data* MyData=new Data(2,1);//2个输入,一个输出 MyData->AddData(input1,output1);//添加数据 MyData->AddData(input2,output2); MyData->AddData(input3,output3); MyData->AddData(input4,output4); NeuralNet* Brain=new NeuralNet(2,1,4,0.1,NeuralNet::ERRORSUM,true);//新建一个神经网络,输入神经元个数,输出神经元个数,隐藏层神经元个数,学习率,停止训练方法(次数或误差最小),是否输出误差值(用于观察是否收敛) Brain->SetErrorThrehold(0.01);//设置误差,默认0.01 //Brain->SetCount(10000);设置次数,默认10000 Brain->Train(MyData);//通过数据,开始训练 cout<<Brain->Update(input1)[0]<<endl;//通过输入得到输出 cout<<Brain->Update(input2)[0]<<endl; cout<<Brain->Update(input3)[0]<<endl; cout<<Brain->Update(input4)[0]<<endl; Brain->saveNet("D:\\1.txt");//保存网络 NeuralNet* Brain2=new NeuralNet("D:\\1.txt");//通过文件读取网络 cout<<Brain2->Update(input1)[0]<<endl; cout<<Brain2->Update(input2)[0]<<endl; cout<<Brain2->Update(input3)[0]<<endl; cout<<Brain2->Update(input4)[0]<<endl; return 0; }
相关文章推荐
- 基于梯度下降的神经网络
- Python神经网络代码实现流程(三):反向传播与梯度下降
- 基于gpu与cuda c的神经网络实现
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现
- 基于HTTP缓存轻松实现客户端应用的离线支持及网络优化
- C++实现BP 神经网络两类图形分辨出来_智能计算期末1
- 最简单的三层神经网络Matlab实现
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现
- 三层 BP 神经网络 matlab 实现
- [置顶] C++实现读取文本文件数据到vector中
- 梯度下降法解神经网络
- 三层神经网络简单实现(Python版)
- Ajax - JavaScript之实现私有属性、像C++和Java一样支持基于类的继承方法之例子
- 基于opencv的神经网络算法实现两类分类问题的可视化演示
- C++实现BP 神经网络模拟二维机械臂运动_智能计算期末2
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现
- c++实现mlp神经网络
- [基于Epoll内置Leader-Follower服务端实现, 已可达50万echo qps(全新支持Lua啦)] - C/C++ - ChinaUnix.net -
- 神经网络:多层网络与C++实现
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现(转载)