C++版全連接神經網絡
本程序主要包括兩部分:一、神經元類NeuronNode,里面含有基本的神經元信息,比如權重w、偏置項b,輸出項out_a,他們都是以矩陣的形式表示;該類可以定義一個或多個神經層,每個神經層節(jié)點組成矩陣
二、神經網絡類NeronNet,在該類中將NeuronNode組織成多層神經網絡。
class NeuronNode { public: Mat z;//單個輸出,將w與前一層的輸出out_a相乘,并加上偏置項b Mat out_a;//本神經層的輸出矩陣 Mat b; Mat w;//后一層網絡各個節(jié)點的權重 int numOfNode;//當前層的節(jié)點數(shù),也就是神經元的個數(shù) int nthOfLayer;//當前層為第n層 }; Mat sigmoid(Mat&z)//激活函數(shù) { 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)//激活函數(shù)的微分形式 { 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;//獲取層數(shù) nn=new NeuronNode[numOfLayers];//創(chuàng)建見網絡層數(shù) 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的行數(shù)為當前層節(jié)點個數(shù),w的列數(shù)為前一層節(jié)點個數(shù) //用隨機數(shù)為第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];//該數(shù)組表示各個神經層的反傳誤差,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;//輸入矢量元素個數(shù) int outNum=4;//輸出矢量元素個數(shù) int a[]={inNum,8,8,outNum};//4層網絡,每層網絡的神經元個數(shù),第0層僅僅作為輸入,可以不算作網絡層 vector<int> numOfNodeOfperLayer(a,a+numOfLayer);//每層節(jié)點個數(shù) 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; }
下面是輸出結果:

浙公網安備 33010602011771號