BP神经网络原理及C++实战

前一段时间做了一个数字识别的小系统,基于BP神经网络算法的,用MFC做的交互。在实现过程中也试着去找一些源码,总体上来讲,这些源码的可移植性都不好,多数将交互部分和核心算法代码杂糅在一起,这样不仅代码阅读困难,而且重要的是核心算法不具备可移植性。设计模式,设计模式的重要性啊!于是自己将BP神经网络的核心算法用标准C++实现,这样可移植性就有保证的,然后在核心算法上实现基于不同GUI库的交互(MFCQT)是能很快的搭建好系统的。下面边介绍BP算法的原理(请看《数字图像处理与机器视觉》非常适合做工程的伙伴),边给出代码的实现,最后给出基于核心算法构建交互的例子。

 

人工神经网络的理论基础

1.感知器

感知器是一种具有简单的两种输出的人工神经元,如下图所示。


 wKiom1Rgu7GSltIFAAB3QPoYEJo250.jpg

 

2.线性单元

只有1-1两种输出的感知器实际上限制了其处理和分类的能力,下图是一种简单的推广,即不带阈值的感知器。

wKiom1RgvBCxfEINAABTuPlzwCA823.jpg

3.误差准则

使用的是一个常用的误差度量标准,平方误差准则。公式如下。

wKioL1RgvN3zBlELAAAfmaBDSoM954.jpg

其中D为训练样本,td为训练观测值d的训练输出,ot为观测值d的实际观测值。如果是个凸函数就好了(搞数学的,一听到凸函数就很高兴,呵呵!),但还是可以用梯度下降的方法求其参数w

 

4.梯度下降推导

在高等数学中梯度的概念实际上就是一个方向向量,也就是方向导数最大的方向,也就是说沿着这个方向,函数值的变化速度最快。我们这里是做梯度下降,那么就是沿着梯度的负方向更新参数w的值来快速达到E函数值的最小了。这样梯度下降算法的步骤基本如下:

1)  初始化参数w(随机,或其它方法)。

2)  求梯度。

3)  沿梯度方向更新参数w,可以添加一个学习率,也就是按多大的步子下降。

4)  重复1),2),3)直到达到设置的条件(迭代次数,或者E的减小量小于某个阈值)。

梯度的表达式如下:

wKiom1RgvMKyZz0PAAAsXJjofww067.jpg

那么如何求梯度呢?就是复合函数求导的过程,如下:

wKiom1RgvPfQfLmrAACfvrKC9P0674.jpg

其中xid为样本中第d个观测值对应的一个输入分量xi。这样,训练过程中参数w的更新表达式如下(其中添加了一个学习率,也就是下降的步长):

wKiom1RgvU_zGpB8AAAflmrdQpw857.jpg

于是参数wi的更新增量为:

wKiom1RgvaDSzn56AAAa9yFtAlw606.jpg

对于学习率选择的问题,一般较小是能够保证收敛的,看下图吧。

wKioL1RgvmnTsw05AADqfaynL2I685.jpg

5.增量梯度下降

对于4中的梯度下降算法,其缺点是有时收敛速度慢,如果在误差曲面上存在多个局部极小值,算法不能保证能够找到全局极小值。为了改善这些缺点,提出了增量梯度下降算法。增量梯度下降,与4中的梯度下降的不同之处在于,4中对参数w的更新是根据整个样本中的观测值的误差来计算的,而增量梯度下降算法是根据样本中单个观测值的误差来计算w的更新。

 

6.梯度检验

这是一个比较实用的内容,如何确定自己的代码就一定没有错呢?因为在求梯度的时候是很容易犯错误的,我就犯过了,嗨,调了两天才找出来,一个数组下表写错了,要是早一点看看斯坦福大学的深度学习基础教程就好了,这里只是截图一部分,有时间去仔细看看吧。

wKioL1RgvrOQvn2fAAIm7AJietY265.jpg


多层神经网络

    好了有了前面的基础,我们现在就可以进行实战了,构造多层神经网络。

1.Sigmoid神经元

Sigmoid神经元可由下图表示:

wKioL1RgvvKATld2AABv9nesXzY799.jpg

2.神经网络层

一个三层的BP神经网络可由下图表示:

wKiom1Rgvr6hpCAgAAFpop9khJM994.jpg

 

3.神经元和神经网络层的标准C++定义

2中的三层BP神经网络的示意图中可以看出,隐藏层和输出层是具有类似的结构的。神经元和神经网络层的定义如下:

////////////////////////////////////////////////////////
// Neuron.h
#ifndef __SNEURON_H__
#define __SNEURON_H__
 
