用如下图1的神经网络结构(正向神经网络结构为2-4-4-2-1 )去逼近函数:

f(x1,x2) = (x1-1)^4 + 2×x2^2。

1)网络各神经元的激发函数为:s函数——F(x) = 1/(1+exp(-x)) ;

2)输入层的神经元不是真正的神经元,它们的输出等于输入。

3)取20个样本值作为训练用。

4)x1,x2的取值范围:0≤x1,x2≤1。

5)误差<0.0001

求解过程

1、对要逼近的函数f(x1,x2) = (x1-1)^4 + 2×x2^2 进行分析。x1,x2的取值范围:0≤x1,x2≤1。那么,输入不用归一化(若下x1,x2的值域不再0~1,那就要输入归一化了,因为我们可以从神经网络的激发函数可以看出,输入在0~1时,变化率是很大的,所以网络对输出很敏感)。求该函数的值域,很显然该函数的值域为:0~3,这就需要归一化了,因为神经网络输出的值只能在0~1之间。设Out_Exp[i]为第i个输入样本的期望值,那么归一化后的期望输出为:Out_Exp[i]/3,用这个值和网络的输出进行比较,来进行训练。最后在网络输出时要反归一化,即把网络的输出乘以3。

2、由于BP算 法的步骤是一定的,我们只要把其思想转化为程序就行了,即把数学表达式转换为程序。我们知道在计算机中每一种算法都需要一定的数据结构去支持。由于算法已 确定,那么我们只要分析和确定其数据结构即可。首先,我们考虑在如何计算机程序设计中表示权系数和阀值,在这里我们定义了3维数组W[Layer_Max][Node_Max][Node_Max+1]用来表示神经网络的全部权系数和阀值,我们约定W[i][j][k]存储网络的权系数,其中i表示为神经网络的第i层,j表示为第i层网络的第j个神经元,k表示为第i -1层的第j个神经网络。那么,W[i][j][k]表示为第i层的第j个神经元和第i -1层的第k个神经元的权系数。W[i][j][Layer[i-1]+1]表示第i层第j个神经元的阀值。

注:1、Layer_Max表示网络结构的层数

2、Node_Max表示整个神经网络中各层中含有神经元的最大数目的个数

3、Layer[i]数组表示网络中第i层的神经元的个数

然后,我们定义网络输入的和期望输出数组。定义2维数组Input_Net[2][21]作为网络输入数组,在这里为了方便取了21样本作为,其中x1取值从0开始,已每次加0.05的步长作为下一个样本取值。而x2的取值则与之相反。那么,由于x1和x2各有21个值,由排列组合得出网络训练样本一共有21*21=421个样本。我们再定义一个2维数组Out_Exp[21][21]表示期望输出。定义二维数组Layer_Node[i][j]存储各层神经元的输出,表示为第i层的第j个神经元的输出。定义二维数组D[i][j]存储各层神经元的的误差微分,表示为第i层的第j个神经元的的误差微分。

3、代价函数为(NetOut(i ,j)-Out_Exp[i][j])^2/2。其中:NetOut(i,j)表示输入x1的第i个值和x2的第j个值所组成的样本时,网络的实际输出。

4、确定BP算法的关键的子程序。

a) 、F( double x ) 该函数是该神经网络的唯一激发函数,它的数学表达:

F( x ) = 1/(1+exp(-x)) 。它的输入为样本值NetIn[i]。输出为一个在区间。

b)、Initialize() 该函数是网络初始化子程序,它初始化权系数和阀值,学习速率,误差精度等。

c)、 NetWorkOut( int i ,int j) 该函数的输入为表示输入x1的第i个值和x2的第j个值所组成的样本时,在计算网络输出的时候,同时计算各层神经元的输出,并保存在Layer_Node[][]数组里。输出为神经网络的实际输出。

d)、 AllLayer_D(int i , int j) 该函数的输入为输入x1的第i个值和x2的第j个值所组成的样本的数组下标,目的是计算各层神经元的误差微分,并把他们保存在D[][]数组里。

e)、 Change_W( ) 该函数是用于根据AllLayer_D( )计算出来的误差微分改变权系数,根据经典的BP算法可以写出改变权系数和阀值式子:

W[i][j][k] = W[i][j][k] – Study_Speed*D[i][j]* Layer_Node[i-1][k]
 W[i][j][Layer[i-1]+1]=W[i][j][Layer[i-1]+1]+Study_Speed*D[i][j]*
 Layer_Node[i-1][ [Layer[i-1]+1]

其中:Study_Speed为学习速率,取值在(0,1)之间,如果太大了,网络将会出现振荡,而不能收敛。

g)、 Train( ) 该函数是用于神经网络训练用的。它调用了上面几个函数来完成网络训练的。当训练完(即网络对于该问题是可以收敛的)时,网络就可以在特定的误差范内逼近函数。下面给出该函数的流程图:

 

//---------------------------------------------------------------------------------------//
 // BP算法例子:用一个五层的神经网络去逼近函数 //
 // f(x1,x2)=pow(x1-1,4)+2*pow(x2,2) //
 // 作者:MaxMatrix //
 // 2004.5.9调通 运行于VC++6.0 //
 //--------------------------------------------------------------------------------------// #include<iostream.h>
 #include<math.h>
 #include<stdlib.h>
 #include<time.h>
 #include<fstream.h>
 //---------------------------------------------------------------------
 #define RANDOM rand()/32767.0 //0~1随机数生成函数

 const int Layer_Max=5;//神经网络的层数 const double PI=3.1415927;//圆周率
 const int Layer_number[Layer_Max]={2,4,4,2,1};//神经网络各层的神经元个数
 const int Neural_Max=4;//神经网络各层最大神经元个数
 const int InMax=21;//样本输入的个数
 ofstream Out_W_File("All_W.txt",ios::out) ;
 ofstream Out_Error("Error.txt",ios::out) ; //定义类 BP
 class BP
 {
 public:
 BP(); //BP类的构造函数 void BP_Print();//打印权系数
 double F(double x);//神经元的激发函数
 double Y(double x1,double x2);//要逼近的函数
 //
 double NetWorkOut(int x1 , int x2);//网络输出,他的输入为
 //第input个样本
 void AllLayer_D(int x1 , int x2);//求所有神经元的输出误差微分

 void Change_W(); //改变权系数

 void Train(); //训练函数 void After_Train_Out(); //经过训练后,21样本的神经网络输出
 double Cost(double out,double Exp);//代价函数
 private:
 double W[Layer_Max][Neural_Max][Neural_Max];//保存权系数
 //规定W[i][j][k]表示网络第i层的第j个神经元连接到
 //第i-1层第k个神经元的权系数
 double Input_Net[2][InMax];//21个样本输入,约定Input_Net[0][i]
 //表示第i个样本的输入x1
 //而 Input_Net[1][i]表示第i个样本的输入x2
 double Out_Exp[InMax][InMax];//期望输出

 double Layer_Node[Layer_Max][Neural_Max];//保存各神经元的输出
 //规定Layer_Node[i][j]表示第i层的第j个神经元的输出 double D[Layer_Max][Neural_Max];//保存各神经元的误差微分
 //规定D[i][j]表示第i层第j个神经元的误差微分 double Study_Speed;//学习速度

 double e;//误差
 }; //构造函数,用来初始化权系数,输入,期望输出和学习速度
 BP::BP()
 {
 srand(time(NULL));//播种,以便产生随即数
 for(int i=1 ; i<Layer_Max ; i++)
 {
 for(int j=0 ; j<Layer_number[i] ; j++)
 {
 for(int k=0 ; k<Layer_number[i-1]+1 ; k++)
 {
 W[i][j][k] = RANDOM;//随机初始化权系数

 }
 // Q[i][j] = RANDOM ;//初始化各神经元的阀值
 }
 }
 //输入归和输出归一化
 for(int l=0 ; l<InMax ; l++)
 {
 Input_Net[0][l] = l * 0.05 ;//把0~1分成20等分,表示x1
 Input_Net[1][l] = 1 - l * 0.05 ;//表示x2
 }
 for(i=0 ; i<InMax ; i++)
 {
 for(int j=0 ; j<InMax ; j++)
 {
 Out_Exp[i][j] = Y(Input_Net[0][i],Input_Net[1][j]) ;//期望输出
 Out_Exp[i][j] = Out_Exp[i][j]/3.000000;//期望输出归一化
 }
 }

 Study_Speed=0.5;//初始化学习速度

 e=0.0001;//误差精度


 }//end
 //激发函数F()
 double BP::F(double x)
 {
 return(1.0/(1+exp(-x)));
 }//end //要逼近的函数Y()
 //输入:两个浮点数
 //输出:一个浮点数
 double BP::Y(double x1,double x2)
 {
 double temp;
 temp = pow(x1-1,4) + 2 * pow(x2,2);
 return temp;
 }//end
 //--------------------------------------------------------
 //代价函数
 double BP::Cost(double Out,double Exp)
 {
 return(pow(Out-Exp,2));
 }//end //网络输出函数
 //输入为:第input个样本
 double BP::NetWorkOut(int x1 , int x2)
 {
 int i,j,k;
 double N_node[Layer_Max][Neural_Max];
 //约定N_node[i][j]表示网络第i层的第j个神经元的总输入
 //第0层的神经元为输入,不用权系数和阀值,即输进什么即输出什么
 N_node[0][0] = Input_Net[0][x1] ;
 Layer_Node[0][0] = Input_Net[0][x1] ;
 N_node[0][1] = Input_Net[1][x2] ;
 Layer_Node[0][1] = Input_Net[1][x2] ; for(i=1 ; i<Layer_Max ; i++)//神经网络的第i层
 {
 for(j=0 ; j<Layer_number[i] ; j++)//Layer_number[i]为第i层的
 { //神经元个数
 N_node[i][j] = 0.0;
 for(k=0 ; k<Layer_number[i-1] ; k++)//Layer_number[i-1]
 { //表示与第i层第j个神经元连接的上一层的
 //神经元个数

 //求上一层神经元对第i层第j个神经元的输入之和
 N_node[i][j]+=Layer_Node[i-1][k] * W[i][j][k];

 }
 N_node[i][j] = N_node[i][j]-W[i][j][k];//减去阀值 //求Layer_Node[i][j],即第i层第j个神经元的输出
 Layer_Node[i][j] = F(N_node[i][j]);
 }
 }
 return Layer_Node[Layer_Max-1][0];//最后一层的输出
 }//end //求所有神经元的输出误差微分函数
 //输入为:第input个样本
 //计算误差微分并保存在D[][]数组中
 void BP::AllLayer_D(int x1 , int x2)
 {
 int i,j,k;
 double temp;
 D[Layer_Max-1][0] = Layer_Node[Layer_Max-1][0] *
 (1-Layer_Node[Layer_Max-1][0])*
 (Layer_Node[Layer_Max-1][0]-Out_Exp[x1][x2]);
 for(i=Layer_Max-1 ; i>0 ; i--)
 {
 for(j=0 ; j<Layer_number[i-1] ; j++)
 {
 temp = 0 ;
 for(k=0 ; k<Layer_number[i] ; k++)
 {
 temp = temp+W[i][k][j]*D[i][k] ;
 }
 D[i-1][j] = Layer_Node[i-1][j] * (1-Layer_Node[i-1][j])
 *temp ;
 }
 }
 }//end
 //修改权系数和阀值
 void BP::Change_W()
 {
 int i,j,k;
 for(i=1 ; i<Layer_Max ; i++)
 {
 for(j=0;j<Layer_number[i];j++)
 {
 for(k=0;k<Layer_number[i-1];k++)
 {
 //修改权系数
 W[i][j][k]=W[i][j][k]-Study_Speed*
 D[i][j]*Layer_Node[i-1][k];

 }
 W[i][j][k]=W[i][j][k]+Study_Speed*D[i][j];//修改阀值
 }
 }
 }//end
 //训练函数
 void BP::Train()
 {
 int i,j;
 int ok=0;
 double Out;
 long int count=0;
 double err;
 ofstream Out_count("Out_count.txt",ios::out) ;
 //把其中的5个权系数的变化保存到文件里
 ofstream outWFile1("W[2][0][0].txt",ios::out) ;
 ofstream outWFile2("W[2][1][1].txt",ios::out) ;
 ofstream outWFile3("W[1][0][0].txt",ios::out) ;
 ofstream outWFile4("W[1][1][0].txt",ios::out) ;
 ofstream outWFile5("W[3][0][1].txt",ios::out) ; while(ok<441)
 {
 count++;
 //20个样本输入
 for(i=0,ok=0 ; i<InMax ; i++)
 {
 for(j=0 ; j<InMax ; j++)
 {
 Out = NetWorkOut(i,j); AllLayer_D(i,j);

 err = Cost(Out,Out_Exp[i][j]);//计算误差

 if(err<e) ok++; //是否满足误差精度

 else Change_W();//否修改权系数和阀值
 }

 }
 if((count%1000)==0)//每1000次,保存权系数
 {
 cout<<count<<" "<<err<<endl;
 Out_count<<count<<"," ;
 Out_Error<<err<<"," ;
 outWFile1<<W[2][0][0]<<"," ;
 outWFile2<<W[2][1][1]<<"," ;
 outWFile3<<W[1][0][0]<<"," ;
 outWFile4<<W[1][1][0]<<"," ;
 outWFile5<<W[3][0][1]<<"," ;
 for(int p=1 ; p<Layer_Max ; p++)
 {
 for(int j=0 ; j<Layer_number[p] ; j++)
 {
 for(int k=0 ; k<Layer_number[p-1]+1 ; k++)
 {
 Out_W_File<<'W'<<'['<<p<<']'
 <<'['<<j<<']'
 <<'['<<k<<']'
 <<'='<<W[p][j][k]<<' '<<' ';
 }
 }
 }
 Out_W_File<<'\n'<<'\n' ;
 } }
 cout<<err<<endl;
 }//end //打印权系数
 void BP::BP_Print()
 {
 //打印权系数
 cout<<"训练后的权系数"<<endl;
 for(int i=1 ; i<Layer_Max ; i++)
 {
 for(int j=0 ; j<Layer_number[i] ; j++)
 {
 for(int k=0 ; k<Layer_number[i-1]+1 ; k++)
 {
 cout<<W[i][j][k]<<" ";
 }
 cout<<endl;
 }
 }
 cout<<endl<<endl;
 }//end //把结果保存到文件
 void BP::After_Train_Out()
 {
 int i,j ;
 ofstream Out_x1("Out_x1.txt",ios::out) ;

 ofstream Out_x2("Out_x2.txt",ios::out) ;

 ofstream Out_Net("Out_Net.txt",ios::out) ;

 ofstream Out_Exp("Out_Exp.txt",ios::out) ; ofstream W_End("W_End.txt",ios::out) ;
 ofstream Q_End("Q_End.txt",ios::out) ;
 ofstream Array("Array.txt",ios::out) ;
 ofstream Out_x11("x1.txt",ios::out) ;
 ofstream Out_x22("x2.txt",ios::out) ;
 ofstream Result1("result1.txt",ios::out) ;
 ofstream Out_x111("x11.txt",ios::out) ;
 ofstream Out_x222("x22.txt",ios::out) ;
 ofstream Result2("result2.txt",ios::out) ;
 
 for( i=0 ; i<InMax ; i++)
 {
 for(j=0 ; j<InMax ; j++)
 {
 Out_x11<<Input_Net[0][i]<<',';
 Out_x22<<Input_Net[1][j]<<"," ;
 Result1<<3*NetWorkOut(i,j)<<"," ;
 Out_x1<<Input_Net[0][i]<<"," ; Array<<Input_Net[0][i]<<" " ;

 Out_x2<<Input_Net[1][j]<<"," ; Array<<Input_Net[1][j]<<" " ;
 Out_Net<<3*NetWorkOut(i,j)<<"," ;
 Array<<Y(Input_Net[0][i],Input_Net[1][j])<<" " ;
 Out_Exp<<Y(Input_Net[0][i],Input_Net[1][j])<<"," ;
 Array<<3*NetWorkOut(i,j)<<" " ;
 Array<<'\n' ;
 }
 Out_x1<<'\n' ;
 Out_x2<<'\n' ;
 Out_x11<<'\n';
 Out_x22<<'\n';
 Result1<<'\n' ;

 }
 for(j=0 ; j<InMax ; j++)
 {
 for(i=0 ; i<InMax ; i++)
 {
 Out_x111<<Input_Net[0][i]<<',';
 Out_x222<<Input_Net[1][j]<<"," ;
 Result2<<3*NetWorkOut(i,j)<<"," ;
 }
 Out_x111<<'\n';
 Out_x222<<'\n' ;
 Result2<<'\n' ;
 } 
 //把经过训练后的权系数和阀值保存到文件里
 for(i=1 ; i<Layer_Max ; i++)
 {
 for(int j=0 ; j<Layer_number[i] ; j++)
 {
 for(int k=0 ; k<Layer_number[i-1]+1 ; k++)
 {

 W_End<<W[i][j][k]<<"," ;//保存权系数
 }
 }
 }//end for

 }//end void main(void)
 {
 BP B;//生成一个BP类对象B
 B.Train();//开始训练
 B.BP_Print();//把结果打印出来
 B.After_Train_Out();//把结果保存到文件 }//end