C++实现BP算法_单隐层_异或(可模拟各种函数并验证)

报告:

1、            题目:

建立一个含单隐层的双输入单输出网络,实现异或问题:

                 x1(1)=0                   x2(1)=1                 d(1)=1

                 x1(2)=1                   x2(2)=0                 d(2)=1

                 x1(3)=1                   x2(3)=1                 d(3)=0

                 x1(4)=0                   x2(4)=0                 d(4)=0  

    训练出相应的网络权值矩阵W

2、            实现步骤:

①初始化:设定wji(0), wkj(0)  ,函数采用Sigmoid()函数, y=1/(1+exp(-x)), y在(0,1)之间.设计一个“合理的网络结构”,隐层数为1 ,隐层节点为2;

②对每个输入样本进行如下计算:

    a 前向信号传播

    b 反向误差传播

n=n+1, 转入下一个计算周期。

3、            核心代码及说明:

参数说明:

innode  //输入结点数   

hidenode //隐含结点数   

 outnode  //输出结点数   

trainsample //BP训练样本数

double w[innode][hidenode];//隐含结点权值   

    double w1[hidenode][outnode];//输出结点权值  

    double rate_w; //权值学习率(输入层-隐含层)   

    double rate_w1;//权值学习率 (隐含层-输出层) 

    double e;//误差计算   

    double error;//允许的最大误差  

double pp[hidenode];//隐含结点的校正误差   

    double qq[outnode];//希望输出值与实际输出值的偏差   

    double yd[outnode];//希望输出值   

   

    double x[innode];//输入向量   

    double x1[hidenode];//隐含结点状态值   

    double x2[outnode];//输出结点状态值 

//输入样本 

double X[trainsample][innode]= {   

    {-1,0,1},{-1,1,0},{-1,1,1},{-1,0,0}   };   

//期望输出样本   

double Y[trainsample][outnode]={   

    {1},{1},{0},{0}     };

训练部分:

    for(int isamp=0;isamp<trainsample;isamp++)//循环训练一次样品   

    {  

        /输入层到隐层 

        for(int j=0;j<hidenode;j++)   

        {   

            o1[j]=0.0;   

            for(int i=0;i<innode;i++)   

                o1[j]=o1[j]+w[i][j]*x[i];//隐含层各单元输入激活值   

           x1[j]=1.0/(1+exp(-o1[j]));//x1[j]=1.0/(1+exp(-o1[j]-b1[j]));//隐含层各单元的输出  

  

        }   

    /隐层到输出层 

        for(int k=0;k<outnode;k++)   

        {   

            o2[k]=0.0;   

            for(int j=0;j<hidenode;j++)   

                o2[k]=o2[k]+w1[j][k]*x1[j]; //输出层各单元输入激活值   

           x2[k]=1.0/(1.0+exp(-o2[k]));  //输出层各单元输出      

        }   

    //计算偏差并调整权值

        for(int k=0;k<outnode;k++)   

        {   

           qq[k]=(yd[k]-x2[k])*x2[k]*(1-x2[k]); //希望输出与实际输出的偏差   

            for(int j=0;j<hidenode;j++)   

               w1[j][k]+=rate_w1*qq[k]*x1[j];  //下一次的隐含层和输出层之间的权值   

        }   

        for(int j=0;j<hidenode;j++)   

        {   

            pp[j]=0.0;   

            for(int k=0;k<outnode;k++)   

               pp[j]=pp[j]+qq[k]*w1[j][k];   

            pp[j]=pp[j]*x1[j]*(1-x1[j]); //隐含层的校正误差  

            for(int i=0;i<innode;i++)   

                w[i][j]+=rate_w*pp[j]*x[i]; //下一次的输入层和隐含层之间的新连接权值  

        }  

        for(int k=0;k<outnode;k++)   

        {   

           e+=fabs(yd[k]-x2[k])*fabs(yd[k]-x2[k]); //计算均方差   

        }   

        error=e/2.0;   

    }   

}

4、            运算结果:

(1)隐层节点数为2,满足允许误差0.01的运行结果,得到了矩阵w1和w,及迭代次数和误差,并经行验证,验证结果正确。运算次数较多,误差较小。

 

(2)隐层节点数为2,满足允许误差0.1的运行结果,运算次数减少,误差变大。

#include<iostream>  
#include<cmath>  
using namespace std;  
  
#define  innode 2  //输入结点数  
#define  hidenode 3//隐含结点数  
#define  outnode 1 //输出结点数  
#define  trainsample 4//BP训练样本数  
class BpNet  
{  
public:  
    void train(double p[trainsample][innode ],double t[trainsample][outnode]);//Bp训练  
    double p[trainsample][innode];     //输入的样本  
    double t[trainsample][outnode];    //样本要输出的  
  