#define NEED_MOMENTUM //if you want to addmomentum, remove the annotation
 
#define MOMENTUM 0.6 //momentumcoefficient, works on when defined NEED_MOMENTUM
 
typedef double WEIGHT_TYPE; // definedatatype of the weight
 
 
struct SNeuron{//neuron cell
         /******Data*******/
         intm_nInput; //number of inputs
         WEIGHT_TYPE*m_pWeights;  //weights array of inputs
#ifdef NEED_MOMENTUM
         WEIGHT_TYPE*m_pPrevUpdate; //record last weights update when momentum is needed
#endif
         doublem_dActivation; //output value, through Sigmoid function
         doublem_dError; //error value of neuron
 
         /********Functions*************/
         voidInit(int nInput){
                   m_nInput= nInput + 1; //add a side term,number of inputs is actual number of actualinputs plus 1
                   m_pWeights= new WEIGHT_TYPE[m_nInput];//allocate for weights array
#ifdef NEED_MOMENTUM
                   m_pPrevUpdate= new WEIGHT_TYPE[m_nInput];//allocate for the last weights array
#endif
                   m_dActivation= 0; //output value, through SIgmoid function
                   m_dError= 0;  //error value of neuron
         }
 
         ~SNeuron(){
                   //releasememory
                   delete[]m_pWeights;
#ifdef NEED_MOMENTUM
                   delete[]m_pPrevUpdate;
#endif
         }
};//SNeuron
 
 
struct SNeuronLayer{//neuron layer
         /************Data**************/
         intm_nNeuron; //Neuron number of this layer
         SNeuron*m_pNeurons; //Neurons array
 
         /*************Functions***************/
         SNeuronLayer(intnNeuron, int nInputsPerNeuron){
                   m_nNeuron= nNeuron;
                   m_pNeurons= new SNeuron[nNeuron];  //allocatememory for nNeuron neurons
 
                   for(inti=0; i<nNeuron; i++){
                            m_pNeurons[i].Init(nInputsPerNeuron);  //initialize neuron
                   }
         }
         ~SNeuronLayer(){
                   delete[]m_pNeurons;  //release neurons array
         }
};//SNeuronLayer
 
#endif//__SNEURON_H__

代码中定义了一个NEED_MOMENTUM,它是用来解决局部极小值的问题的,其含义是本次权重的更新是依赖于上一次权重更新的。另外还有一种解决局部极小值问题的方法是,将w初始化为接近于0的随机数。

 

4.反向传播(BP)算法

前面虽然有了神经元和神经网络层的定义,但要构造一个三层的BP神经网络之前,还要搞清楚BP算法是如何来学习神经网络的参数的。它仍采用梯度下降算法,但不同的是这里的输出是整个网络的输出,而不再是一个单元的输出,所有对误差函数E重新定义如下:


wKiom1Rgv6HgAO8fAAApeDEAOfM978.jpg

其中outputs是网络中输出层单元的集合,tkdokd是训练样本中第d个观测值在第k个输出单元的而输出值。

 

1BP算法推导

先引入下列符号:

wKioL1RgwE3iRiDYAADpzb0k1yg980.jpg

增量梯度下降算法中,对于每个训练样本中第d个观测的一个输入权重wij的增量如下表示:

wKioL1RgwJnwNhTTAAAW7EB7v0w899.jpg

其中Ed是训练样本中第d个观测的误差,通过对输出层所有单元的求和得到:

wKioL1RgwLuDQ4olAAAiDrGsF8E752.jpg

这里说明一下,神经网络输出层的所有单元联合一起表示一个样本观测的训练值的。假设样本观测值为5种,即5种类别,那么先验训练数据的类别表示为:1,0,0,0,00,1,0,0,00,0,1,0,00,0,0,1,00,0,0,0,1。这样在对神经网络训练时,我们的训练输出值的表示也就是类似的,当然基于神经元的结构表示,我们也可以将先验训练数据的类别表示中的1换成0.9等。

下面我们就要求梯度了(要分层求解,输出层,隐藏层),梯度向量中的各元素求解如下:

wKiom1RgwL6QeOimAABWAfEi0B8959.jpg

1)当单元j是一个输出单元时:

wKiom1RgwOug-XAiAAAlk5dFJ-o518.jpg

其中:

wKioL1RgwXvw4gEWAAChKLD6rzc918.jpg

于是得到:

wKiom1RgwZrSlN_6AACB4pw7ljs023.jpg

2)当单元j是一个隐藏层单元时,有如下推导:

wKioL1Rgwm_xivUmAAE7AtHd_xM921.jpg

