少女祈祷中...

我们实验性的用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

: http://www.deuxmille.org/archives/1613

本文相关评论 - 才 4 条评论
好吧
2010-08-10 20:28:18

好吧顶一下

nacllv
2010-08-15 15:21:27

楼主你到底学什么的,又有计算机,又有数学,又有金融。好晕

2010-08-18 13:06:05

这个有点难

2010-10-10 04:27:58

很久没看到这么经典的帖子了,感谢博主分享

  • :em14:
  • :em04:
  • :em11:
  • :em32:
  • :em08:
  • :em05:
  • :em17:
  • :em19:
  • :em24:
  • :em00:
  • :em31:
  • :em33:
  • :em06:
  • :em26:
  • :em27:
  • :em03:
  • :em28:
  • :em30:
  • :em13:
  • :em23:
  • :em21:
  • :em16:
  • :em20:
  • :em15:
  • :em07:
  • :em29:
  • :em02:
  • :em12:
  • :em18:
  • :em10:
  • :em01:
  • :em09:
  • :em22:
  • :em25:

Additional comments powered by BackType