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);
}