5.标准C++构建三层BP神经网络

该神经网络提供了重要的两个接口。一个是一次训练训练接口TrainingEpoch,可用于上层算法构建训练函数时调用;另一个是计算给定一个输入神经网络的输出接口CalculateOutput,它在一次训练函数中被调用,更重要的是,在上层算法中构建识别函数调用。

 

头文件:

// NeuralNet.h: interface for theCNeuralNet class.
//
//////////////////////////////////////////////////////////////////////
 
#ifndef __NEURALNET_H__
#define __NEURALNET_H__
 
#include <vector>
#include <math.h>
#include "Neuron.h"
using namespace std;
 
typedef vector<double> iovector;
#define BIAS 1 //bias term's coefficient w0
 
/*************Random functions initializingweights*************/
#define WEIGHT_FACTOR 0.1 //used to confineinitial weights
 
/*Return a random float between 0 to 1*/
inline double RandFloat(){ return(rand())/(RAND_MAX+1.0); }
 
/*Return a random float between -1 to 1*/
inline double RandomClamped(){ returnWEIGHT_FACTOR*(RandFloat() - RandFloat()); }
 
class CNeuralNet{
private:
         /*Initialparameters, can not be changed throghout the whole training.*/
         intm_nInput;  //number of inputs
         intm_nOutput; //number of outputs
         intm_nNeuronsPerLyr; //unit number of hidden layer
         intm_nHiddenLayer; //hidden layer, not including the output layer
 
         /***Dinamicparameters****/
         doublem_dErrorSum;  //one epoch's sum-error
 
         SNeuronLayer*m_pHiddenLyr;  //hidden layer
         SNeuronLayer*m_pOutLyr;     //output layer
 
public:
         /*
         *Constructorand Destructor.
         */
         CNeuralNet(intnInput, int nOutput, int nNeuronsPerLyr, int nHiddenLayer);
         ~CNeuralNet();
         /*
         *Computeoutput of network, feedforward.
         */
         bool CalculateOutput(vector<double> input,vector<double>& output);
         /*
         *Trainingan Epoch, backward adjustment.
         */
         bool TrainingEpoch(vector<iovector>& SetIn,vector<iovector>& SetOut, double LearningRate);
         /*
         *Geterror-sum.
         */
         doubleGetErrorSum(){ return m_dErrorSum; }
         SNeuronLayer*GetHiddenLyr(){ return m_pHiddenLyr; }
         SNeuronLayer*GetOutLyr(){ return m_pOutLyr; }
 
private:
         /*
         *Biuldnetwork, allocate memory for each layer.
         */
         voidCreateNetwork();
         /*
         *Initializenetwork.
         */
         voidInitializeNetwork();
         /*
         *Sigmoidencourage fuction.
         */
         doubleSigmoid(double netinput){
                   doubleresponse = 1.0;  //control steep degreeof sigmoid function
                   return(1 / ( 1 + exp(-netinput / response) ) );
         }       
};
 
#endif //__NEURALNET_H__


实现文件:

// NeuralNet.cpp: implementation of theCNeuralNet class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "NeuralNet.h"
#include <assert.h>
 
CNeuralNet::CNeuralNet(int nInput, intnOutput, int nNeuronsPerLyr, int nHiddenLayer){
         assert(nInput>0 && nOutput>0 && nNeuronsPerLyr>0 &&nHiddenLayer>0 );
         m_nInput= nInput;
         m_nOutput= nOutput;
         m_nNeuronsPerLyr= nNeuronsPerLyr;
         if(nHiddenLayer!= 1)
                   m_nHiddenLayer= 1;
         else
                   m_nHiddenLayer= nHiddenLayer; //temporarily surpport only one hidden layer
 
         m_pHiddenLyr= NULL;
         m_pOutLyr= NULL;
 
         CreateNetwork();   //allocate for each layer
         InitializeNetwork();  //initialize the whole network
}
 
CNeuralNet::~CNeuralNet(){
         if(m_pHiddenLyr!= NULL)
                   deletem_pHiddenLyr;
         if(m_pOutLyr!= NULL)
                   deletem_pOutLyr;
}
 
void CNeuralNet::CreateNetwork(){
         m_pHiddenLyr= new SNeuronLayer(m_nNeuronsPerLyr, m_nInput);
         m_pOutLyr= new SNeuronLayer(m_nOutput, m_nNeuronsPerLyr);
}
 
