BP网络算法及其改进

来源:互联网 发布:网络推广图片 编辑:程序博客网 时间:2024/05/16 16:10

书上的一个例子,是要识别英文字符C、I、T。

  

则XC=(1,1,1,1,0,0,1,1,1),XI=(0,1,0,0,1,0,0,1,0),XT=(1,1,1,0,1,0,0,1,0)。

1.标准BP算法

网络图我就不画了。

输入层X=(x0,x1,x2,...xi,...,xn)

隐藏层Y=(y0,y1,y2,...,yj,...,ym)

x0=y0=-1

输出层O=(o1,o2,...,ok,...,ol)

期望输出D=(d1,d2,...,dk,...,dl)

输入层到隐藏层的权重Vij

隐藏层到输出层的权重Wjk

对于输出层:

净输入

输出

对于隐藏层:

净输入

输出

变换函数f(x)采用单极形Sigmoid函数:

函数具有性质:

 采用批训练法,误差是所有样本的均方误差和:

显然误差E是网络权值W和V的函数,E分别对W和V求偏导得到E的梯度,要减小误差E,则权值W和V调整的方向就应该是误差梯度的反方向。结合(1)式(2)式可得权值调整公式:

 

上代码:

首先从复旦语料库全体训练集中随机挑取360个训练样本(每类40个)作为神经网络的输入

#/usr/bin/perlsrand();my $outf="/home/orisun/master/fudan_corpus/tc_ann.txt";open OUT,">$outf" or die "Can't open file:$!";my $dir_prefix="/home/orisun/master/fudan_corpus/train_vec/";my @cat=qw/C3-Art_ws C7-History_ws C11-Space_ws C19-Computer_ws C31-Enviornment_ws C32-Agriculture_ws C34-Economy_ws C38-Politics_ws C39-Sports_ws/;foreach(0..$#cat){    $dir=$dir_prefix.$cat[$_];    opendir(DIR,"$dir") or die "Can't open directory:$!";    @files=grep {/^[^\.]/} readdir(DIR);    #文件不能以.开头    foreach(1..40){        $index=rand($#files);        print OUT $dir."/".$files[$index]."\n";    }}close OUT;


 

