我们实验性的用BP算法来解决XOR这样一个简单的问题。这两周一直在看Faussett的Fundamentals of Neural networks, architectures, algorithms and applications这本书。
书上第300页的数据如下:使用2-4-1的拓扑结构,学习速率为0.02,如果使用momentum,mu=0.9,当平方误差(欧氏距离的平方)小于0.05时停止学习。我们不考察0,1这种binary的数据表示,因为-1,+1这种bipolar的数据表示在各种神经网络模型中已经被证实明显优于binary。当使用Nguyen-Widrow的初始化,作者做了8次实验,收敛次数分布从224到285,8次中只有一次不收敛。如果用随机的初始化,收敛次数为387,并没有提到不收敛的几率。如果随机初始化+带动能的训练,收敛次数为38。不知道为什么到308页的时候作者给的参数(alpha=0.75,mu=0.9+binary)根本无法收敛,可能是印刷错误,之前某页也说到“学习速率还是取0.2(跟以前一样)”,可是以前明明是0.02。这让我非要自己做实验探求一番不可:
2-4-1,alpha=0.02,mu=0.9,括号中是收敛步数,NA表示发散:
| Random | Nguyen-Widrow | Random+momentum | Nguyen-Widrow+momentum |
| 3250 | 549 | 344 | 109 |
| 4079 | 545 | 323 | 59 |
| 2572 | 568 | 407 | 40 |
| 3091 | 8785 | 328 | 50 |
| 3556 | 512 | 466 | 47 |
| 2949 | 560 | 437 | 56 |
| 4220 | 450 | 384 | 43 |
| 3846 | 460 | 381 | 46 |
| 3506 | 541 | 297 | 48 |
| 4229 | 364 | 336 | 25 |
| 3631 | 453 | 441 | 58 |
| 3314 | 589 | 316 | 44 |
| 4221 | 682 | 283 | 40 |
| 3976 | 450 | 593 | 65 |
| 3143 | 481 | 375 | 37 |
每一次实验都收敛了,而且结果显而易见,Nguyen-Widrow和动能学习有着明显的优势。下面研究一下拓扑结构对收敛速度的影响,(m,n)表示2-m-1的拓扑结构,每个隐藏层有n个元,以下结果均基于bipolar+Nguyen-Widrow+Momentum,alpha=0.02,mu=0.9:
| 第一次试验 | 第二次试验 | 第三次试验 | |
| (1,10) | 15 | 15 | 17 |
| (1,15) | 10 | 8 | 16 |
| (1,30) | 4 | 7 | 5 |
| (1,60) | NA | NA | 4 |
| (2,5) | 15 | 14 | 19 |
| (2,10) | 16 | 8 | 12 |
| (2,20) | 7 | 10 | 6 |
| (2,40) | 5 | 10 | 12 |
| (2,60) | 5 | 6 | 4 |
| (2,100) | 5 | 4 | 3 |
| (5,5) | 17 | 27 | 15 |
| (5,20) | 7 | 6 | 8 |
| (5,60) | 5 | 6 | 5 |
| (5,100) | 5 | 6 | 4 |
| (20,5) | NA | NA | NA |
| (10,5) | 106 | 18 | 6346 |
| (10,20) | 22 | 17 | 17 |
如同上篇文章所说,训练对数应该是权重总数的10倍才好,这里只有4对数据对。单层隐藏层足够解决这个问题,2层的结果更鲁棒,这是可以想象的,因为第二层所引入的值都是-1到+1的值。层数更多也无意义。每层的元数稍稍要多一点,这样每次训练改变的权值也就多一点,收敛也许更快。我这最后一句话太不科学了。谁叫我这篇博客的题目就是一个目前没有说法的一个问题。只能靠实验了。
代码如下:
这个是functions_.h:
#ifndef _FUNCTION__h_ #define _FUNCTION__h_ #include <cmath> using namespace std; inline double unif_rand(){ //generateur de loi uniforme(0,1) //rand(); return(rand()+0.5)/(RAND_MAX+1.0); }; double inner_product(double *a, double *b, int n){ double sum=0; for(int i=0;i<n;i++){ sum+=a[i]*b[i]; } return sum; } double euclidien(double*a, int n){ return sqrt(inner_product(a,a,n)); } inline double activation(double x, bool is_random){ if(is_random){ return (2.0/(1+exp(-1.0*x))-1.0); } else{ return tanh(x); } } inline double derivee(double y, bool is_random){ if(is_random){ return 0.5*(1-y*y); } else{ return 1-y*y; } } #endif
#ifndef _GENERALIZED_XOR_H_ #define _GENERALIZED_XOR_H_ #include "functions_.h" class xor{ public: xor(int m, int n, double alpha, double mu); ~xor(); void weight_initialization(bool is_random); double squared_error(bool is_random); void train(bool is_random); private: int m; int n; double **input; double **weight_input; double **change_input_new; double **change_input_old; double **hidden; double **hidden_test; double **inter_hidden; double ***weight; double ***change_hidden_new; double ***change_hidden_old; double inter_y; double y; double y_test; double *output; double alpha; double mu; }; xor::xor(int m, int n, double alpha, double mu):m(m),n(n),alpha(alpha),mu(mu){ input=new double *[4]; input[0]=new double [12]; input[0][0]=1.; input[0][1]=1.; input[0][2]=1.; for(int i=1;i<4;i++){ input[i]=input[i-1]+3; input[i][2]=1.; } input[1][0]=1.; input[1][1]=-1.; input[2][0]=-1.; input[2][1]=1.; input[3][0]=-1.; input[3][1]=-1.; output=new double [4]; output[0]=-1.; output[1]=1.; output[2]=1.; output[3]=-1.; weight_input=new double *[n]; change_input_new=new double *[n]; change_input_old=new double *[n]; weight_input[0]=new double [n*3]; change_input_new[0]=new double [n*3]; change_input_old[0]=new double [n*3]; for(int i=1;i<n;i++){ weight_input[i]=weight_input[i-1]+3; change_input_new[i]=change_input_new[i-1]+3; change_input_old[i]=change_input_old[i-1]+3; } hidden=new double *[m]; hidden_test=new double *[m]; inter_hidden=new double *[m]; hidden[0]=new double [m*n+m]; hidden_test[0]=new double [m*n+m]; inter_hidden[0]=new double [m*n]; hidden[0][n]=1.; hidden_test[0][n]=1.; for(int i=1; i<m;i++){ hidden[i]=hidden[i-1]+n+1; hidden_test[i]=hidden_test[i-1]+n+1; hidden[i][n]=1.; hidden_test[i][n]=1.; inter_hidden[i]=inter_hidden[i-1]+n; } weight=new double**[m]; change_hidden_new=new double **[m]; change_hidden_old=new double **[m]; for(int i=0;i<m-1;i++){ weight[i]=new double *[n]; change_hidden_new[i]=new double *[n]; change_hidden_old[i]=new double *[n]; } weight[m-1]=new double *[1]; change_hidden_new[m-1]=new double *[1]; change_hidden_old[m-1]=new double *[1]; weight[0][0]=new double [(n+1)*(n*m-n+1)]; change_hidden_new[0][0]=new double [(n+1)*(n*m-n+1)]; change_hidden_old[0][0]=new double [(n+1)*(n*m-n+1)]; for(int i=0;i<m-1;i++){ for(int j=0;j<n;j++){ weight[i][j]=weight[0][0]+(i*n+j)*(n+1); change_hidden_new[i][j]=change_hidden_new[0][0]+(i*n+j)*(n+1); change_hidden_old[i][j]=change_hidden_old[0][0]+(i*n+j)*(n+1); } } weight[m-1][0]=weight[0][0]+(m*n-n)*(n+1); change_hidden_new[m-1][0]=change_hidden_new[0][0]+(m*n-n)*(n+1); change_hidden_old[m-1][0]=change_hidden_old[0][0]+(m*n-n)*(n+1); }; xor::~xor(){ delete [] input[0]; delete [] input; delete [] output; delete [] weight_input[0]; delete [] weight_input; delete [] change_input_new[0]; delete [] change_input_new; delete [] change_input_old[0]; delete [] change_input_old; delete [] hidden[0]; delete [] hidden; delete [] inter_hidden[0]; delete [] inter_hidden; delete [] change_hidden_old[0][0]; delete [] change_hidden_new[0][0]; delete [] weight[0][0]; delete [] change_hidden_old[m-1]; delete [] change_hidden_new[m-1]; delete [] weight[m-1]; for(int i=0;i<m-1;i++){ delete [] change_hidden_old[i]; delete [] change_hidden_new[i]; delete [] weight[i]; } delete [] change_hidden_old; delete [] change_hidden_new; delete [] weight; }; void xor::weight_initialization(bool is_random){ double beta=0.7*pow(double(n),0.5); double norm; for(int i=0;i<n;i++){ for(int j=0;j<2;j++){ weight_input[i][j]=unif_rand()-0.5; change_input_old[i][j]=0.; } change_input_old[i][2]=0.; if(!is_random){ weight_input[i][2]=(2*unif_rand()-1)*beta; norm=euclidien(weight_input[i],2); for(int j=0;j<2;j++){ weight_input[i][j]=weight_input[i][j]*beta/norm; } } else{ weight_input[i][2]=unif_rand()-0.5; } } double beta_hidden=0.7*pow(n,1./double(n)); double norm_hidden; for(int i=0;i<m-1;i++){ for(int j=0;j<n;j++){ for(int k=0;k<n;k++){ weight[i][j][k]=unif_rand()-0.5; change_hidden_old[i][j][k]=0.; } change_hidden_old[i][j][n]=0.; if(!is_random){ weight[i][j][n]=(2*unif_rand()-1)*beta_hidden; norm_hidden=euclidien(weight[i][j],n); for(int k=0;k<n;k++){ weight[i][j][k]=weight[i][j][k]*beta_hidden/norm_hidden; } } else{ weight[i][j][n]=unif_rand()-0.5; } } } double beta_out=0.7*pow(1.,1./double(n)); double norm_out; for(int i=0;i<n;i++){ weight[m-1][0][i]=unif_rand()-0.5; change_hidden_old[m-1][0][i]=0.; } change_hidden_old[m-1][0][n]=0.; if(!is_random){ weight[m-1][0][n]=(2*unif_rand()-1)*beta_out; norm_out=euclidien(weight[m-1][0],n); for(int i=1;i<n;i++){ weight[m-1][0][i]=weight[m-1][0][i]*beta_out/norm_out; } } else{ weight[m-1][0][n]=unif_rand()-0.5; } }; double xor::squared_error(bool is_random){ double se=0.0; for(int i=0;i<4;i++){ for(int j=0;j<n;j++){ hidden_test[0][j]=activation(inner_product(input[i],weight_input[j],3),is_random); } for(int j=1;j<m;j++){ for(int k=0;k<n;k++){ hidden_test[j][k]=activation(inner_product(hidden_test[j-1],weight[j-1][k],n+1),is_random); } } y_test=activation(inner_product(hidden_test[m-1],weight[m-1][0],n+1),is_random); se+=pow(y_test-output[i],2.0); } return se; }; void xor::train(bool is_random){ bool finish=true; int epoch=0; double temp_error; while(finish){ epoch++; temp_error=squared_error(is_random); cout<<epoch<<"th error="<<temp_error<<endl; if(temp_error<0.05) system("pause"); for(int i=0;i<4;i++){ for(int j=0;j<n;j++){ hidden[0][j]=activation(inner_product(input[i],weight_input[j],3),is_random); } for(int j=1;j<m;j++){ for(int k=0;k<n;k++){ hidden[j][k]=activation(inner_product(hidden[j-1],weight[j-1][k],n+1),is_random); } } y=activation(inner_product(hidden[m-1],weight[m-1][0],n+1),is_random); ////////////////////////////////////////////////////////////////start bp inter_y=(output[i]-y)*derivee(y,is_random); for(int j=0;j<=n;j++){ change_hidden_new[m-1][0][j]=alpha*inter_y*hidden[m-1][j]+mu*change_hidden_old[m-1][0][j]; change_hidden_old[m-1][0][j]=change_hidden_new[m-1][0][j]; } for(int k=0;k<n;k++){ inter_hidden[m-1][k]=inter_y*weight[m-1][0][k]*derivee(hidden[m-1][k],is_random); } if(m>=2){ for(int j=0;j<n;j++){ for(int k=0;k<=n;k++){ change_hidden_new[m-2][j][k]=alpha*inter_hidden[m-1][j]*hidden[m-2][k]+mu*change_hidden_old[m-2][j][k]; change_hidden_old[m-2][j][k]=change_hidden_new[m-2][j][k]; } } for(int j=m-2;j>0;j--){ for(int k=0;k<n;k++){ inter_hidden[j][k]=0.0; for(int l=0;l<n;l++){ inter_hidden[j][k]+=inter_hidden[j+1][l]*weight[j][l][k]; } inter_hidden[j][k]=inter_hidden[j][k]*derivee(hidden[j][k],is_random); } for(int k=0;k<n;k++){ for(int l=0;l<=n;l++){ change_hidden_new[j-1][k][l]=alpha*inter_hidden[j][k]*hidden[j-1][l]+mu*change_hidden_old[j-1][k][l]; change_hidden_old[j-1][k][l]=change_hidden_new[j-1][k][l]; } } } for(int j=0;j<n;j++){ inter_hidden[0][j]=0.0; for(int k=0;k<n;k++){ inter_hidden[0][j]+=inter_hidden[1][k]*weight[0][k][j]; } inter_hidden[0][j]=inter_hidden[0][j]*derivee(hidden[0][j],is_random); } } for(int j=0;j<n;j++){ for(int k=0;k<3;k++){ change_input_new[j][k]=alpha*inter_hidden[0][j]*input[i][k]+mu*change_input_old[j][k]; change_input_old[j][k]=change_input_new[j][k]; } } ////////////////////////////////////////////////////////////////////////////////////////////start change weights. for(int j=0;j<n;j++){ for(int k=0;k<3;k++){ weight_input[j][k]+=change_input_new[j][k]; } } if(m>=2){ for(int j=0;j<m-1;j++){ for(int k=0;k<n;k++){ for(int l=0;l<=n;l++){ weight[j][k][l]+=change_hidden_new[j][k][l]; } } } } for(int j=0;j<=n;j++){ weight[m-1][0][j]+=change_hidden_new[m-1][0][j]; } //////////////////////la fin } } }; #endif
Additional comments powered by BackType

好吧顶一下
楼主你到底学什么的,又有计算机,又有数学,又有金融。好晕
这个有点难
很久没看到这么经典的帖子了,感谢博主分享