传一个早期写的神经网络的代码C++, 很方便做成动态库

本文介绍了一个基于反向传播(BP)算法的人工神经网络(ANN)类实现细节。该类支持动量项改进,并提供了训练、预测及权重保存与加载等功能。

ContractedBlock.gifCode

 

此代码可在任何场合使用       但请保留注释

 

ExpandedBlockStart.gif




//**************基于BP的ANN类*******************
//                      --Feathersky
//***********************************************




#ifndef ANN_BP_H
#define ANN_BP_H




#include 
"math.h"
#include 
<stdlib.h>
#include 
<iostream.h>
#include 
<fstream.h>
#include 
<iomanip.h>

#include
"MemoryTool1.h"




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

class ANN_BP 
{

public:    
    
int nMethod;       //动量项标志

    
double Rate;      //学习率

    
int layer1;       //输入层结点数
    int layer2;       //中间层结点数
    int layer3;       //输出层结点数 

    
double *Y1;       //第一层输出
    double *Y2;       //第二层输出
    double *Y3;       //第三层输出
    double *Dest;     //期望输出

    
double **W1;     //权值矩阵
    double **W2;

    
double **SW1;    //第一层权值的改变量  动量项作法使用
    double **SW2;    //第二层权值的改变量  动量项作法使用
    
    
double *S3;     //辅助变量
    double *S2;        //辅助变量

    
double **G1_1;  //第一层权值距阵各分量偏导  
    double **G2_1;  //第二层权值距阵各分量偏导  

public:

    
// 类构造函数,输入参数为3层BP网络每层的结点数。
    
// 在此函数中进行内存分配等初始化工作

    ANN_BP()
    {
        Y1
=Y2=Y3=Dest=NULL;
        W1
=W2=NULL;
        SW1
=SW2=NULL;
        S3
=S2=NULL;
        G1_1
=G2_1=NULL;    
        
        SetMethod();
    }

