BP神经网络c/c++代码实现

本文详细介绍了BP神经网络的概念,包括其学习规则、反向传播机制和梯度下降法的应用。通过实例演示了如何使用BP算法处理鸢尾花数据集,展示了神经网络的权重调整过程和测试结果。
摘要由CSDN通过智能技术生成
BP神经网络概念
首先从名称中可以看出,Bp神经网络可以分为两个部分,bp和神经网络。bp是 Back Propagation 的简写 ,意思是反向传播。

BP网络能学习和存贮大量的输入-输出模式映射关系,而无需事前揭示描述这种映射关系的数学方程。它的学习规则是使用最速下降法,通过反向传播来不断调整网络的权值和阈值,使网络的误差平方和最小。

其主要的特点是:信号是正向传播的,而误差是反向传播的。

举一个例子,某厂商生产一种产品,投放到市场之后得到了消费者的反馈,根据消费者的反馈,厂商对产品进一步升级,优化,一直循环往复,直到实现最终目的——生产出让消费者更满意的产品。产品投放就是“信号前向传播”,消费者的反馈就是“误差反向传播”。这就是BP神经网络的核心。

单隐层前馈神经网络,与多隐层前馈神经网络(最下层为输入层,最上层为输出层)


BP神经网络公式推导
第 j 个输出神经元的输入: y ^ j = f ( β j − θ j ) 其中 θ j 为输出层的阈值 第j个输出神经元的输入:\hat{y}_{j}=f(\beta _{j}-\theta _{j}) 其中\theta _{j}为输出层的阈值
第j个输出神经元的输入: 
y
^

  
j

 =f(β 
j

 −θ 
j

 )其中θ 
j

 为输出层的阈值

第 h 个隐层神经元的输出: b h = f ( α h − γ h ) 其中 γ h 为隐藏层的阈值 第h个隐层神经元的输出:b_{h}=f(\alpha _{h}-\gamma _{h})其中\gamma _{h}为隐藏层的阈值
第h个隐层神经元的输出:b 
h

 =f(α 
h

 −γ 
h

 )其中γ 
h

 为隐藏层的阈值


假设隐层和输出层神经元都使用Sigmoid函数


其中BP神经网络采用梯度下降法来调整参数。梯度下降在机器学习中应用十分广泛,它的主要目的是通过迭代找到目标函数的最小值,或者收敛到最小值。梯度下降的基本过程就和下山的场景很类似。首先我们有一个可微分的函数,这个函数就代表着一座山。目标是找到这个函数的最小值,也就是山底。那最快的下山的方式就是找到当前位置最陡峭的方向,然后沿着此方向向下走,对应到函数中,就是找到给定点的梯度,然后朝着梯度相反的方向,就能让函数值下降的最快。
所以,我们重复利用这个方法,反复求取梯度,最后就能到达局部的最小值


标准BP算法伪代码如下

学习率 η \etaη控制着算法每一轮迭代中的更新步长,若 η \etaη太大可能会错过 最低点,容易产生震荡;若 η \etaη太小则收敛速度可能过慢.


BP神经网络的应用
针对鸢尾花数据集构建一个神经网络


数据预处理后得到(其中Iris-setosa:0.000000,Iris-versicolor:0.500000,Iris-virginica:1.000000)

针对鸢尾花数据集构建一个神经网络
因数据有四个属性,所以输入层为4个神经元加1个“哑结点”,
鸢尾花是三分类问题,输出层用一个神经元
隐层用3个神经元加1个“哑结点” ,隐层神经元数量是可以调整的

BP算法的实现过程
S1: 从文件读入训练样本,存放到 x[N][M+1] 和 y[N]中
S2: 初始化权重 v[M+1][H] 和 w[H+1]
S3: 开始训练神经网络
3-1:对每一个样本进行以下计算(最外层循环)
3-2:计算隐层的输出,结果存放在b[H+1]中
3-3:计算最后的输出,结果存放在ty[N]中
3-4:按公式,调整权重w[H+1]
3-5:按公式,调整权重v[M+1][H]
3-6:计算这个样本的误差
3-7:累计所有样本的总误差
S4: 判断总误差是否小于某个给定值(如1e-4),
如果误差大于该值,则重复S3,否则结束训练
S5: 将训练好的权重 v[M+1][H] 和 w[H+1] 输出并存入文件
S6: 用测试样本集考查神经网络分类的正确率
具体代码如下:

#include <bits/stdc++.h>
using namespace std;
const int N=15;//训练样本总数
const int M=4;//一个样本的属性个数
const int H=3;//隐藏神经元个数
const double E=0.9;//学习率
double x[N][M+1];//x为样本输入,x[i][0]=-1
double w[H+1];//隐层到输出层的权重,用w[0]表示阈值
double v[M+1][H+1];//输入层到隐层的权重,v[0][i]表示阈值
double y[N];//训练样本的已知分类标记
double b[H+1];//隐层的计算输出
double ty[N];//神经网络计算输出
double sigmoid(double x) {
    return 1.0/(1.0+exp(-1*x));
}
void input() {
    freopen("data3.txt","r",stdin);//将测试样本数据放入data3.txt文件中,并打开读入
    int k=0;
    while(cin>>x[k][1]>>x[k][2]>>x[k][3]>>x[k][4]>>y[k])k++;//输入样本
    for(int i=0; i<=H; i++) {//参数随机初始化
        int r=rand()%101;
        w[i]=r*1.0/100;
    }
    for(int i=0; i<N; i++)x[i][0]=-1;//哑结点初始化
    b[0]=-1;
    for(int i=0; i<=M; i++) {
        for(int j=1; j<=H; j++) {
            int r=rand()%101;
            v[i][j]=r*1.0/100;
        }
    }
}
double g(int j) {
    return ty[j]*(1-ty[j])*(y[j]-ty[j]);
}
double e(int h,int j) {
    return b[h]*(1-b[h])*w[h]*g(j);
}
double dw(int h,int j) {//计算w的调整度
    return E*g(j)*b[h];
}
double dv(int i,int h,int j) {//计算v的调整度
    return E*e(h,j)*x[j][i];
}
void BP() {
    double sume=100;
    while(sume>1e-2) {
        sume=0;
        for(int k=0; k<N; k++) {
            for(int i=1; i<=H; i++) {//计算每一隐层输出
                b[i]=0;
                for(int j=0; j<=M; j++) {
                    b[i]+=x[k][j]*v[j][i];
                }
                b[i]=sigmoid(b[i]);
            }
            ty[k]=0;
            for(int i=0; i<=H; i++) {//计算每一输出层输出
                ty[k]+=b[i]*w[i];
            }
            ty[k]=sigmoid(ty[k]);
            for(int i=0; i<=H; i++) {//计算权重w
                w[i]+=dw(i,k);
            }
            for(int i=0; i<=M; i++) { //计算权重v
                for(int j=1; j<=H; j++) {
                    v[i][j]+=dv(i,j,k);
                }
            }
            //计算误差
            sume+=abs(ty[k]-y[k]);
        }
        cout<<sume<<endl;
    }
}
void test() {//测试样本结果 
    vector<vector<double>> v= {
        { 0,13.2526,1.82329,-5.42751},
        {  0,-13.4099,-2.62366,-1.10806},
        {  0,3.30317,-5.1121,-13.1777},
        {  0,14.5049,0.292959,-3.93443},
        { 0,14.4629,1.01794,4.40286}
    };
    vector<double> w= {
        8.47695,
        14.7531,
        4.77114,
        12.343
    };
    double sume=0;
    for(int k=0; k<N; k++) {
        for(int i=1; i<=H; i++) {//计算每一隐层输出
            b[i]=0;
            for(int j=0; j<=M; j++) {
                b[i]+=x[k][j]*v[j][i];
            }
            b[i]=sigmoid(b[i]);
        }
        ty[k]=0;
        for(int i=0; i<=H; i++) {//计算每一输出层输出
            ty[k]+=b[i]*w[i];
        }
        ty[k]=sigmoid(ty[k]);
        sume+=abs(ty[k]-y[k]);
        cout<<ty[k]<<endl;//测试输出 
    }
    cout<<sume<<endl;//误差总和输出 
}
int main() {
    input();//输入数据并且初始化
    BP();
    for(int i=0; i<=M; i++) {
        for(int j=0; j<=H; j++) {
            cout<<" "<<v[i][j];
        }
        cout<<endl;
    }
    cout<<endl;
    for(int i=0; i<=H; i++) {
        cout<<w[i]<<endl;
    }
    test();
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
输出结果(w[M+1][H+1]和v[H+1])

测试结果(每个测试样本的模拟输出,以及最后的误差总和)

可以看到模拟输出与样本结果误差相似
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/waitingforone/article/details/138288975

前一段时间做了一个数字识别的小系统,基于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__
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
代码中定义了一个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__
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.


实现文件:

// 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;
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.

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__
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.


实现文件:

复制 
// 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;
}
-----------------------------------
©著作权归作者所有:来自51CTO博客作者Remyspot的原创作品,谢绝转载,否则将追究法律责任
BP神经网络原理及C++实战
https://blog.51cto.com/remyspot/1575156

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值