void CNeuralNet::InitializeNetwork(){
         inti, j;  //variables for loop
 
         /*usepresent time as random seed, so every time runs this programm can producedifferent random sequence*/
         //srand((unsigned)time(NULL) );
 
         /*initializehidden layer's weights*/
         for(i=0;i<m_pHiddenLyr->m_nNeuron; i++){
                   for(j=0;j<m_pHiddenLyr->m_pNeurons[i].m_nInput; j++){
                            m_pHiddenLyr->m_pNeurons[i].m_pWeights[j]= RandomClamped();
                            #ifdefNEED_MOMENTUM
                            /*whenthe first epoch train started, there is no previous weights update*/
                            m_pHiddenLyr->m_pNeurons[i].m_pPrevUpdate[j]= 0;
                            #endif
                   }       
         }
         /*initializeoutput layer's weights*/
         for(i=0;i<m_pOutLyr->m_nNeuron; i++){
                   for(intj=0; j<m_pOutLyr->m_pNeurons[i].m_nInput; j++){
                            m_pOutLyr->m_pNeurons[i].m_pWeights[j]= RandomClamped();
                            #ifdefNEED_MOMENTUM
                            /*whenthe first epoch train started, there is no previous weights update*/
                            m_pOutLyr->m_pNeurons[i].m_pPrevUpdate[j]= 0;
                            #endif
                   }
         }
 
         m_dErrorSum= 9999.0;  //initialize a large trainingerror, it will be decreasing with training
}
 
boolCNeuralNet::CalculateOutput(vector<double> input,vector<double>& output){
         if(input.size()!= m_nInput){ //input feature vector's dimention not equals to input of network
                   returnfalse;
         }
         inti, j;
         doublenInputSum;  //sum term
 
         /*computehidden layer output*/
         for(i=0;i<m_pHiddenLyr->m_nNeuron; i++){
                   nInputSum= 0;
                   for(j=0;j<m_pHiddenLyr->m_pNeurons[i].m_nInput-1; j++){
                            nInputSum+= m_pHiddenLyr->m_pNeurons[i].m_pWeights[j] * input[j];
                   }
                   /*plusbias term*/
                   nInputSum+= m_pHiddenLyr->m_pNeurons[i].m_pWeights[j] * BIAS;
                   /*computesigmoid fuction's output*/
                   m_pHiddenLyr->m_pNeurons[i].m_dActivation= Sigmoid(nInputSum);
         }
 
         /*computeoutput layer's output*/
         for(i=0;i<m_pOutLyr->m_nNeuron; i++){
                   nInputSum= 0;
                   for(j=0;j<m_pOutLyr->m_pNeurons[i].m_nInput-1; j++){
                            nInputSum+= m_pOutLyr->m_pNeurons[i].m_pWeights[j]
                                     *m_pHiddenLyr->m_pNeurons[j].m_dActivation;
                   }
                   /*plusbias term*/
                   nInputSum+= m_pOutLyr->m_pNeurons[i].m_pWeights[j] * BIAS;
                   /*computesigmoid fuction's output*/
                   m_pOutLyr->m_pNeurons[i].m_dActivation= Sigmoid(nInputSum);
                   /*saveit to the output vector*/
                   output.push_back(m_pOutLyr->m_pNeurons[i].m_dActivation);
         }
         returntrue;
}
 
