1. 程式人生 > >神經網路-3層BP網的VC動態庫封裝

神經網路-3層BP網的VC動態庫封裝

神經網路-3BP網的VC動態庫封裝實現shades_smile.gif<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

神經網路是一個大範疇,這裡討論誤差反向傳播網路(BP網路) ,基本原理是通過分析網路輸出的誤差來調整網路的自由引數,使網路的大量自由引數平衡到一個收斂值,引數的收斂過程是訓練過程,訓練完成後網路基本穩定就可以投入應用.

網路運作過程:star.gif

簡單的單層BP網具有N0個輸入x,還含有N0個自由引數w(權值)

1.正向變換輸出coffee.gif

xw求內積記為v,v再通過一個啟用函式後輸出即為網路輸出,一般的啟用函式要求為

sigmoid非線性函式.而具有sigmoid非線性性的其中一個廣泛應用的函式為logistic函式,表示式為

logistic(v)=1/(1+exp(-av)) Pizza.gif

其中a為函式斜率.如此即得到網路的輸出值

另外比較常用的還可以是雙曲正切函式.根據需要選擇.

2.誤差反向傳播coffee.gif

誤差計算為e=d-o ,其中d為期望的網路輸出收斂值,o為網路實際輸出.

首先計算輸出結點的區域性梯度域

s=e*f’(v)Pizza.gif

f為啟用函式,f’表示導數

權值調整值
delta=n*s*yPizza.gif

n為學習率,取值0.01~0.9

s是上面計算的區域性梯度域

y是此結點的上次輸出

然後進行權值調整
w=w+deltaPizza.gif

迭代足夠多次後網路即可收斂

,達到訓練目的.


 

訓練過程就像教育一個小孩一樣,通過不斷教育教會小孩什麼使對的什麼是錯的,因此神經網路又屬於人工智慧範疇.

訓練的本質是使網路的自由引數w收斂到一個期望的水平,收斂使通過誤差反向傳播實現的,通過誤差計算自由引數的調整值,誤差將越來越小.

網路的本質是低維特徵空間逼近高維特徵空間的高度非線性對映.

BP網路不單隻可以有一層,還可以設定多層,當然誤差的計算就複雜一點.

下面是我用VC.NET製作的3BP網路的DLL,一個輸入層,一個輸出層和一個隱藏層,各層神經元數可以根據需要調整.網路為全連線結構,採用logistic函式作為啟用函式.函式斜率固定為1,學習率可以自由調整

.shades_smile.gif

//--------------VC.NET DLLpc.gif

//---------------------------以下開始為實際程式碼.

int N0,N1,N2;//------結點數

double *a1,*a2;//-------權值

double *x;//------輸入

double a;//----logistic 函式引數

double *o1,*o2;//-------各神經元輸出標量

double *s1,*s2;//---------區域性梯度域

double n;//-----------學習率

//----------網路引數初始化

extern "C" __declspec(dllexport)

bool InitNetwork(int n0,int n1,int n2,double nn=0.8)

{

N0=n0;

N1=n1;

N2=n2;

a1=new double[(N1+1)*(N0+1)];

a2=new double[N2*(N1+1)];

x=new double[(N0+1)];

o1=new double[(N1+1)];

o2=new double [N2];

s1=new double [(N1+1)];

s2=new double [N2];

//----------網路初始化

for(int i=0;i<(N1+1)*(N0+1);i++)

{

a1[i]=1;

}

for(int i=0;i<(N2)*(N1+1);i++)

{

a2[i]=1;

}

a=1;//----logistic 函式引數

n=nn;//---------學習率

return true;

}

//-------------釋放網路記憶體

extern "C" __declspec(dllexport)

bool ReleaseNetwork()

{

delete a1;

delete a2;

delete x;

delete o1;

delete o2;

delete s1;

delete s2;

return true;

}

//-----------------網路公共計算

bool CommonWork()

{

int i,j;

//----------------網路公共計算

//---------計算隱藏層的輸出/輸出層端的輸入

double v=0;

for(i=0;i<N1;i++)

{

v=0;

for(j=0;j<N0+1;j++)

{

v+=(a1[i*(N0+1)+j]*x[j]);//線性求和

}

//logistic函式(sigmoid)

o1[i]=(double)1.0/(1.0+exp(-a*v));

}

o1[N1]=1;//--------隱藏層固定偏置=1

//------------計算輸出層輸出

for(i=0;i<N2;i++)

{

v=0;

for(j=0;j<N1+1;j++)

{

v+=(a2[i*(N1+1)+j]*o1[j]);//線性求和

}

//logistic函式(sigmoid)

o2[i]=(double)1.0/(1.0+exp(-a*v));

}

return true;

}

