神經網路-3層BP網的VC動態庫封裝
神經網路-3層BP網的VC動態庫封裝實現<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
神經網路是一個大範疇,這裡討論誤差反向傳播網路(BP網路) ,基本原理是通過分析網路輸出的誤差來調整網路的自由引數,使網路的大量自由引數平衡到一個收斂值,引數的收斂過程是訓練過程,訓練完成後網路基本穩定就可以投入應用.
網路運作過程:
簡單的單層BP網具有N0個輸入x,還含有N0個自由引數w(權值)
1.正向變換輸出
x和w求內積記為v,v再通過一個啟用函式後輸出即為網路輸出,一般的啟用函式要求為
logistic(v)=1/(1+exp(-av))
其中a為函式斜率.如此即得到網路的輸出值
另外比較常用的還可以是雙曲正切函式.根據需要選擇.
2.誤差反向傳播
誤差計算為e=d-o ,其中d為期望的網路輸出收斂值,o為網路實際輸出.
首先計算輸出結點的區域性梯度域
s=e*f’(v)
f為啟用函式,f’表示導數
權值調整值
delta=n*s*y
n為學習率,取值0.01~0.9
s是上面計算的區域性梯度域
y是此結點的上次輸出
然後進行權值調整
w=w+delta
迭代足夠多次後網路即可收斂
訓練過程就像教育一個小孩一樣,通過不斷教育教會小孩什麼使對的什麼是錯的,因此神經網路又屬於人工智慧範疇.
訓練的本質是使網路的自由引數w收斂到一個期望的水平,收斂使通過誤差反向傳播實現的,通過誤差計算自由引數的調整值,誤差將越來越小.
網路的本質是低維特徵空間逼近高維特徵空間的高度非線性對映.
BP網路不單隻可以有一層,還可以設定多層,當然誤差的計算就複雜一點.
下面是我用VC.NET製作的3層BP網路的DLL庫,一個輸入層,一個輸出層和一個隱藏層,各層神經元數可以根據需要調整.網路為全連線結構,採用logistic函式作為啟用函式.函式斜率固定為1,學習率可以自由調整
//--------------VC.NET DLL
//---------------------------以下開始為實際程式碼.
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();
//------對輸出層神經元的輸出量化到0或1
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 控制檯程式
#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