bool CNeuralNet::TrainingEpoch(vector<iovector>&SetIn, vector<iovector>& SetOut, double LearningRate){
         inti, j, k;
         doubleWeightUpdate;  //weight's update value
         doubleerr;  //error term
 
         /*increment'sgradient decrease(update weights according to each training sample)*/
         m_dErrorSum= 0;  // sum of error term
         for(i=0;i<SetIn.size(); i++){
                   iovectorvecOutputs;
                   /*forwardlyspread inputs through network*/
                   if(!CalculateOutput(SetIn[i],vecOutputs)){
                            returnfalse;
                   }
 
                   /*updatethe output layer's weights*/
                   for(j=0;j<m_pOutLyr->m_nNeuron; j++){
 
                            /*computeerror term*/
                            err= ((double)SetOut[i][j]-vecOutputs[j])*vecOutputs[j]*(1-vecOutputs[j]);
                            m_pOutLyr->m_pNeurons[j].m_dError= err;  //record this unit's error
 
                            /*updatesum error*/
                            m_dErrorSum+= ((double)SetOut[i][j] - vecOutputs[j]) * ((double)SetOut[i][j] -vecOutputs[j]);
 
                            /*updateeach input's weight*/
                            for(k=0;k<m_pOutLyr->m_pNeurons[j].m_nInput-1; k++){
                                     WeightUpdate= err * LearningRate * m_pHiddenLyr->m_pNeurons[k].m_dActivation;
#ifdef NEED_MOMENTUM
                                     /*updateweights with momentum*/
                                     m_pOutLyr->m_pNeurons[j].m_pWeights[k]+=
                                               WeightUpdate+ m_pOutLyr->m_pNeurons[j].m_pPrevUpdate[k] * MOMENTUM;
                                     m_pOutLyr->m_pNeurons[j].m_pPrevUpdate[k]= WeightUpdate;
#else
                                     /*updateunit weights*/
                                     m_pOutLyr->m_pNeurons[j].m_pWeights[k]+= WeightUpdate;
#endif
                            }
                            /*biasupdate volume*/
                            WeightUpdate= err * LearningRate * BIAS;
#ifdef NEED_MOMENTUM
                            /*updatebias with momentum*/
                            m_pOutLyr->m_pNeurons[j].m_pWeights[k]+=
                                     WeightUpdate+ m_pOutLyr->m_pNeurons[j].m_pPrevUpdate[k] * MOMENTUM;
                            m_pOutLyr->m_pNeurons[j].m_pPrevUpdate[k]= WeightUpdate;
#else
                            /*updatebias*/
                            m_pOutLyr->m_pNeurons[j].m_pWeights[k]+= WeightUpdate;
#endif
                   }//for out layer
 
                   /*updatethe hidden layer's weights*/
                   for(j=0;j<m_pHiddenLyr->m_nNeuron; j++){
 
                            err= 0;
                            for(intk=0; k<m_pOutLyr->m_nNeuron; k++){
                                     err+= m_pOutLyr->m_pNeurons[k].m_dError *m_pOutLyr->m_pNeurons[k].m_pWeights[j];
                            }
                            err*= m_pHiddenLyr->m_pNeurons[j].m_dActivation * (1 -m_pHiddenLyr->m_pNeurons[j].m_dActivation);
                            m_pHiddenLyr->m_pNeurons[j].m_dError= err;  //record this unit's error
 
                            /*updateeach input's weight*/
                            for(k=0;k<m_pHiddenLyr->m_pNeurons[j].m_nInput-1; k++){
                                     WeightUpdate= err * LearningRate * SetIn[i][k];
#ifdef NEED_MOMENTUM
                                     /*updateweights with momentum*/
                                     m_pHiddenLyr->m_pNeurons[j].m_pWeights[k]+=
                                               WeightUpdate+ m_pHiddenLyr->m_pNeurons[j].m_pPrevUpdate[k] * MOMENTUM;
                                     m_pHiddenLyr->m_pNeurons[j].m_pPrevUpdate[k]= WeightUpdate;
#else
                                     m_pHiddenLyr->m_pNeurons[j].m_pWeights[k]+= WeightUpdate;
#endif
                            }
                            /*biasupdate volume*/
                            WeightUpdate= err * LearningRate * BIAS;
#ifdef NEED_MOMENTUM
                            /*updatebias with momentum*/
                            m_pHiddenLyr->m_pNeurons[j].m_pWeights[k]+=
                                     WeightUpdate+ m_pHiddenLyr->m_pNeurons[j].m_pPrevUpdate[k] * MOMENTUM;
                            m_pHiddenLyr->m_pNeurons[j].m_pPrevUpdate[k]= WeightUpdate;
#else
                            /*updatebias*/
                            m_pHiddenLyr->m_pNeurons[j].m_pWeights[k]+= WeightUpdate;
#endif
                   }//forhidden layer
         }//forone epoch
         returntrue;
}

 

6.基于BP核心算法构建MVC框架

到此为止我们的核心算法已经构建出来了,再应用两次Strategy 设计模式,我们就很容易构建出一个MVC框架(see alsohttp://remyspot.blog.51cto.com/8218746/1574484)。下面给出一个应用Strategy设计模式基于CNeuralNet类构建一个Controller,在Controller中我们就可以开始依赖特定的GUI库了。下面的这个Controller是不能直接使用的,你所要做的是参考该代码(重点参看

boolTrain(vector<iovector>& SetIn, vector<iovector>& SetOut);

bool SaveTrainResultToFile(const char* lpszFileName, boolbCreate);

bool LoadTrainResultFromFile(const char* lpszFileName, DWORDdwStartPos);

int Recognize(CString strPathName, CRect rt, double&dConfidence);

接口的实现), 然后基于前面的核心BP算构建你自己的Controller,然后在该Controller的上层实现你自己的交互功能。

说明一下,Train接口中的SetIn是训练数据的特征,SetOut是训练数据的类别表示。

 

头文件:

#ifndef __OPERATEONNEURALNET_H__
#define __OPERATEONNEURALNET_H__
 
