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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章