#include<iostream>#include<cmath>#include<cstdlib>#include<ctime>#include<cassert> using namespace std; const int dim=9;    //样本向量的维度const int in_count=dim+1;   //输入层节点数const int hidden_count=5;   //隐藏层节点数const int out_count=3;  //输出层节点数const int P=3;  //样本数const int iter_lim=1500;    //最大迭代次数const double Epsilon=0.03;  //允许误差double Eta=0.2; //学习率double W[hidden_count][out_count]={0};  //从隐藏层到输出层的权值double V[in_count][hidden_count-1]={0}; //从输入层到隐藏层的权值 /** * 单极性Sigmoid函数 */inline double sigmoid(double activation,double response){    double ex=-activation/response;    return 1.0/(pow(M_E,ex)+1);} /** * 初始网络权值W和V,赋予[0,1]上的随机数 */void initParam(){    srand(time(0));    for(int i=0;i<hidden_count;++i){        for(int j=0;j<out_count;++j)            W[i][j]=rand()/(double)RAND_MAX;    }    for(int i=0;i<in_count;++i){        for(int j=0;j<hidden_count-1;++j)            V[i][j]=rand()/(double)RAND_MAX;    }} void printWeight(){    cout<<"W="<<endl;    for(int i=0;i<hidden_count;++i){        for(int j=0;j<out_count;++j)            cout<<W[i][j]<<"\t";        cout<<endl;    }    cout<<"V="<<endl;    for(int i=0;i<in_count;++i){        for(int j=0;j<hidden_count-1;++j)            cout<<V[i][j]<<"\t";        cout<<endl;    }} /** * 给定输入,求网络的输出 */void getOutput(double (&input)[in_count],double (&Y)[hidden_count],double (&output)[out_count]){    assert(input[0]==-1);    assert(Y[0]==-1);    for(int j=1;j<hidden_count;++j){        double net=0.0;     //隐藏层的净输入        for(int i=0;i<in_count;++i)            net+=input[i]*V[i][j];        Y[j]=sigmoid(net,1);    //把净输入抛给S形函数,得到隐藏层的输出    }    for(int k=0;k<out_count;++k){        double net=0.0;     //输出层的净输入        for(int j=0;j<hidden_count;++j)            net+=Y[j]*W[j][k];        output[k]=sigmoid(net,1);   //把净输入抛给S形函数,得到输出层的输出        //cout<<output[k]<<"\t";    }    //cout<<endl;} /** * 批训练法根据样本总体误差调整权重W和V */void adjustWeight(double (&input)[P][in_count],double (&Y)[P][hidden_count],                    double (&output)[P][out_count],double (&D)[P][out_count]){    double delte_W[hidden_count][out_count]={0};    //数组必须显式地赋0,否则它的初始值是一个随机的数    double delte_V[in_count][hidden_count]={0};    for(int j=0;j<hidden_count;++j){        for(int k=0;k<out_count;++k){            for(int p=0;p<P;++p){                delte_W[j][k]+=(D[p][k]-output[p][k])*output[p][k]*(1-output[p][k])*Y[p][j];            }            delte_W[j][k]*=Eta;            W[j][k]+=delte_W[j][k];        }    }    for(int i=0;i<in_count;++i){        for(int j=0;j<hidden_count;++j){            for(int p=0;p<P;++p){                double tmp=0.0;                for(int k=0;k<out_count;++k){                    tmp+=(D[p][k]-output[p][k])*output[p][k]*(1-output[p][k])*W[j][k];                }                delte_V[i][j]+=tmp*Y[p][j]*(1-Y[p][j])*input[p][i];            }            delte_V[i][j]*=Eta;            V[i][j]+=delte_V[i][j];        }    }} /** * 计算所有样本的均方误差和 */double getMSE(double (&output)[P][out_count],double (&D)[P][out_count]){    double error=0.0;    for(int p=0;p<P;++p){        for(int k=0;k<out_count;++k){            error+=pow((D[p][k]-output[p][k]),2);        }    }    error/=2;    return error;} int main(){    initParam();    double X[P][in_count]={{-1,1,1,1,1,0,0,1,1,1},      //"C"                            {-1,0,1,0,0,1,0,0,1,0},     //"I"                            {-1,1,1,1,0,1,0,0,1,0}};    //"T"    double D[P][out_count]={{1,0,0},        //"C"                            {0,1,0},        //"I"                            {0,0,1}};       //"T"    double Y[P][hidden_count]={{-1},{-1},{-1}};    double O[P][out_count]={0};         int iteration=iter_lim;    //printWeight();    while(iteration-->0){        for(int p=0;p<P;++p)            getOutput(X[p],Y[p],O[p]);        double err=getMSE(O,D);        cout<<"第"<<iter_lim-1-iteration<<"次迭代误差:"<<err<<endl;        //printWeight();        if(err<Epsilon){     //如果误差小于允许的误差,则退出迭代            cout<<"误差小于允许误差,迭代退出。"<<endl;            break;        }        adjustWeight(X,Y,O,D);    }         //使用原样本进行测试    double Out[out_count]={0};    for(int p=0;p<P;++p){        getOutput(X[p],Y[p],Out);        for(int k=0;k<out_count;k++)            cout<<Out[k]<<"\t";        cout<<endl;    }         return 0;}

运行输出:

……

……

第1262次迭代误差:0.0304531
第1263次迭代误差:0.0304104
第1264次迭代误差:0.0303678
第1265次迭代误差:0.0303252
第1266次迭代误差:0.0302828
第1267次迭代误差:0.0302405
第1268次迭代误差:0.0301984
第1269次迭代误差:0.0301563
第1270次迭代误差:0.0301143
第1271次迭代误差:0.0300725
第1272次迭代误差:0.0300307
第1273次迭代误差:0.0299891
误差小于允许误差,迭代退出。
0.941014 0.0105744 0.0708488 
0.0195598 0.901582 0.115183 
0.066168 0.0872207 0.873328

2.增加动量项

 该方法是从前一次的权值调整量中取出一部分叠加到本次权值调整量中。

