神经网络-3层BP网的VC动态库封装

来源:互联网 发布:单片机仿真软件下载 编辑:程序博客网 时间:2024/04/29 04:35

神经网络-3BP网的VC动态库封装实现

 

神经网络是一个大范畴,这里讨论 误差反向传播网络(BP网络) ,基本原理是通过分析网络输出的误差来调整网络的自由参数,使网络的大量自由参数平衡到一个收敛值,参数的收敛过程是训练过程,训练完成后网络基本稳定就可以投入应用.

 

网络运作过程:

简单的单层BP网具有N0个输入x,还含有N0个自由参数w(权值)

1.正向变换输出

xw求内积记为v,v再通过一个激活函数后输出即为网络输出,一般的激活函数要求为sigmoid非线性函数.而具有sigmoid非线性性的其中一个广泛应用的函数为logistic函数,表达式为

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制作的3BP网络的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();

 

       //------对输出层神经元的输出量化到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 控制台程序

#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个输出

 

     //-------------训练前

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

     {

         //-----------转换成82进制数输入

         for(k=0;k<8;k++)

         {

              x[k]=(double)((i>>k) & 1);

         }

 

         Work(x,y);//送入网络分类

         std::cout<<"输入"<<i<<"/t输出"<<y[0]<<"/n";//显示输出

     }

 

     std::cout<<"/n/n/n/n";

     //-----------------开始100轮训练,目标是训练网络具有区分09的数是奇数还是偶数的能力,奇数输出1,偶数输出0

     int expected=0;//期望值

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

     {

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

         {

              //转成82进制数作为网络的8个输入

              for(k=0;k<8;k++)

              {

                   x[k]=(double)((j>>k) & 1);

              }

 

              expected=j%2;//期望结果

 

              Tran(x,&expected);//训练

         }

     }

 

     //-------------训练完成,下面检验训练成果

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

     {

         //-----------转换成82进制数输入

         for(k=0;k<8;k++)

         {

              x[k]=(double)((i>>k) & 1);

         }

 

         Work(x,y);//送入网络分类

         std::cout<<"输入"<<i<<"/t输出"<<y[0]<<"/n";//显示网络输出

     }

 

     ReleaseNetwork();

     ::FreeLibrary(h);

     system("pause");

     return 0;

}

 

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

经过实际运行,这个程序学会了区分09之间的数字是奇数还是偶数的能力.实验成功.

程序的下载地址为

http://www.lingch.net/myproducts/BPNetwork.rar

此网络的结构如图

 
神经网络已经广泛应用于模式识别和模式分类上.

原创粉丝点击