    double *recognize(double *p);//Bp识别  
  
    void writetrain(); //写训练完的权值  
    void readtrain(); //读训练好的权值,这使的不用每次去训练了,只要把训练最好的权值存下来就OK  
  
    BpNet();  
    virtual ~BpNet();  
  
public:  
    void init();  
    double w[innode][hidenode];//隐含结点权值  
    double w1[hidenode][outnode];//输出结点权值  
    
  
    double rate_w; //权值学习率(输入层-隐含层)  
    double rate_w1;//权值学习率 (隐含层-输出层)  
   
  
    double e;//误差计算  
    double error;//允许的最大误差  
    double result[outnode];// Bp输出  
};  
  
BpNet::BpNet()  
{  
    error=0.1;  
    e=0.0;  
  
    rate_w=0.9;  //权值学习率(输入层--隐含层)  
    rate_w1=0.9; //权值学习率 (隐含层--输出层)  
    
}  
  
BpNet::~BpNet()  
{  
  
}  
  
void winit(double w[],int n) //权值初始化  
{  
  for(int i=0;i<n;i++)  
    w[i]= (rand()%10)*0.1-0.5;
}  
  
void BpNet::init()  
{  
    winit((double*)w,innode*hidenode);  
    winit((double*)w1,hidenode*outnode);  
  
}  
  
void BpNet::train(double p[trainsample][innode],double t[trainsample][outnode])  
{  
    double pp[hidenode];//隐含结点的校正误差  
    double qq[outnode];//希望输出值与实际输出值的偏差  
    double yd[outnode];//希望输出值  
  
    double x[innode]; //输入向量  
    double x1[hidenode];//隐含结点状态值  
    double x2[outnode];//输出结点状态值  
    double o1[hidenode];//隐含层激活值  
    double o2[outnode];//输出层激活值  
  
    for(int isamp=0;isamp<trainsample;isamp++)//循环训练一次样品  
    {  
        for(int i=0;i<innode;i++)  
            x[i]=p[isamp][i]; //输入的样本  
        for(int i=0;i<outnode;i++)  
            yd[i]=t[isamp][i]; //期望输出的样本  
  
        //构造每个样品的输入和输出标准  
		//输入层到隐层
        for(int j=0;j<hidenode;j++)  
        {  
            o1[j]=0.0;  
            for(int i=0;i<innode;i++)  
                o1[j]=o1[j]+w[i][j]*x[i];//隐含层各单元输入激活值  
           x1[j]=1.0/(1+exp(-o1[j]));// x1[j]=1.0/(1+exp(-o1[j]-b1[j]));//隐含层各单元的输出  
            //    if(o1[j]+b1[j]>0) x1[j]=1;  
            //else x1[j]=0;  
        }  
    //隐层到输出层
        for(int k=0;k<outnode;k++)  
        {  
            o2[k]=0.0;  
            for(int j=0;j<hidenode;j++)  
                o2[k]=o2[k]+w1[j][k]*x1[j]; //输出层各单元输入激活值  
           x2[k]=1.0/(1.0+exp(-o2[k]));  //输出层各单元输出  
            
        }  
  



        for(int k=0;k<outnode;k++)  
        {  
            qq[k]=(yd[k]-x2[k])*x2[k]*(1-x2[k]); //希望输出与实际输出的偏差  
            for(int j=0;j<hidenode;j++)  
                w1[j][k]+=rate_w1*qq[k]*x1[j];  //下一次的隐含层和输出层之间的新连接权  
        }  
  
        for(int j=0;j<hidenode;j++)  
        {  
            pp[j]=0.0;  
            for(int k=0;k<outnode;k++)  
                pp[j]=pp[j]+qq[k]*w1[j][k];  
            pp[j]=pp[j]*x1[j]*(1-x1[j]); //隐含层的校正误差  
  
            for(int i=0;i<innode;i++)  
                w[i][j]+=rate_w*pp[j]*x[i]; //下一次的输入层和隐含层之间的新连接权  
        }  
  
        for(int k=0;k<outnode;k++)  
        {  
            e+=fabs(yd[k]-x2[k])*fabs(yd[k]-x2[k]); //计算均方差  
        }  
        error=e/2.0;  
  
       
    }  
}  
  
