神經網絡編程及其訓練

1.神經元結構


每個神經元包含權值、輸入值、激活元素,誤差項,閥值組成。在這裏用一個結構體Nerver來表示,其中Weights_Temp[20],這個數組在批處理訓練的時候會用來,保存個樣本訓練後權值的改變量。

typedef struct
{
	float Weights[20];                     //權值
	float Weights_Temp[20];                     //批處理時臨時權值
	float Activating_Element;           //激活元素Iq
	float OutPut;                           //神經元輸出
	float Error;                              //誤差項
	float Threshold;                         //閥值
}Nerver;                                     //神經元結構體


 

神經元輸出與前一層的輸入,本層的權值,閥值之間的關係爲:

其中函數f()一種非線性函數,編程中採用

2. 3層的神經網絡

 for (i=1;i<=Lay_Number;i++)   //Lay_Number 是神經網絡的層數,本例中取4層

			   {
			     for (j=0;j<Lay_Arrangement[i];j++)//Lay_Arrangement是每層中節點個數

			       {
				 for (k=0;k<Lay_Arrangement[i-1];k++)  
				  {
				    Sum+=NetWeb[i-1][k].OutPut*NetWeb[i][j].Weights[k];//NetWeb是Nerver結構體的一個對象

				   }
				    NetWeb[i][j].Activating_Element=Sum+NetWeb[i][j].Threshold;
				    Sum=0;
				    NetWeb[i][j].OutPut=(a/(1+exp(-NetWeb[i][j].Activating_Element/thta)));//第i層第j個神經元的輸出值y
				 }
			   }                //計算每個神經元的值

 

在這裏神經網絡的正向計算已經完成,其中權值、閥值,訓練樣本的初始化這些工作在後面的完整代碼中給出。

3. 權值的訓練過程

反向傳播模型採用德爾塔原則:

均方誤差:

 

權值訓練,相鄰兩層間誤差項的遞推關係:

 

 

 每層每個節點誤差項的計算如下:

 for(Cal=0;Cal<1;Cal++)
	       {
		NetWeb[Lay_Number][Cal].Error=(Optimal_Value[m]-NetWeb[Lay_Number][Cal].OutPut)*a/thta*NetWeb[Lay_Number][Cal].OutPut*(1-NetWeb[Lay_Number][Cal].OutPut);//輸出層誤差項的計算
		Sum_Error+=abs(Optimal_Value[m]-NetWeb[Lay_Number][Cal].OutPut); //累計誤差
	         }     //輸出層的各節點誤差項

	       for (i=Lay_Number-1;i>0;i--)
	       {
		for (j=0;j<Lay_Arrangement[i];j++)
		   {
		     for (k=0;k<Lay_Arrangement[i+1];k++)
			{
			  Sum+=NetWeb[i+1][k].Error*NetWeb[i+1][k].Weights[j];
			}
			   NetWeb[i][j].Error=Sum*a/thta*NetWeb[i][j].OutPut*(1-NetWeb[i][j].OutPut);
			   Sum=0;
		   }
	       }     //利用遞推關係計算每層每節點誤差項


對一個樣本訓練後利用每個節點的誤差項,得到每層每節點的權值變化量,由於程序中使用批處理的方式,也就是當所有樣本全部訓練完一次後在改變網絡中的權值,所以每個樣本訓練後,得到的權值改變量會累加到相應的數組NetWeb[i][j].Weights_Temp[k]中:

for (i=1;i<=Lay_Number;i++)
			{
				for (j=0;j<Lay_Arrangement[i];j++)
				{
					for (k=0;k<Lay_Arrangement[i-1];k++)  
					{
						NetWeb[i][j].Weights_Temp[k]+=Learn_Coefficient*NetWeb[i][j].Error*NetWeb[i-1][k].OutPut;
					}			
				}
			}      //計算訓練一個樣本後得權值改變累計     

所用樣本都訓練完一次後,對網絡權值進行更新:

 for (i=1;i<=Lay_Number;i++)
		   {
			   for (j=0;j<Lay_Arrangement[i];j++)
			   {
				   for (k=0;k<Lay_Arrangement[i-1];k++)  
				   {
					   NetWeb[i][j].Weights[k]+=NetWeb[i][j].Weights_Temp[k];
				   }			
			   }
		   }      //一個訓練批次後,更新權值



4.訓練結果

           

 