其中是动量系数,在(0,1)上取值。动量项反应了以前积累的调整经验,对于t时刻的调整起阻尼作用。当误差曲面出现骤然起伏时,可以减小振荡趋势,提高训练速度。

目前BP算法中都增加了动量项,以至于有动量项的BP算法成为一种新的标准算法。

#include<iostream>#include<cmath>#include<cstdlib>#include<ctime>#include<cassert> using namespace std; const int dim=9;    //样本向量的维度const int in_count=dim+1;   //输入层节点数const int hidden_count=5;   //隐藏层节点数const int out_count=3;  //输出层节点数const int P=3;  //样本数const int iter_lim=1500;    //最大迭代次数const double Epsilon=0.03;  //允许误差double Eta=0.2; //学习率double W[hidden_count][out_count]={0};  //从隐藏层到输出层的权值double V[in_count][hidden_count-1]={0}; //从输入层到隐藏层的权值double alpha=0.8;   //动量系数double pre_delte_W[hidden_count][out_count]={0};    //上一次的权值调整量double pre_delte_V[in_count][hidden_count]={0};     //数组必须显式地赋0,否则它的初始值是一个随机的数 /** * 单极性Sigmoid函数 */inline double sigmoid(double activation,double response){    double ex=-activation/response;    return 1.0/(pow(M_E,ex)+1);} /** * 初始网络权值W和V,赋予[0,1]上的随机数 */void initParam(){    srand(time(0));    for(int i=0;i<hidden_count;++i){        for(int j=0;j<out_count;++j)            W[i][j]=rand()/(double)RAND_MAX;    }    for(int i=0;i<in_count;++i){        for(int j=0;j<hidden_count-1;++j)            V[i][j]=rand()/(double)RAND_MAX;    }} void printWeight(){    cout<<"W="<<endl;    for(int i=0;i<hidden_count;++i){        for(int j=0;j<out_count;++j)            cout<<W[i][j]<<"\t";        cout<<endl;    }    cout<<"V="<<endl;    for(int i=0;i<in_count;++i){        for(int j=0;j<hidden_count-1;++j)            cout<<V[i][j]<<"\t";        cout<<endl;    }} /** * 给定输入,求网络的输出 */void getOutput(double (&input)[in_count],double (&Y)[hidden_count],double (&output)[out_count]){    assert(input[0]==-1);    assert(Y[0]==-1);    for(int j=1;j<hidden_count;++j){        double net=0.0;     //隐藏层的净输入        for(int i=0;i<in_count;++i)            net+=input[i]*V[i][j];        Y[j]=sigmoid(net,1);    //把净输入抛给S形函数,得到隐藏层的输出    }    for(int k=0;k<out_count;++k){        double net=0.0;     //输出层的净输入        for(int j=0;j<hidden_count;++j)            net+=Y[j]*W[j][k];        output[k]=sigmoid(net,1);   //把净输入抛给S形函数,得到输出层的输出        //cout<<output[k]<<"\t";    }    //cout<<endl;} /** * 批训练法根据样本总体误差调整权重W和V */void adjustWeight(double (&input)[P][in_count],double (&Y)[P][hidden_count],                    double (&output)[P][out_count],double (&D)[P][out_count]){    double delte_W[hidden_count][out_count]={0};    //数组必须显式地赋0,否则它的初始值是一个随机的数    double delte_V[in_count][hidden_count]={0};    for(int j=0;j<hidden_count;++j){        for(int k=0;k<out_count;++k){            for(int p=0;p<P;++p){                delte_W[j][k]+=(D[p][k]-output[p][k])*output[p][k]*(1-output[p][k])*Y[p][j];            }            delte_W[j][k]*=Eta;            W[j][k]+=delte_W[j][k]+alpha*pre_delte_W[j][k];            pre_delte_W[j][k]=delte_W[j][k];        }    }    for(int i=0;i<in_count;++i){        for(int j=0;j<hidden_count;++j){            for(int p=0;p<P;++p){                double tmp=0.0;                for(int k=0;k<out_count;++k){                    tmp+=(D[p][k]-output[p][k])*output[p][k]*(1-output[p][k])*W[j][k];                }                delte_V[i][j]+=tmp*Y[p][j]*(1-Y[p][j])*input[p][i];            }            delte_V[i][j]*=Eta;            V[i][j]+=delte_V[i][j]+alpha*pre_delte_V[i][j];            pre_delte_V[i][j]=delte_V[i][j];        }    }} /** * 计算所有样本的均方误差和 */double getMSE(double (&output)[P][out_count],double (&D)[P][out_count]){    double error=0.0;    for(int p=0;p<P;++p){        for(int k=0;k<out_count;++k){            error+=pow((D[p][k]-output[p][k]),2);        }    }    error/=2;    return error;} int main(){    initParam();    double X[P][in_count]={{-1,1,1,1,1,0,0,1,1,1},      //"C"                            {-1,0,1,0,0,1,0,0,1,0},     //"I"                            {-1,1,1,1,0,1,0,0,1,0}};    //"T"    double D[P][out_count]={{1,0,0},        //"C"                            {0,1,0},        //"I"                            {0,0,1}};       //"T"    double Y[P][hidden_count]={{-1},{-1},{-1}};    double O[P][out_count]={0};         int iteration=iter_lim;    //printWeight();    while(iteration-->0){        for(int p=0;p<P;++p)            getOutput(X[p],Y[p],O[p]);                 double err=getMSE(O,D);        cout<<"第"<<iter_lim-1-iteration<<"次迭代误差:"<<err<<endl;        //printWeight();        if(err<Epsilon){     //如果误差小于允许的误差,则退出迭代            cout<<"误差小于允许误差,迭代退出。"<<endl;            break;        }        adjustWeight(X,Y,O,D);    }         //使用原样本进行测试    double Out[out_count]={0};    for(int p=0;p<P;++p){        getOutput(X[p],Y[p],Out);        for(int k=0;k<out_count;k++)            cout<<Out[k]<<"\t";        cout<<endl;    }         return 0;}


