前一段時間做了一個數字識別的小系統,基於BP神經網絡算法的,用MFC做的交互。在實現過程中也試着去找一些源碼,總體上來講,這些源碼的可移植性都不好,多數將交互部分和核心算法代碼雜糅在一起,這樣不僅代碼閱讀困難,而且重要的是核心算法不具備可移植性。設計模式,設計模式的重要性啊!於是自己將BP神經網絡的核心算法用標準C++實現,這樣可移植性就有保證的,然後在覈心算法上實現基於不同GUI庫的交互(MFC,QT)是能很快的搭建好系統的。下面邊介紹BP算法的原理(請看《數字圖像處理與機器視覺》非常適合做工程的夥伴),邊給出代碼的實現,最後給出基於核心算法構建交互的例子。
人工神經網絡的理論基礎
1.感知器
感知器是一種具有簡單的兩種輸出的人工神經元,如下圖所示。
2.線性單元
只有1和-1兩種輸出的感知器實際上限制了其處理和分類的能力,下圖是一種簡單的推廣,即不帶閾值的感知器。
3.誤差準則
使用的是一個常用的誤差度量標準,平方誤差準則。公式如下。
其中D爲訓練樣本,td爲訓練觀測值d的訓練輸出,ot爲觀測值d的實際觀測值。如果是個凸函數就好了(搞數學的,一聽到凸函數就很高興,呵呵!),但還是可以用梯度下降的方法求其參數w。
4.梯度下降推導
在高等數學中梯度的概念實際上就是一個方向向量,也就是方向導數最大的方向,也就是說沿着這個方向,函數值的變化速度最快。我們這裏是做梯度下降,那麼就是沿着梯度的負方向更新參數w的值來快速達到E函數值的最小了。這樣梯度下降算法的步驟基本如下:
1) 初始化參數w(隨機,或其它方法)。
2) 求梯度。
3) 沿梯度方向更新參數w,可以添加一個學習率,也就是按多大的步子下降。
4) 重複1),2),3)直到達到設置的條件(迭代次數,或者E的減小量小於某個閾值)。
梯度的表達式如下:
那麼如何求梯度呢?就是複合函數求導的過程,如下:
其中xid爲樣本中第d個觀測值對應的一個輸入分量xi。這樣,訓練過程中參數w的更新表達式如下(其中添加了一個學習率,也就是下降的步長):
於是參數wi的更新增量爲:
對於學習率選擇的問題,一般較小是能夠保證收斂的,看下圖吧。
5.增量梯度下降
對於4中的梯度下降算法,其缺點是有時收斂速度慢,如果在誤差曲面上存在多個局部極小值,算法不能保證能夠找到全局極小值。爲了改善這些缺點,提出了增量梯度下降算法。增量梯度下降,與4中的梯度下降的不同之處在於,4中對參數w的更新是根據整個樣本中的觀測值的誤差來計算的,而增量梯度下降算法是根據樣本中單個觀測值的誤差來計算w的更新。
6.梯度檢驗
這是一個比較實用的內容,如何確定自己的代碼就一定沒有錯呢?因爲在求梯度的時候是很容易犯錯誤的,我就犯過了,嗨,調了兩天才找出來,一個數組下表寫錯了,要是早一點看看斯坦福大學的深度學習基礎教程就好了,這裏只是截圖一部分,有時間去仔細看看吧。
多層神經網絡
好了有了前面的基礎,我們現在就可以進行實戰了,構造多層神經網絡。
1.Sigmoid神經元
Sigmoid神經元可由下圖表示:
2.神經網絡層
一個三層的BP神經網絡可由下圖表示:
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重新定義如下:
其中outputs是網絡中輸出層單元的集合,tkd,okd是訓練樣本中第d個觀測值在第k個輸出單元的而輸出值。
1)BP算法推導
先引入下列符號:
增量梯度下降算法中,對於每個訓練樣本中第d個觀測的一個輸入權重wij的增量如下表示:
其中Ed是訓練樣本中第d個觀測的誤差,通過對輸出層所有單元的求和得到:
這裏說明一下,神經網絡輸出層的所有單元聯合一起表示一個樣本觀測的訓練值的。假設樣本觀測值爲5種,即5種類別,那麼先驗訓練數據的類別表示爲:1,0,0,0,0;0,1,0,0,0;0,0,1,0,0;0,0,0,1,0;0,0,0,0,1。這樣在對神經網絡訓練時,我們的訓練輸出值的表示也就是類似的,當然基於神經元的結構表示,我們也可以將先驗訓練數據的類別表示中的1換成0.9等。
下面我們就要求梯度了(要分層求解,輸出層,隱藏層),梯度向量中的各元素求解如下:
1)當單元j是一個輸出單元時:
其中:
於是得到:
2)當單元j是一個隱藏層單元時,有如下推導:
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 also:http://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; }