#include "NeuralNet.h"
#define NEURALNET_VERSION 1.0
#define RESAMPLE_LEN 4
 
class COperateOnNeuralNet{
private:
         /*network*/
         CNeuralNet*m_oNetWork;
 
         /*network'sparameter*/
         intm_nInput;
         intm_nOutput;
         intm_nNeuronsPerLyr;
         intm_nHiddenLayer;
 
         /*trainingconfiguration*/
         intm_nMaxEpoch;  // max training epoch times
         doublem_dMinError; // error threshold
         doublem_dLearningRate;
 
         /*dinamiccurrent parameter*/
         intm_nEpochs;
         doublem_dErr;  //mean error of oneepoch(m_dErrorSum/(num-of-samples * num-of-output))
         boolm_bStop; //control whether stop or not during the training
         vector<double> m_vecError;   //record each epoch'straining error, used for drawing error curve
 
public:
         COperateOnNeuralNet();
         ~COperateOnNeuralNet();
 
         voidSetNetWorkParameter(int nInput, int nOutput, int nNeuronsPerLyr, intnHiddenLayer);
         boolCreatNetWork();
         voidSetTrainConfiguration(int nMaxEpoch, double dMinError, double dLearningRate);
         voidSetStopFlag(bool bStop) { m_bStop = bStop; }
 
         doubleGetError(){ return m_dErr; }
         intGetEpoch(){ return m_nEpochs; }
         intGetNumNeuronsPerLyr(){ return m_nNeuronsPerLyr; }
 
         boolTrain(vector<iovector>& SetIn, vector<iovector>& SetOut);
         bool SaveTrainResultToFile(const char* lpszFileName, boolbCreate);
         bool LoadTrainResultFromFile(const char* lpszFileName, DWORDdwStartPos);
         int Recognize(CString strPathName, CRect rt, double&dConfidence);
};
/*
* Can be used when saving or readingtraining result.
*/
struct NEURALNET_HEADER{
         DWORDdwVersion;  //version imformation
        
         /*initialparameters*/
         intm_nInput;  //number of inputs
         intm_nOutput; //number of outputs
         intm_nNeuronsPerLyr; //unit number of hidden layer
         intm_nHiddenLayer; //hidden layer, not including the output layer
 
         /*trainingconfiguration*/
         intm_nMaxEpoch;  // max training epoch times
         doublem_dMinError; // error threshold
         doublem_dLearningRate;
 
         /*dinamiccurrent parameter*/
         intm_nEpochs;
         doublem_dErr;  //mean error of oneepoch(m_dErrorSum/(num-of-samples * num-of-output))
};
#endif //__OPERATEONNEURALNET_H__


实现文件:

// OperateOnNeuralNet.cpp: implementationof the COperateOnNeuralNet class.
//
//////////////////////////////////////////////////////////////////////
 
#include "stdafx.h"
#include "MyDigitRec.h"
#include "OperateOnNeuralNet.h"
#include "Img.h"
#include <assert.h>
 
/*
*Handle message during waiting.
*/
void WaitForIdle()
{
         MSGmsg; 
         while(::PeekMessage(&msg,NULL, 0, 0, PM_REMOVE))
         {
                   ::TranslateMessage(&msg);
                   ::DispatchMessage(&msg);
         }
}
 
COperateOnNeuralNet::COperateOnNeuralNet(){
         m_nInput= 0;
         m_nOutput= 0;
         m_nNeuronsPerLyr= 0;
         m_nHiddenLayer= 0;
 
         m_nMaxEpoch= 0;
         m_dMinError= 0;
         m_dLearningRate= 0;
 
         m_oNetWork= 0;
 
         m_nEpochs= 0;
         m_dErr= 0;
         m_bStop= false;
}
 
COperateOnNeuralNet::~COperateOnNeuralNet(){
         if(m_oNetWork)
                   deletem_oNetWork;
}
 
voidCOperateOnNeuralNet::SetNetWorkParameter(int nInput, int nOutput, intnNeuronsPerLyr, int nHiddenLayer){
         assert(nInput>0 && nOutput>0 && nNeuronsPerLyr>0 &&nHiddenLayer>0 );
         m_nInput= nInput;
         m_nOutput= nOutput;
         m_nNeuronsPerLyr= nNeuronsPerLyr;
         m_nHiddenLayer= nHiddenLayer;
}
 
bool COperateOnNeuralNet::CreatNetWork(){
         assert(m_nInput>0 && m_nOutput>0 && m_nNeuronsPerLyr>0&& m_nHiddenLayer>0 );
         m_oNetWork= new CNeuralNet(m_nInput, m_nOutput, m_nNeuronsPerLyr, m_nHiddenLayer);
         if(m_oNetWork)
                   returntrue;
         else
                   returnfalse;
}
 