//---------------網路分類,輸入範圍為[0,1],輸出為N2個神經元量化後的輸出.

extern "C" __declspec(dllexport)

bool Work(double *input,int *output)

{

int i;

for(i=0;i<N0;i++)

{

x[i]=input[i];

}

x[N0]=1;//-------輸入層固定偏置

CommonWork();

//------對輸出層神經元的輸出量化到01

for(i=0;i<N2;i++)

{

if(o2[i]<0.5)

output[i]=0;

else

output[i]=1;

}

return true;

}

//------------訓練

extern "C" __declspec(dllexport)

bool Tran(double *input,int *expected)

{

int i=0,j=0;

for(i=0;i<N0;i++)

{

x[i]=input[i];

}

x[N0]=1;//-------輸入層固定偏置

CommonWork();

//-------------輸出層調節

//----------計算輸出層的區域性梯度域

for(i=0;i<N2;i++)

{

s2[i]=a*((double)expected[i]-o2[i])*o2[i]*(1-o2[i]);

}

///-------------調整權值

for(i=0;i<N2;i++)

{

for(j=0;j<N1+1;j++)

{

a2[i*(N1+1)+j]+=(n*s2[i]*o1[j]);

}

}

//----------------隱藏層調節

//----------計算隱藏層結點區域性梯度域

for(i=0;i<N1+1;i++)

{

double ek=0;

for(j=0;j<N2;j++)

{

ek+=(s2[j]*a1[j*(N1+1)+i]);

}

s1[i]=a*o1[i]*(1-o1[i])*ek;

}

///-------------調整權值

for(i=0;i<N1+1;i++)

{

for(j=0;j<N0+1;j++)

{

a1[i*(N0+1)+j]+=(n*s1[i]*x[j]);

}

}

return true;

}

extern "C" __declspec(dllexport)

bool SaveNetwork(char *filePath)

{

fstream f;

f.open(filePath,ios_base::out|ios_base::trunc);

int i;

for(i=0;i<(N1+1)*(N0+1);i++)

{

f<<a1[i]<<" ";

}

for(i=0;i<N2*(N1+1);i++)

{

f<<a2[i]<<" ";

}

f.close();

return true;

}

extern "C" __declspec(dllexport)

bool LoadNetwork(char *filePath)

{

fstream f;

f.open(filePath,ios_base::in);

int i;

for(i=0;i<(N1+1)*(N0+1);i++)

{

f>>a1[i];

}

for(i=0;i<N2*(N1+1);i++)

{

f>>a2[i];

}

f.close();

return true;

}

//---------------------------------------------------------------------------

//------------------------------------------------------------------------

//------------------------------------------------------------------------

附帶一個使用SAMPLE

設定8各輸入訊號,4各隱藏層神經元,1個輸出神經元

//-----------------------VC.NET 控制檯程式pc.gif

#include<iostream>

#include<Windows.h>

typedef bool (*pInitNetwork)(int n0,int n1,int n2,double nn);

typedef bool (*pReleaseNetwork)();

typedef bool (*pWork)(double *input,int *output);

typedef bool (*pTran)(double *input,int *expected);

typedef bool (*pSaveNetwork)(char *filePath);

typedef bool (*pLoadNetwork)(char *filePath);

pInitNetwork InitNetwork=NULL;

pReleaseNetwork ReleaseNetwork=NULL;

pWork Work=NULL;

pTran Tran=NULL;

pSaveNetwork SaveNetwork=NULL;

pLoadNetwork LoadNetwork=NULL;

int main(int argc, char* argv[])

{

int i=0,j=0,k=0;

HMODULE h=::LoadLibrary("F://projects//BPNetwork3//Release//BPNetwork3.dll");

if(h==NULL)

{

std::cout<<"裝載DLL錯誤/n";

system("pause");

return -1;

}

InitNetwork=(pInitNetwork)::GetProcAddress(h,"InitNetwork");

ReleaseNetwork=(pReleaseNetwork)::GetProcAddress(h,"ReleaseNetwork");

Work=(pWork)::GetProcAddress(h,"Work");

Tran=(pTran)::GetProcAddress(h,"Tran");

SaveNetwork=(pSaveNetwork)::GetProcAddress(h,"SaveNetwork");

LoadNetwork=(pLoadNetwork)::GetProcAddress(h,"LoadNetwork");

if(InitNetwork==NULL||ReleaseNetwork==NULL||Work==NULL||Tran==NULL||SaveNetwork==NULL||LoadNetwork==NULL)

{

std::cout<<"取函式地址錯誤/n";

::FreeLibrary(h);

system("pause");

return -1;

}

InitNetwork(8,4,1,0.8);//-----------初始化 8個輸入,4個隱藏神經元,1個輸出,學習率0.8

double x[8];//8個輸入

int y[1];//1