程序中採用了一個輸入層,兩個隱層和一個輸入層的結構。每次訓練結果會有所差異。誤差總會存在,還存在許多改進的地方,不過作爲一個理解神經網絡基本運作原理來說已經夠了。

目標函數選擇 (sin(2*3.1415926*x)+1)/2。訓練範圍爲(0,1)。

對於樣本的選取,最好將輸入與輸出規範化到(0,1)中這樣訓練效果會比較好。程序中S函數參數選取爲a=1.716,thta=1。a的值要比目標函數的最大值要大,這樣訓練才能成功。

 

5.源代碼

該源代碼使用了opencv顯示圖像,需要包含opencv庫,可能由於各個電腦程序庫不同,並不一定可以直接運行。但各個功能部分是可以使用的,本程序可以實現任意層數與節點的神經網絡。

 

#include <stdio.h>
#include <tchar.h>
#include "function.h"
#include <math.h>
#include "opencv2/opencv.hpp"


#include <ctime>
#include <cstdlib>
using namespace cv;#define NUM 40
  double random(double,double);typedef struct
{
	float Weights[20];                     //權值
	float Weights_Temp[20];                     //批處理時臨時權值
	float Activating_Element;           //激活元素Iq
	float OutPut;                           //神經元輸出
	float Error;                              //誤差項
	float Threshold;                         //閥值
}Nerver;                                     //神經元結構體
int _tmain(int argc, _TCHAR* argv[])
{
	unsigned int Lay_Arrangement[10],i,j,k,m,Lay_Number=3,Learn_Num=0,Sample_Num=NUM,Cal=0;
	float Sum=0,Optimal_Value[NUM+1],Learn_Coefficient=0.01,x,Sum_Error=100000,a=1.716,thta=1;
	Lay_Arrangement[0]=1;
    Lay_Arrangement[1]=3;
	Lay_Arrangement[2]=2;
	Lay_Arrangement[3]=1;
   
	Nerver NetWeb[10][NUM];
	 srand(unsigned(time(0)));
	IplImage *im;
	CvPoint PrePoint,Point;
	CvScalar m_RGB;
	for (i=1;i<=Lay_Number;i++)          //i層
	{
		for (j=0;j<Lay_Arrangement[i];j++)   //第j個節點
		{			
			NetWeb[i][j].Threshold=0;		//初始化神經元閥值				
		}		
	}
	for (i=1;i<=Lay_Number;i++)          //i層
	{
		for (j=0;j<Lay_Arrangement[i];j++)   //第j個節點
		{
			for (k=0;k<Lay_Arrangement[i-1];k++)   // 第i層第j個節點與前一層第k個節點之間的權值初始值
			{
				NetWeb[i][j].Weights[k]=random(-0.5,0.5);
				//NetWeb[i][j].Weights[k]=0.1;
				NetWeb[i][j].Weights_Temp[k]=0;

			}			
		}		
	}

	for(x=0.0;x<PI;x+=PI/Sample_Num)
	{		
		NetWeb[0][Cal].OutPut=x;   //神經元輸入值
		Optimal_Value[Cal]=(sin(2*3.1415926*x)+1)/2;   //神經元理想輸出值
		//Optimal_Value[Cal]=sin(x); 
		Cal++;
	}      //初始化神經網絡輸入層
	while ((Sum_Error>0.0001)&&(Learn_Num<40000))
	{
		Sum_Error=0;
		Learn_Num++;
		 
		for (i=1;i<=Lay_Number;i++)          //i層
		{
			for (j=0;j<Lay_Arrangement[i];j++)   //第j個節點
			{
				for (k=0;k<Lay_Arrangement[i-1];k++)   // 第i層第j個節點與前一層第k個節點之間的權值初始值
				{					
					NetWeb[i][j].Weights_Temp[k]=0;
				}			
			}		
		}
		
	       for(m=0;m<Sample_Num;m++)
		   {
			   NetWeb[0][0].OutPut=NetWeb[0][m].OutPut;			  
			   for (i=1;i<=Lay_Number;i++)
			   {
				   for (j=0;j<Lay_Arrangement[i];j++)
				   {
					   for (k=0;k<Lay_Arrangement[i-1];k++)  
					   {
						   Sum+=NetWeb[i-1][k].OutPut*NetWeb[i][j].Weights[k];
					   }
					   NetWeb[i][j].Activating_Element=Sum+NetWeb[i][j].Threshold;
					   Sum=0;
					   NetWeb[i][j].OutPut=(a/(1+exp(-NetWeb[i][j].Activating_Element/thta)));
				   }
			   }                //計算每個神經元的值
			   for(Cal=0;Cal<1;Cal++)
			   {
				   NetWeb[Lay_Number][Cal].Error=(Optimal_Value[m]-NetWeb[Lay_Number][Cal].OutPut)*a/thta*NetWeb[Lay_Number][Cal].OutPut*(1-NetWeb[Lay_Number][Cal].OutPut);
				   Sum_Error+=abs(Optimal_Value[m]-NetWeb[Lay_Number][Cal].OutPut); //累計誤差
			   }     //輸出層的各節點誤差項



			   for (i=Lay_Number-1;i>0;i--)
			   {
				   for (j=0;j<Lay_Arrangement[i];j++)
				   {
					   for (k=0;k<Lay_Arrangement[i+1];k++)
					   {
						   Sum+=NetWeb[i+1][k].Error*NetWeb[i+1][k].Weights[j];
					   }
					   NetWeb[i][j].Error=Sum*a/thta*NetWeb[i][j].OutPut*(1-NetWeb[i][j].OutPut);
					   Sum=0;
				   }
			   }     //計算每層每節點誤差項

		   
			
			for (i=1;i<=Lay_Number;i++)
			{
				for (j=0;j<Lay_Arrangement[i];j++)
				{
					for (k=0;k<Lay_Arrangement[i-1];k++)  
					{
						NetWeb[i][j].Weights_Temp[k]+=Learn_Coefficient*NetWeb[i][j].Error*NetWeb[i-1][k].OutPut;
					}			
				}
			}      //計算訓練一個樣本後得權值改變累計       		
	    }
		   for (i=1;i<=Lay_Number;i++)
		   {
			   for (j=0;j<Lay_Arrangement[i];j++)
			   {
				   for (k=0;k<Lay_Arrangement[i-1];k++)  
				   {
					   NetWeb[i][j].Weights[k]+=NetWeb[i][j].Weights_Temp[k];
				   }			
			   }
		   }      //一個訓練批次後,更新權值
}

	im=cvCreateImage(cvSize(1400,400),IPL_DEPTH_8U,3);
	Mat im_Mat(im);
	cvZero(im);
	PrePoint=cvPoint(0,100);
	m_RGB=CV_RGB(255,0,0);
	x=0;
	for(Cal=0.0;Cal<Sample_Num;Cal++)
	{			
		Point=cvPoint(int(100*x),int(100*Optimal_Value[Cal]));
		cvLine(im, PrePoint, Point,m_RGB,1,CV_AA,0);
		PrePoint=Point;
		x+=PI/Sample_Num;
	} 
	m_RGB=CV_RGB(0,255,0);	
	PrePoint=cvPoint(0,int((NetWeb[Lay_Number][Cal].OutPut)*100));
	x=0;
	for (i=1;i<=Lay_Number;i++)
	{
		for (j=0;j<Lay_Arrangement[i];j++)
		{
			for (k=0;k<Lay_Arrangement[i-1];k++)  
			{
				printf("%f1.4 ",NetWeb[i][j].Weights[k]);
			}			
		}
		printf("\n");
	}      //一個訓練批次後,更新權值
	for(Cal=0.0;Cal<Sample_Num;Cal++)
	{	
		NetWeb[0][0].OutPut=x;					  
		for (i=1;i<=Lay_Number;i++)
		{
			for (j=0;j<Lay_Arrangement[i];j++)
			{
				for (k=0;k<Lay_Arrangement[i-1];k++)  
				{
					Sum+=NetWeb[i-1][k].OutPut*NetWeb[i][j].Weights[k];
				}
				NetWeb[i][j].Activating_Element=Sum+NetWeb[i][j].Threshold;
				Sum=0;
				NetWeb[i][j].OutPut=(a/(1+exp(-NetWeb[i][j].Activating_Element/thta)));//利用訓練後網絡實現對目標函數的擬合
			}
		}   

		
		Point=cvPoint(int(100*x),int(100*(NetWeb[Lay_Number][0].OutPut)));
		cvLine(im, PrePoint, Point,m_RGB,1,CV_AA,0);
		PrePoint=Point;
		x+=PI/Sample_Num;
		 
	}     
	imshow("result",im_Mat);
	waitKey(0);




	return 0;
}

double random(double start, double end)
{
	return start+(end-start)*rand()/(RAND_MAX + 1.0);
}


 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章