double *BpNet::recognize(double *p)  
{  
    double x[innode]; //输入向量  
    double x1[hidenode]; //隐含结点状态值  
    double x2[outnode]; //输出结点状态值  
    double o1[hidenode]; //隐含层激活值  
    double o2[hidenode]; //输出层激活值  
  
    for(int i=0;i<innode;i++)  
        x[i]=p[i];  
  
    for(int j=0;j<hidenode;j++)  
    {  
        o1[j]=0.0;  
        for(int i=0;i<innode;i++)  
            o1[j]=o1[j]+w[i][j]*x[i]; //隐含层各单元激活值  
        x1[j]=1.0/(1+exp(-o1[j]));//x1[j]=1.0/(1.0+exp(-o1[j]-b1[j])); //隐含层各单元输出  
       
    }  
  
    for(int k=0;k<outnode;k++)  
    {  
        o2[k]=0.0;  
        for(int j=0;j<hidenode;j++)  
            o2[k]=o2[k]+w1[j][k]*x1[j];//输出层各单元激活值  
       x2[k]=1.0/(1.0+exp(-o2[k])); //  x2[k]=1.0/(1.0+exp(-o2[k]-b2[k]));//输出层各单元输出  
     
    }  
  
    for(int k=0;k<outnode;k++)  
    {  
        result[k]=x2[k];  
    }  
    return result;  
}  
  
void BpNet::writetrain()  
{  
    FILE *stream0;  
    FILE *stream1;  
    
    int i,j;  
    //隐含结点权值写入  
    if(( stream0 = fopen("w.txt", "w+" ))==NULL)  
    {  
        cout<<"创建文件失败!";  
        exit(1);  
    }  
    for(i=0;i<innode;i++)  
    {  
        for(j=0;j<hidenode;j++)  
        {  
            fprintf(stream0, "%f\n", w[i][j]);  
        }  
    }  
    fclose(stream0);  
  
    //输出结点权值写入  
    if(( stream1 = fopen("w1.txt", "w+" ))==NULL)  
    {  
        cout<<"创建文件失败!";  
        exit(1);  
    }  
    for(i=0;i<hidenode;i++)  
    {  
        for(j=0;j<outnode;j++)  
        {  
            fprintf(stream1, "%f\n",w1[i][j]);  
        }  
    }  
    fclose(stream1);  
  
}  
  
void BpNet::readtrain()  
{  
    FILE *stream0;  
    FILE *stream1;  
   
    int i,j;  
  
    //隐含结点权值读出  
    if(( stream0 = fopen("w.txt", "r" ))==NULL)  
    {  
        cout<<"打开文件失败!";  
        exit(1);  
    }  
    float  wx[innode][hidenode];  
    for(i=0;i<innode;i++)  
    {  
        for(j=0;j<hidenode;j++)  
        {  
            fscanf(stream0, "%f", &wx[i][j]);  
            w[i][j]=wx[i][j];  
        }  
    }  
    fclose(stream0);  
  
    //输出结点权值读出  
    if(( stream1 = fopen("w1.txt", "r" ))==NULL)  
    {  
        cout<<"打开文件失败!";  
        exit(1);  
    }  
    float  wx1[hidenode][outnode];  
    for(i=0;i<hidenode;i++)  
    {  
        for(j=0;j<outnode;j++)  
        {  
            fscanf(stream1, "%f", &wx1[i][j]);  
            w1[i][j]=wx1[i][j];  
        }  
    }  
    fclose(stream1);  
  }
//输入样本  可改变,以实现不同函数的模拟
double X[trainsample][innode]= {  
    {0,1},{1,0},{1,1,},{0,0} 
    };  
//期望输出样本  
double Y[trainsample][outnode]={  
    {1},{1},{0},{0}  
    };  
  
int main()  
{  
    BpNet bp;  
    bp.init();  
    int times=0;  
    while(bp.error>0.01)  
    {  
        bp.e=0.0;  
        times++;  
        bp.train(X,Y);  
        cout<<"Times="<<times<<" error="<<bp.error<<endl;  
    }  
    cout<<"trainning complete..."<<endl;  
	bp.writetrain();
	bp.readtrain();
    double m[innode]={1,1};  
    double *r=bp.recognize(m); 
	 for(int i=0;i<innode;++i)  
       cout<<m[i]; 
	 cout<<"的训练结果为";
    for(int i=0;i<outnode;++i)  
       cout<<bp.result[i]<<" ";  
    double cha[trainsample][outnode];  
    double mi=100;  
    double index;  
    for(int i=0;i<trainsample;i++)  
    {  
        for(int j=0;j<outnode;j++)  
        {  
            //找差值最小的那个样本  
            cha[i][j]=(double)(fabs(Y[i][j]-bp.result[j]));  
            if(cha[i][j]<mi)  
            {  
                mi=cha[i][j];  
                index=Y[i][j];  //得到对应的期望输出
            }  
        }  
    }  
    
    cout<<" 期望输出为 "<<index<<endl;  
    cout<<endl;  
    return 0;  
} 

  • 1
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值