只需要五百多次迭代就退出了。

3.自适应调节学习率

 学习率实际上是步长。从误差曲面上看,在平坦区域内太小会使训练次数增加;在误差变化剧烈的区域内太大会跨过较窄的坑凹处,使训练出现振荡,反而增加了迭代次数。

这里给出一种自适应调整的方法:经过权值调整后如果总误差上升,则本次调整无效,乘以(<0),当总误差开始下降后,再让乘以(>0)。

#include<iostream>#include<cmath>#include<cstdlib>#include<ctime>#include<cassert> using namespace std; const int dim=9;    //样本向量的维度const int in_count=dim+1;   //输入层节点数const int hidden_count=5;   //隐藏层节点数const int out_count=3;  //输出层节点数const int P=3;  //样本数const int iter_lim=1500;    //最大迭代次数const double Epsilon=0.03;  //允许误差double Eta=0.2; //学习率const double beta=0.9;      //调整无效时减小学习率const double theta=1.1;     //调整有效时增大学习率double W[hidden_count][out_count]={0};  //从隐藏层到输出层的权值double V[in_count][hidden_count-1]={0}; //从输入层到隐藏层的权值double alpha=0.8;   //动量系数double pre_delte_W[hidden_count][out_count]={0};    //上一次的权值调整量double pre_delte_V[in_count][hidden_count]={0};     //数组必须显式地赋0,否则它的初始值是一个随机的数double pre_E=RAND_MAX;      //上一次的系统误差 /** * 单极性Sigmoid函数 */inline double sigmoid(double activation,double response){    double ex=-activation/response;    return 1.0/(pow(M_E,ex)+1);} /** * 初始网络权值W和V,赋予[0,1]上的随机数 */void initParam(){    srand(time(0));    for(int i=0;i<hidden_count;++i){        for(int j=0;j<out_count;++j)            W[i][j]=rand()/(double)RAND_MAX;    }    for(int i=0;i<in_count;++i){        for(int j=0;j<hidden_count-1;++j)            V[i][j]=rand()/(double)RAND_MAX;    }} void printWeight(){    cout<<"W="<<endl;    for(int i=0;i<hidden_count;++i){        for(int j=0;j<out_count;++j)            cout<<W[i][j]<<"\t";        cout<<endl;    }    cout<<"V="<<endl;    for(int i=0;i<in_count;++i){        for(int j=0;j<hidden_count-1;++j)            cout<<V[i][j]<<"\t";        cout<<endl;    }} /** * 给定输入,求网络的输出 */void getOutput(double (&input)[in_count],double (&Y)[hidden_count],double (&output)[out_count]){    assert(input[0]==-1);    assert(Y[0]==-1);    for(int j=1;j<hidden_count;++j){        double net=0.0;     //隐藏层的净输入        for(int i=0;i<in_count;++i)            net+=input[i]*V[i][j];        Y[j]=sigmoid(net,1);    //把净输入抛给S形函数,得到隐藏层的输出    }    for(int k=0;k<out_count;++k){        double net=0.0;     //输出层的净输入        for(int j=0;j<hidden_count;++j)            net+=Y[j]*W[j][k];        output[k]=sigmoid(net,1);   //把净输入抛给S形函数,得到输出层的输出        //cout<<output[k]<<"\t";    }    //cout<<endl;} /** * 批训练法根据样本总体误差调整权重W和V */void adjustWeight(double (&input)[P][in_count],double (&Y)[P][hidden_count],                    double (&output)[P][out_count],double (&D)[P][out_count]){    double delte_W[hidden_count][out_count]={0};    //数组必须显式地赋0,否则它的初始值是一个随机的数    double delte_V[in_count][hidden_count]={0};    for(int j=0;j<hidden_count;++j){        for(int k=0;k<out_count;++k){            for(int p=0;p<P;++p){                delte_W[j][k]+=(D[p][k]-output[p][k])*output[p][k]*(1-output[p][k])*Y[p][j];            }            delte_W[j][k]*=Eta;            W[j][k]+=delte_W[j][k]+alpha*pre_delte_W[j][k];     //加入动量项            pre_delte_W[j][k]=delte_W[j][k];        }    }    for(int i=0;i<in_count;++i){        for(int j=0;j<hidden_count;++j){            for(int p=0;p<P;++p){                double tmp=0.0;                for(int k=0;k<out_count;++k){                    tmp+=(D[p][k]-output[p][k])*output[p][k]*(1-output[p][k])*W[j][k];                }                delte_V[i][j]+=tmp*Y[p][j]*(1-Y[p][j])*input[p][i];            }            delte_V[i][j]*=Eta;            V[i][j]+=delte_V[i][j]+alpha*pre_delte_V[i][j];     //加入动量项            pre_delte_V[i][j]=delte_V[i][j];        }    }} /** * 计算所有样本的均方误差和 */double getMSE(double (&output)[P][out_count],double (&D)[P][out_count]){    double error=0.0;    for(int p=0;p<P;++p){        for(int k=0;k<out_count;++k){            error+=pow((D[p][k]-output[p][k]),2);        }    }    error/=2;    return error;} int main(){    initParam();    double X[P][in_count]={{-1,1,1,1,1,0,0,1,1,1},      //"C"                            {-1,0,1,0,0,1,0,0,1,0},     //"I"                            {-1,1,1,1,0,1,0,0,1,0}};    //"T"    double D[P][out_count]={{1,0,0},        //"C"                            {0,1,0},        //"I"                            {0,0,1}};       //"T"    double Y[P][hidden_count]={{-1},{-1},{-1}};    double O[P][out_count]={0};         int iteration=iter_lim;    //printWeight();    while(iteration-->0){        for(int p=0;p<P;++p)            getOutput(X[p],Y[p],O[p]);                 double err=getMSE(O,D);        cout<<"第"<<iter_lim-1-iteration<<"次迭代误差:"<<err<<endl;        //printWeight();        if(err<Epsilon){     //如果误差小于允许的误差,则退出迭代            cout<<"误差小于允许误差,迭代退出。"<<endl;            break;        }        //动态调整学习率        if(err>pre_E)            Eta*=beta;        else if(err<pre_E)            Eta*=theta;        pre_E=err;        adjustWeight(X,Y,O,D);    }         //使用原样本进行测试    double Out[out_count]={0};    for(int p=0;p<P;++p){        getOutput(X[p],Y[p],Out);        for(int k=0;k<out_count;k++)            cout<<Out[k]<<"\t";        cout<<endl;    }         return 0;}


