您的位置:首页 > 理论基础 > 计算机网络

反向传播神经网络(BP网络)介绍及Java实现

2017-04-01 21:03 281 查看
在之前的文章中我简单展望过人工智能。并提出过神经网络是一个核心技术,今天我就要介绍神经网络的经典算法反向传播神经网络(Backpropagation Neural Network)

BP网络由DE Rumelhart, GE Hinton等于1986年提出(值得注意的是GE Hinton也是深度学习的奠基人),它是最经典的一个神经网络算法。神经网络是一组连接的输入/输出神经元,每个连接都有相对应的权重。如图,是一个典型的多层前馈(multilayer feed-forward)神经网络结构。它由一个输入层、一个输出层和多个隐藏层组成。在给出输入后,经过层层网络,最后得到一个输出。而神经网络算法本质就是找为各连接找出合适的权值



网络计算过程

在讨论BP算法前,还是先明确一下,在上面的网络中得到权值后,具体的计算过程到底是怎么回事。首先明确,上图中的输入层其实是我们要分类的样本的各特征,而隐藏层和输出层每个圆圈都代表了一个sigmoid单元。如下图所示,sigmoid的计算过程为:首先得到各输入与常数1的和net=∑ni=0wixi,然后用sigmoid函数计算输出out=σ(net)=11+e−net。这便是单各单元的计算方式,而整个网络便是前一层作为输入与后一层的每各单元连接,计算出各单元的输出后,再把这一层做为输入传递到后一层。。。



算法过程

下面先给出BP神经网络的算法过程



算法中的终止条件可以是到达一定的迭代次数或到达一定的准确度。可以看出算法本身并不复杂,后面要讲的推导也不会比高等数学的题难推,就是几个数学公式比较复杂。可是算法却有十分神奇的能力,所以有机会一定把数学学好

算法推导

梯度下降

可以从上面的算法过程看出,BP算法其实就是不断根据输出与实际值的误差来更新各连接权值。那么什么样的权值是最好的,我们中学应该都学过最小二乘法,即每一个输出与实际值差值的平方的和最小,即误差函数最小。在BP网络中,误差这样定义:

E(w⃗ )=12∑d∈D∑k∈outputs(tkd−okd)2

所以我们的目的是使误差值最小。那么如何使该误差函数取的最小值,通过计算E关于w的导数,列方程使所有导数都为零?显然是不可行的。所以我们要利用计算机的计算能力去一步步逼近最小值,这里用的方法就是梯度下降(Gradient descend),即朝着梯度下降的方向逐步偏移。

迭代方法推导

在正式推导前我先说明:其实BP算法有中形式:标准形随机梯度下降方法。这一点刚开始也让我疑惑了很久:为什么上面每一个样例都要迭代一次?不是应该一次扫一遍数据库,计算出总误差再迭代吗?后面那种其实就是标准形式的梯度下降方法,可是这样计算量大,收敛的速度慢。而每一个样例都迭代速度快,而且还可以避免陷入局部最优。下面我介绍的就是随机梯度下降方式的推导过程。可以看出这种方式的误差函数和上面那个有区别。但后面推导过程其实非常相似。

误差函数:



正式推导前先明确几个标记,可以使后面推导更简洁:



输出单元的误差:



隐藏单元的误差:



可以看出误差更新的过程是从后往前传播,所以算法的名字为反向传播算法

Java实现

算法理解起来可能比较困难,但是整体过程并不复杂,用Java实现,总代码不会超过100行即可实现,但是代码也比较绕,理解起来也会费点劲。下面给出我的实现,或者可以下载我的整个工程,我这个项目是用BPNN做手写体识别,最后效果并不好,不过算法本身是正确的,我用它做过习题。

public double layer[][];//store the output of every unit
public double layerErr[][];//store the error of every unit
public double layerWeight[][][];//store weights
public double layerWeightDelta[][][];//store number of weights adjust
public double mobp;// momentum rate
public double rate;// adjust steps length

/**
* initialize the BPNN
* @param layernum units number of every layer
* @param rate adjust steps length
* @param mobp momentum rate
*/
public BpNN(int[] layernum,double rate,double mobp){
this.rate=rate;
this.mobp=mobp;
layer=new double[layernum.length][];
layerErr=new double[layernum.length][];
layerWeight=new double[layernum.length][][];
layerWeightDelta=new double[layernum.length][][];
for(int l=0;l<layernum.length;l++){
layer[l]=new double[layernum[l]];
layerErr[l]=new double[layernum[l]];
//          layerWeight[l]=new double[layernum[l]+1][];
//          layerWeightDelta[l]=new double[layernum[l]+1][];
if(l+1<layernum.length){
layerWeight[l]=new double[layernum[l]+1][layernum[l+1]];
layerWeightDelta[l]=new double[layernum[l]+1][layernum[l+1]];
for(int x=0;x<layernum[l]+1;x++){
for(int y=0;y<layernum[l+1];y++){
layerWeight[l][x][y]=0.1;
}
}
}
}
}
/**
* compute the output from front to back
* @param in input vector
* @return output vector
*
*/
public double[] compute(double[] in){
for(int l=1;l<layer.length;l++){
for(int j=0;j<layer[l].length;j++){
double z=layerWeight[l-1][layer[l-1].length][j];//constant multiply weights
for(int x=0;x<layer[l-1].length;x++){
layer[l-1][x]=l==1?in[x]:layer[l-1][x];
z+=layerWeight[l-1][x][j]*layer[l-1][x];
}
layer[l][j]=1/(1+Math.exp(-z));
}
}
return layer[layer.length-1];
}
/**
* update weight from back to front
* @param tar target values (instances)
*/
public void updateWeight(double[] tar){
int l=layer.length-1;
for(int y=0;y<layer[l].length;y++){
layerErr[l][y]=layer[l][y]*(1-layer[l][y])*(tar[y]-layer[l][y]);
}
while(l-->0){
for(int j=0;j<layerErr[l].length;j++){
double z=0;
for(int i=0;i<layerErr[l+1].length;i++){
z+=l>0?layerErr[l+1][i]*layerWeight[l][j][i]:0;//0 layer is input layer, need not calculate error
layerWeightDelta[l][j][i]=mobp*layerWeightDelta[l][j][i]+rate*layerErr[l+1][i]*layer[l][j];//delta weight with momentum
layerWeight[l][j][i]+=layerWeightDelta[l][j][i];//update weight of units
if(j==layerErr[l].length-1){//update weights of constant
layerWeightDelta[l][j+1][i]=mobp*layerWeightDelta[l][j+1][i]+rate*layerErr[l+1][i];
layerWeight[l][j+1][i]+=layerWeightDelta[l][j+1][i];
}
}
layerErr[l][j]=z*layer[l][j]*(1-layer[l][j]);//compute error
}
}
}

public void train(double[] in, double[] tar) {
compute(in);
updateWeight(tar);
}
public double[][][] getWeight(){
return layerWeight;
}
public void setWeight(double[][][] weight){
layerWeight=weight;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息