    ANN_BP(
int input,int middle,int output)    
    {        
        SetLayerNodeCount(input,middle,output);    
        
        SetMethod();
    }
    

    
// 类析构函数  释放分配的内存
    ~ANN_BP()    
    {
        FreeMemory();
    }

    
void FreeMemory()
    {
        delete[] Y1;
        delete[] Y2;
        delete[] Y3;
        delete[] Dest;

        free2D(W1);
        free2D(W2);
        free2D(SW1);
        free2D(SW2);

        delete[] S2;
        delete[] S3;

        free2D(G1_1);
        free2D(G2_1);
    }

    
void SetLayerNodeCount(int input,int middle,int output)
    {
        FreeMemory();

        layer1
=input+1;        //输入层结点数
        layer2=middle;       //中间层结点数
        layer3=output;         //输出层结点数 

    
        Y1  
= new double[layer1];
        Y2  
= new double[layer2];
        Y3  
= new double[layer3];
        Dest
= new double[layer3];

        W1  
=malloc2D(layer2,layer1);
        W2  
=malloc2D(layer3,layer2);
        SW1 
=malloc2D(layer2,layer1);
        SW2 
=malloc2D(layer3,layer2);
    
        S2  
= new double[layer2];
        S3  
= new double[layer3];

        G1_1  
=malloc2D(layer2,layer1);
        G2_1  
=malloc2D(layer3,layer2);
    
        initial(); 
    }



    
void SetMethod(int method=0)
    {
        nMethod
=method;       //是否使用动量项标志        
    }

private:    

    
//用随机数初始化权值
    void initial()    
    {        
        
for(int i=0;i<layer1;i++)
        
for(int j=0;j<layer2;j++)
        {
            W1[i][j]
=(double)(rand()%2000-1000)/1000;
            
if(W1[i][j]==0) W1[i][j]=0.1;
        }
            
        
for(    i=0;i<layer2;i++)
        
for(int j=0;j<layer3;j++)
        {
            W2[i][j]
=(double)(rand()%2000-1000)/1000;
            
if(W2[i][j]==0) W2[i][j]=0.1;
        }            
    }


    
    
int sgn(double i)
    {
        
if(i>0return 1;
        
if(i<0return -1;
        
return 0;
    }
    

   
//sigmoid 函数
    double func(double i)      
    {
        
//    return (double)( (1-exp(-i))/(1+exp(-i)) );
        if(i<-45return 0;      
        
if(i>45return 1;
        
return 1.0/(1.0+exp(-i));        
    }
    
/*
    我在程序中调试,曾遇见过此类溢出,故此技巧是必须的
    Subject: How to avoid overflow in the logistic function? 
    The formula for the logistic activation function is often written as: 
    netoutput = 1 / (1+exp(-netinput));
    
    But this formula can produce floating-point overflow in the exponential function if you program it in this simple form. To avoid overflow, you can do this: 
     if (netinput < -45) netoutput = 0;
     else if (netinput > 45) netoutput = 1;
     else netoutput = 1 / (1+exp(-netinput));
      
    The constant 45 will work for double precision on all machines that I know of, but there may be some bizarre machines where it will require some adjustment. Other activation functions can be handled similarly. 
    
*/
    
    

    
void ComputeY()   // 计算各层输出Y1[], Y2[],Y3[]
    {        
        
int i;

        
for(i=0; i<layer2; i++)
        {    
            Y2[i]
=0;    
            
for(int j=0;j<layer1;j++)      Y2[i]+=Y1[j]*W1[j][i];
            Y2[i]
=func(Y2[i]);                      
        }    
    
        
        
for(i=0; i<layer3; i++)
        {            
            Y3[i]
=0;
            
for(int j=0;j<layer2;j++)   Y3[i]+=Y2[j]*W2[j][i];
            Y3[i]
=func(Y3[i]);                      
        }
        
    }
    
    
    
    
void ComputeG()     //计算 误差对各层矩阵的偏导,求得梯度信息
    {    
        
int i,j;
        
        
for(i=0;i<layer3;i++)    
        {
            S3[i]
=(Dest[i]-Y3[i])*(1-Y3[i])*Y3[i];        
        }
        
        
for(i=0;i<layer2;i++)
        {
            S2[i]
=0;
            
for(int j=0;j<layer3;j++)  S2[i]+=W2[i][j]*S3[j];
            S2[i]
*=(1-Y2[i]) * Y2[i];
        }

        
for( i=0;i<layer1;i++ )
        
for( j=0;j<layer2;j++ )
            G1_1[i][j]
=Y1[i]*S2[j];  
                    
        
for( i=0;i<layer2;i++ )
        
for( j=0;j<layer3;j++ ) 
            G2_1[i][j]
=Y2[i]*S3[j];    
    }
    

        
    
void WeightAdapt()    // 按梯度方向修正权值
    {    

        
if(nMethod==0)            //不使用动量项改进
        {
            
for(int i=0;i<layer2;i++)
            
for(int j=0;j<layer3;j++)
            {                
                W2[i][j]
+=Rate*G2_1[i][j];                     
            }
                
            
for(i=0;i<layer1;i++)
            
for(int j=0;j<layer2;j++)
            {
                W1[i][j]
+=Rate*G1_1[i][j];                
            }
        }

        
if(nMethod==1)                //使用动量项改进 此方式只有对一个样本训练的次数大于1时有用
        {
            
for(int i=0;i<layer2;i++)
            
for(int j=0;j<layer3;j++)
            {                
                W2[i][j]
+=Rate*G2_1[i][j]+0.5*Rate*SW2[i][j]; 

                SW2[i][j]
=Rate*G2_1[i][j];
            }
                
            
for(i=0;i<layer1;i++)
            
for(int j=0;j<layer2;j++)
            {
                W1[i][j]
+=Rate*G1_1[i][j]+0.5*Rate*SW1[i][j]; 

                SW1[i][j]
=Rate*G1_1[i][j];
            }
        }

    }



public:
    
    
//LimitTrainCount 最大训练次数
    
//Error           允许学习误差
    double TrainOneCase(double Input[],double Out[],double Error=0.000001,int LimitTrainCount=500)   
    {        
        
int w,h;

        
if(nMethod==1)
        {
            
for( h=0; h<layer1; h++)
            
for( w=0; w<layer2; w++)
            {
                SW1[h][w]
=0;                       //动量项置为0
            }
                
            
for( h=0; h<layer2; h++)
            
for( w=0; w<layer3; w++)
            {
                SW2[h][w]
=0;                       //动量项置为0
            } 
        }


            
        Y1[layer1
-1]=-1;                               //域值对应的输入
        for(int i=0;i<layer1-1;i++) Y1[i] = Input[i];

        
for(    i=0;i<layer3;i++) Dest[i] = Out[i];


        
double TrainError;        
    
        Rate
=0.3;    
        
do
        {
            
if(--LimitTrainCount<0break;    
            
            ComputeY();        
            ComputeG();
            WeightAdapt();
            
            TrainError
=0;
            
for(i=0; i<layer3; i++)    TrainError+=(Dest[i]-Y3[i])*(Dest[i]-Y3[i])/2;

            Rate
=0.05+TrainError/5;   //根据误差自适应修改学习率
        }
        
while(TrainError>Error);      // 误差指标

        
return TrainError;            //返回误差
    }

    
    
double ComputeError(double Input[],double Out[])
    {
        
double TrainError=0;

        Y1[layer1
-1]=-1;
        
for(int i=0;i<layer1-1;i++) Y1[i] = Input[i];

        
for(    i=0;i<layer3;i++) Dest[i] = Out[i];        

        
        ComputeY();        
        
        
for(i=0; i<layer3; i++)    TrainError+=(Dest[i]-Y3[i])*(Dest[i]-Y3[i])/2;
        
        
return TrainError;         //返回误差
    }



    
//Out[]即为输出结果
    int DecideOneCase(double Input[],double Out[])
    {
        Y1[layer1
-1]=-1;
        
for(int i=0;i<layer1-1;i++) Y1[i] = Input[i];

        ComputeY();

        
double max=0;
        
int    index=0;

        
for(i=0;i<layer3; i++ ) 
        {
            Out[i]
=Y3[i];

            
if(Y3[i]>max) 
            {
                max
=Y3[i];
                index
=i;
            }
        }

        
return index;  //index 为out[]中最大的那个数的索引
    }


/********************************************
                保存机制     仅保存权重
/******************************************
*/

    
int SaveWeight(char* file)   //save the value of weight
    {
        ofstream 
in(file);
        
in.precision(32);    
        
in<<layer1-1<<' '<<layer2<<' '<<layer3<<endl;    

        
for(int i=0;i<layer1;i++)
        {
            
for(int j=0;j<layer2;j++)    in<<setw(64)<<W1[i][j]<<' ';
            
in<<endl;
        }
        
in<<endl;
        
for(i=0;i<layer2;i++)
        {
            
for(int j=0;j<layer3;j++)    in<<setw(64)<<W2[i][j]<<' ';
            
in<<endl;
        }
        
        
in.close();
        
return 1;
        
    }
    
    
int LoadWeight(char * file)    //load the value of weight
    {
        
//CFileFind finder; 
        
//if(!finder.FindFile(file)) { initial(); return; }    

        
int input, middle, output;

        ifstream 
out(file);    
        
//out.precision(32);

        
if(out.fail() || out.eof()) return 0;

        
out>>input>>middle>>output;
        
        SetLayerNodeCount(input,middle,output);
        
        
for(int i=0;i<layer1;i++)
        
for(int j=0;j<layer2;j++)    
            
out>>W1[i][j];
        
        
for(i=0;i<layer2;i++)
        
for(int j=0;j<layer3;j++)
            
out>>W2[i][j];
                
        
out.close();    

        
return 1;                
    }        

};








#endif







//--------------------二维数据内存管理----Feathersky-----------------------------------------------
                

/*************************************************
二维数据内存空间 每行以DWORD方式对齐
二位数据内存空间是连续的 
每行首地址另存于一数组
*************************************************
*/



#ifndef MEMORYTOOL1_H
#define MEMORYTOOL1_H



#include 
<malloc.h>



/****************************************************************************
* Funciton:  malloc2D
* Puroose:   分配二维数据内存空间
***************************************************************************
*/
double ** malloc2D(int nW, int nH)
{       
    
double ** pLineHead=(double **)malloc(nH*sizeof(double *));

    
double * pMem=(double *)malloc(nW*nH*sizeof(double));

    
for(int i=0; i<nH; i++)
    {
        pLineHead[i]
=(double *)((BYTE *)pMem+nW*i*sizeof(double));    
    }
    
    
return pLineHead;
}



/****************************************************************************
* Funciton:  Free2D
* Puroose:   释放二维数据内存空间
***************************************************************************
*/

inline 
void free2D(double  ** & pLineHead)
{
    
if(pLineHead)
    {    
        free(pLineHead[
0]);    
        free(pLineHead);
    }    
}




#endif





转载于:https://www.cnblogs.com/feathersky/archive/2008/12/18/1357450.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值