只进行了四十多次迭代就退出了。

4.引入陡度因子

误差曲面进入平坦区是因为神经元输出进入了变换函数的饱和区。

接近于0,而Dk-Ok仍然较大时,可以判断已进入了平坦区,此时我们令>1就可以压缩神经元的净输入,使之退出变换函数的饱和区。当退出平坦区后再令=1。

当然引入陡度因子后,(1)就改变了,相应的(2)、(3)、(4)式都要变,这里我们忽略该变化,权值调整公式还按照原先的来。

该方法对提高BP算法的收敛速度十分有效。

#include<iostream>#include<cmath>#include<cstdlib>#include<ctime>#include<cassert> using namespace std; const int dim=9;    //样本向量的维度const int in_count=dim+1;   //输入层节点数const int hidden_count=5;   //隐藏层节点数const int out_count=3;  //输出层节点数const int P=3;  //样本数const int iter_lim=1500;    //最大迭代次数const double Epsilon=0.03;  //允许误差double Eta=0.2; //学习率const double beta=0.9;      //调整无效时减小学习率const double theta=1.1;     //调整有效时增大学习率double lambda=1;        //陡度因子double W[hidden_count][out_count]={0};  //从隐藏层到输出层的权值double V[in_count][hidden_count-1]={0}; //从输入层到隐藏层的权值double alpha=0.8;   //动量系数double pre_delte_W[hidden_count][out_count]={0};    //上一次的权值调整量double pre_delte_V[in_count][hidden_count]={0};     //数组必须显式地赋0,否则它的初始值是一个随机的数double pre_E=RAND_MAX;      //上一次的系统误差 /** * 单极性Sigmoid函数 */inline double sigmoid(double activation,double response){    double ex=-activation/response;    return 1.0/(pow(M_E,ex)+1);} /** * 初始网络权值W和V,赋予[0,1]上的随机数 */void initParam(){    srand(time(0));    for(int i=0;i<hidden_count;++i){        for(int j=0;j<out_count;++j)            W[i][j]=rand()/(double)RAND_MAX;    }    for(int i=0;i<in_count;++i){        for(int j=0;j<hidden_count-1;++j)            V[i][j]=rand()/(double)RAND_MAX;    }} void printWeight(){    cout<<"W="<<endl;    for(int i=0;i<hidden_count;++i){        for(int j=0;j<out_count;++j)            cout<<W[i][j]<<"\t";        cout<<endl;    }    cout<<"V="<<endl;    for(int i=0;i<in_count;++i){        for(int j=0;j<hidden_count-1;++j)            cout<<V[i][j]<<"\t";        cout<<endl;    }} /** * 给定输入,求网络的输出 */void getOutput(double (&input)[in_count],double (&Y)[hidden_count],double (&output)[out_count]){    assert(input[0]==-1);    assert(Y[0]==-1);    for(int j=1;j<hidden_count;++j){        double net=0.0;     //隐藏层的净输入        for(int i=0;i<in_count;++i)            net+=input[i]*V[i][j];        Y[j]=sigmoid(net,lambda);   //把净输入抛给S形函数,得到隐藏层的输出    }    for(int k=0;k<out_count;++k){        double net=0.0;     //输出层的净输入        for(int j=0;j<hidden_count;++j)            net+=Y[j]*W[j][k];        output[k]=sigmoid(net,lambda);  //把净输入抛给S形函数,得到输出层的输出        //cout<<output[k]<<"\t";    }    //cout<<endl;} /** * 批训练法根据样本总体误差调整权重W和V */void adjustWeight(double (&input)[P][in_count],double (&Y)[P][hidden_count],                    double (&output)[P][out_count],double (&D)[P][out_count]){    double delte_W[hidden_count][out_count]={0};    //数组必须显式地赋0,否则它的初始值是一个随机的数    double delte_V[in_count][hidden_count]={0};    for(int j=0;j<hidden_count;++j){        for(int k=0;k<out_count;++k){            for(int p=0;p<P;++p){                delte_W[j][k]+=(D[p][k]-output[p][k])*output[p][k]*(1-output[p][k])*Y[p][j];            }            delte_W[j][k]*=Eta;            W[j][k]+=delte_W[j][k]+alpha*pre_delte_W[j][k];     //加入动量项            pre_delte_W[j][k]=delte_W[j][k];        }    }    for(int i=0;i<in_count;++i){        for(int j=0;j<hidden_count;++j){            for(int p=0;p<P;++p){                double tmp=0.0;                for(int k=0;k<out_count;++k){                    tmp+=(D[p][k]-output[p][k])*output[p][k]*(1-output[p][k])*W[j][k];                }                delte_V[i][j]+=tmp*Y[p][j]*(1-Y[p][j])*input[p][i];            }            delte_V[i][j]*=Eta;            V[i][j]+=delte_V[i][j]+alpha*pre_delte_V[i][j];     //加入动量项            pre_delte_V[i][j]=delte_V[i][j];        }    }} /** * 计算所有样本的均方误差和 */double getMSE(double (&output)[P][out_count],double (&D)[P][out_count]){    double error=0.0;    for(int p=0;p<P;++p){        for(int k=0;k<out_count;++k){            error+=pow((D[p][k]-output[p][k]),2);        }    }    error/=2;    return error;} int main(){    initParam();    double X[P][in_count]={{-1,1,1,1,1,0,0,1,1,1},      //"C"                            {-1,0,1,0,0,1,0,0,1,0},     //"I"                            {-1,1,1,1,0,1,0,0,1,0}};    //"T"    double D[P][out_count]={{1,0,0},        //"C"                            {0,1,0},        //"I"                            {0,0,1}};       //"T"    double Y[P][hidden_count]={{-1},{-1},{-1}};    double O[P][out_count]={0};         int iteration=iter_lim;    //printWeight();    while(iteration-->0){        for(int p=0;p<P;++p)            getOutput(X[p],Y[p],O[p]);                 double err=getMSE(O,D);        cout<<"第"<<iter_lim-1-iteration<<"次迭代误差:"<<err<<endl;        //printWeight();        if(err<Epsilon){     //如果误差小于允许的误差,则退出迭代            cout<<"误差小于允许误差,迭代退出。"<<endl;            break;        }        //动态调整学习率        if(err>pre_E)            Eta*=beta;        else if(err<pre_E)            Eta*=theta;        pre_E=err;        //动态调整陡度因子        if(err-pre_E<0.0004 && pre_E-err<0.004 && err>0.3) //误差变化量接近于0(进入平坦区),而误差仍很大            lambda=2;        else if(err-pre_E>0.0004 || pre_E-err>0.004)  //退出平坦区            lambda=1;        adjustWeight(X,Y,O,D);    }         //使用原样本进行测试    double Out[out_count]={0};    for(int p=0;p<P;++p){        getOutput(X[p],Y[p],Out);        for(int k=0;k<out_count;k++)            cout<<Out[k]<<"\t";        cout<<endl;    }         return 0;}


还是四十多次迭代后误差就达到了要求。

多层感知器的主要功能

  1. 非线性映射能力
  2. 泛化能力
  3. 容错能力。它允许输入样本上带有较大误差甚至个别错误。

误差曲线与BP算法的局限性

  1. 存在平坦区域
    平坦即梯度小,梯度公式为:
     

    这意味着有3种可能进入平坦区域:Ok充分接近于Dk,此时对应着误差的某个谷点;Ok接近于0;Ok接近于1。
    只要调整方向正确,调整时间足够长,总可以退出平坦区域进入某个谷点。
  2. 存在多个极小点
    极小点的特点是梯度为0,误差曲面的这一特点使以误差梯度降低为权值调整依据的BP算法无法识别局部极小点,因此BP算法经常会陷入局部极小点无法自拔。 
原文来自:博客园(华夏35度)http://www.cnblogs.com/zhangchaoyang 作者:Orisun

原创粉丝点击