voidCOperateOnNeuralNet::SetTrainConfiguration(int nMaxEpoch, double dMinError,double dLearningRate){
         assert(nMaxEpoch>0&& !(dMinError<0) && dLearningRate!=0);
         m_nMaxEpoch= nMaxEpoch;
         m_dMinError= dMinError;
         m_dLearningRate= dLearningRate;
}
 
boolCOperateOnNeuralNet::Train(vector<iovector>& SetIn, vector<iovector>&SetOut){
         m_bStop= false;  //no stop during training
         CStringstrOutMsg;
 
         do{
                   /*trainone epoch*/
                   if(!m_oNetWork->TrainingEpoch(SetIn, SetOut, m_dLearningRate) ){
                            strOutMsg.Format("Erroroccured at training %dth epoch!",m_nEpochs+1);
                            AfxMessageBox(strOutMsg);
                            returnfalse;
                   }else{
                            m_nEpochs++;
                   }
                  
                   /*computemean error of one epoch(m_dErrorSum/(num-of-samples * num-of-output))*/
                   intsum = m_oNetWork->GetErrorSum();
                   m_dErr= m_oNetWork->GetErrorSum() / ( m_nOutput * SetIn.size() );
                   m_vecError.push_back(m_dErr);
                   if(m_dErr< m_dMinError){
                            break;
                   }
 
                   /*stopin loop to chech wether user's action made or message sent, mostly for changem_bStop */
                   WaitForIdle();
 
                   if(m_bStop){
                            break;
                   }
         }while(--m_nMaxEpoch> 0);
 
         returntrue;
}
 
boolCOperateOnNeuralNet::SaveTrainResultToFile(const char* lpszFileName, boolbCreate){
         CFilefile;
         if(bCreate){
                   if(!file.Open(lpszFileName,CFile::modeWrite|CFile::modeCreate))
                            returnfalse;
         }else{
                   if(!file.Open(lpszFileName,CFile::modeWrite))
                            returnfalse;
                   file.SeekToEnd();  //add to end of file
         }
 
         /*createnetwork head information*/
         /*initialparameter*/
         NEURALNET_HEADERheader = {0};
         header.dwVersion= NEURALNET_VERSION;
         header.m_nInput= m_nInput;
         header.m_nOutput= m_nOutput;
         header.m_nNeuronsPerLyr= m_nNeuronsPerLyr;
         header.m_nHiddenLayer= m_nHiddenLayer;
         /*trainingconfiguration*/
         header.m_nMaxEpoch= m_nMaxEpoch;
         header.m_dMinError= m_dMinError;
         header.m_dLearningRate= m_dLearningRate;
         /*dinamiccurrent parameter*/
         header.m_nEpochs= m_nEpochs;
         header.m_dErr= m_dErr;
 
         file.Write(&header,sizeof(header));
 
         /*writeweight information to file*/
         inti, j;
         /*hiddenlayer weight*/
         for(i=0;i<m_oNetWork->GetHiddenLyr()->m_nNeuron; i++){
                   file.Write(&m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_dActivation,
                            sizeof(m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_dActivation));
                   file.Write(&m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_dError,
                            sizeof(m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_dError));
                   for(j=0;j<m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_nInput; j++){
                            file.Write(&m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_pWeights[j],
                                     sizeof(m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_pWeights[j]));
                   }
         }
         /*outputlayer weight*/
         for(i=0;i<m_oNetWork->GetOutLyr()->m_nNeuron; i++){
                   file.Write(&m_oNetWork->GetOutLyr()->m_pNeurons[i].m_dActivation,
                            sizeof(m_oNetWork->GetOutLyr()->m_pNeurons[i].m_dActivation));
                   file.Write(&m_oNetWork->GetOutLyr()->m_pNeurons[i].m_dError,
                            sizeof(m_oNetWork->GetOutLyr()->m_pNeurons[i].m_dError));
                   for(j=0;j<m_oNetWork->GetOutLyr()->m_pNeurons[i].m_nInput; j++){
                            file.Write(&m_oNetWork->GetOutLyr()->m_pNeurons[i].m_pWeights[j],
                                     sizeof(m_oNetWork->GetOutLyr()->m_pNeurons[i].m_pWeights[j]));
                   }
         }
 
         file.Close();
         returntrue;
}
 
