C++版全连接神经网络

本程序主要包括两部分:一、神经元类NeuronNode,里面含有基本的神经元信息,比如权重w、偏置项b,输出项out_a,他们都是以矩阵的形式表示;该类可以定义一个或多个神经层,每个神经层节点组成矩阵

                                       二、神经网络类NeronNet,在该类中将NeuronNode组织成多层神经网络。

 

class NeuronNode
{
public:
    Mat z;//单个输出,将w与前一层的输出out_a相乘,并加上偏置项b
    Mat out_a;//本神经层的输出矩阵
    Mat b;
    Mat w;//后一层网络各个节点的权重
    int numOfNode;//当前层的节点数,也就是神经元的个数
    int nthOfLayer;//当前层为第n层
};

Mat sigmoid(Mat&z)//激活函数
{
    Mat temp=Mat(z.size(),z.type(),Scalar::all(0));
    for(int i=0;i<z.rows;i++)
    {
        for(int j=0;j<z.cols;j++)
        {
            float x=z.ptr<float>(i)[j];
            temp.ptr<float>(i)[j]=1/(1+exp(-x));
        }
    }
    return temp;
}
Mat sigmoid_d(Mat&z)//激活函数的微分形式
{
    Mat temp=Mat(z.size(),z.type(),Scalar::all(0));
    for(int i=0;i<z.rows;i++)
    {
        for(int j=0;j<z.cols;j++)
        {
            float x=z.ptr<float>(i)[j];
            float sig=1/(1+exp(-x));
            temp.ptr<float>(i)[j]=sig*(1-sig);
        }
    }
    return temp;
}
class NeronNet
{
public:
    NeronNet(int layer_num,vector<int> node_num_perLayer,Mat&inputMode,Mat&expectOut)
    {
        vector<int> pn=node_num_perLayer;//用一个简化指针替代原指针,只是为了方便
        if(inputMode.rows!=pn[0])
        {
            cout<<"Err: your input size !=num of node of input layer!\n";
            exit(1);
        }
        if(layer_num!=(int)pn.size())
        {
            cout<<"Err: layer_num !=num of layers from node of input layer!\n";
            exit(1);
        }
        if(numOfLayers<3)
        {
            cout<<"Err: layer_num <3!\n";
            exit(1);
        }
        this->expectOut=expectOut;
        this->totalErr=1;
        numOfLayers=layer_num;//获取层数
        nn=new NeuronNode[numOfLayers];//创建见网络层数
        nn[0].out_a=inputMode;//第1层,即nn[0]层,获取输入矢量

        cv::RNG rng;
        for(int i=1;i<numOfLayers;i++)
        {
            nn[i].b=Mat(pn[i],1,CV_32F,Scalar(0));
            nn[i].w=Mat(pn[i],pn[i-1],CV_32F,Scalar(0));//w的行数为当前层节点个数,w的列数为前一层节点个数
            //用随机数为第i层的偏移b、权重w初始化
            rng.fill(nn[i].b,RNG::UNIFORM,1,0);
            rng.fill(nn[i].w,RNG::UNIFORM,1,0);
            if(inputMode.cols>1)
            {
                int nx=inputMode.cols;
                Mat nnb;
                repeat(nn[i].b,1,nx,nnb);
                nn[i].b=nnb.clone();
            }
        }

    }

    void calcForward()
    {
        for(int i=1;i<numOfLayers;i++)
        {
            nn[i].z=(nn[i].w)*(nn[i-1].out_a)+nn[i].b;
            nn[i].out_a=sigmoid(nn[i].z);
        }
    }
    void calcBackward(float alpha)
    {

        int L=numOfLayers-1;//最后一层的角标
        Mat D[numOfLayers];//该数组表示各个神经层的反传误差,D[0]废弃
        Mat delt[numOfLayers];//计算Bias的误差,实际上就是使用D[l]的列均值
        //首先计算最后一层误差
        {
            D[L]=(nn[L].out_a-expectOut);
            D[L] =D[L].mul(sigmoid_d(nn[L].z));
            reduce(D[L],delt[L],1,REDUCE_AVG);//计算D[L]所有列的平均值
        }
        //计算中间层误差
        for(int i=1;i<L;i++)
        {
            D[L-i]=(nn[L-i+1].w.t()*D[L-i+1]).mul(sigmoid_d(nn[L-i].z));
            reduce(D[L-i],delt[L-i],1,REDUCE_AVG);//计算D[L]所有列的平均值
        }
        //更新权重和偏置

        for(int i=1;i<=L;i++)
        {
            nn[i].w=nn[i].w-alpha*D[i]*nn[i-1].out_a.t();

            if(expectOut.cols>1)
            {
                Mat del;
                repeat(delt[i],1,expectOut.cols,del);
                delt[i]=del.clone();
            }
            nn[i].b=nn[i].b-alpha*delt[i];
        }
    }
    NeuronNode *nn;
    int numOfNeuron;
    int numOfLayers;
    Mat expectOut;
    float totalErr;
};

下面是演示程序:在该程序中同时输入两个学习矢量

1,-1,

0, 1,
0,-1,
0,-1
这两个矢量的期望输出为:
 0,0,
 0,1,
 0,0,
1,0
通过学习,使得网络具有识别两个输入矢量的能力。
在本程序中,可以同时学习多个矢量,当然也可以只学习一个矢量。
下面是实例:
int main()
{
    int numOfLayer=4;
    int inNum=4;//输入矢量元素个数
    int outNum=4;//输出矢量元素个数
    int a[]={inNum,8,8,outNum};//4层网络,每层网络的神经元个数,第0层仅仅作为输入,可以不算作网络层
    vector<int> numOfNodeOfperLayer(a,a+numOfLayer);//每层节点个数

    Mat inputL=(Mat_<float>(inNum,2)<<1,-1,
                                      0, 1,
                                      0,-1,
                                      0,-1);//第0层,作为矢量输入层
    Mat epout=(Mat_<float>(inNum,2)<<0,0,
                                     0,1,
                                     0,0,
                                     1,0);//最后一层,期望输出矢量
    cout<<"expected output:"<<endl<<epout<<endl<<endl;
    NeronNet nnet(numOfLayer,numOfNodeOfperLayer,inputL,epout);
    for(int i=0;i<15000;i++)//训练网络
    {
        nnet.calcForward();
        nnet.calcBackward(0.1);
    }

cout<<"practice output:"<<endl<<nnet.nn[numOfLayer-1].out_a<<endl<<endl;

    return 0;
}

 

下面是输出结果:

 

 



 

posted @ 2022-08-09 15:03  凤凰_1  阅读(305)  评论(0编辑  收藏  举报