boolCOperateOnNeuralNet::LoadTrainResultFromFile(const char* lpszFileName, DWORDdwStartPos){
         CFilefile;
         if(!file.Open(lpszFileName,CFile::modeRead)){
                   returnfalse;
         }
 
         file.Seek(dwStartPos,CFile::begin);  //point to dwStartPos
 
         /*readin NeuralNet_Head infomation*/
         NEURALNET_HEADERheader = {0};
         if(file.Read(&header, sizeof(header)) != sizeof(header) ){
                   returnfalse;
         }
 
         /*chechversion*/
         if(header.dwVersion!= NEURALNET_VERSION){
                   returnfalse;
         }
 
         /*checkbasic NeuralNet's structure*/
         if(header.m_nInput!= m_nInput
                   ||header.m_nOutput != m_nOutput
                   ||header.m_nNeuronsPerLyr != m_nNeuronsPerLyr
                   ||header.m_nHiddenLayer != m_nHiddenLayer
                   ||header.m_nMaxEpoch != m_nMaxEpoch
                   ||header.m_dMinError != m_dMinError
                   ||header.m_dLearningRate != m_dLearningRate ){
                   returnfalse;
         }
        
         /*dynamicparameters*/
         m_nEpochs= header.m_nEpochs;  //update trainingepochs
         m_dErr= header.m_dErr;                    //update training error
 
         /*readin NetWork's weights*/
         inti,j;
         /*readin hidden layer weights*/
         for(i=0;i<m_oNetWork->GetHiddenLyr()->m_nNeuron; i++){
                   file.Read(&m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_dActivation,
                            sizeof(m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_dActivation));
                   file.Read(&m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_dError,
                            sizeof(m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_dError));
 
                   for(j=0;j<m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_nInput; j++){
                            file.Read(&m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_pWeights[j],
                                     sizeof(m_oNetWork->GetHiddenLyr()->m_pNeurons[i].m_pWeights[j]));
                   }
         }
 
         /*readin out layer weights*/
         for(i=0;i<m_oNetWork->GetOutLyr()->m_nNeuron; i++){
                   file.Read(&m_oNetWork->GetOutLyr()->m_pNeurons[i].m_dActivation,
                            sizeof(m_oNetWork->GetOutLyr()->m_pNeurons[i].m_dActivation));
                   file.Read(&m_oNetWork->GetOutLyr()->m_pNeurons[i].m_dError,
                            sizeof(m_oNetWork->GetOutLyr()->m_pNeurons[i].m_dError));
 
                   for(j=0;j<m_oNetWork->GetOutLyr()->m_pNeurons[i].m_nInput; j++){
                            file.Read(&m_oNetWork->GetOutLyr()->m_pNeurons[i].m_pWeights[j],
                                     sizeof(m_oNetWork->GetOutLyr()->m_pNeurons[i].m_pWeights[j]));
                   }
         }
        
         returntrue;
}
 
int COperateOnNeuralNet::Recognize(CStringstrPathName, CRect rt, double &dConfidence){
         intnBestMatch;  //category number
         doubledMaxOut1 = 0; //max output
         doubledMaxOut2 = 0; //second max output
 
         CImggray;
         if(!gray.AttachFromFile(strPathName)){
                   return-1;
         }
 
         /*convert the picture waitiong for being recognized to vector*/
         vector<double>vecToRec;
         for(intj=rt.top; j<rt.bottom; j+= RESAMPLE_LEN){
                   for(inti=rt.left; i<rt.right; i+=RESAMPLE_LEN){
                            intnGray = 0;
                            for(intmm=j; mm<j+RESAMPLE_LEN; mm++){
                                     for(intnn=i; nn<i+RESAMPLE_LEN; nn++)
                                               nGray+= gray.GetGray(nn, mm);
                            }
                            nGray/= RESAMPLE_LEN*RESAMPLE_LEN;
                            vecToRec.push_back(nGray/255.0);
                   }
         }
 
         /*computethe output result*/
         vector<double>outputs;
         if(!m_oNetWork->CalculateOutput(vecToRec,outputs)){
                   AfxMessageBox("Recfailed!");
                   return-1;
         }
 
         /*findthe max output unit, and its unit number is the category number*/
         nBestMatch= 0;
         for(intk=0; k<outputs.size(); k++){
                   if(outputs[k]> dMaxOut1){
                            dMaxOut2= dMaxOut1;
                            dMaxOut1= outputs[k];
                            nBestMatch= k;
                   }
         }
         dConfidence= dMaxOut1 - dMaxOut2;  //compute beliefdegree
         returnnBestMatch;
}

本文出自 “Remys” 博客,谢绝转载!

你可能感兴趣的:(C++,神经网络,BP)