[轉]BP人工神經網絡的C++實現


原文地址:http://blog.csdn.net/luxiaoxun/article/details/7649945

 
 分類:

BP(Back Propagation)網絡是1986年由Rumelhart和McCelland為首的科學家小組提出,是一種按誤差逆傳播算法訓練的多層前饋網絡,是目前應用最廣泛的神經網絡模型之一。BP網絡能學習和存貯大量的輸入輸出模式映射關系,而無需事前揭示描述這種映射關系的數學方程。它的學習規則是使用最速下降法(梯度法),通過反向傳播來不斷調整網絡的權值和閾值,使網絡的誤差平方和最小。BP神經網絡模型拓撲結構包括輸入層(input layer)、隱層(hide layer)和輸出層(output layer)。

以下是我收集的一些關於神經網絡的文章:

神經網絡介紹——利用反向傳播算法的模式學習
http://www.ibm.com/developerworks/cn/Linux/other/l-neural/index.html

人工智能 Java 坦克機器人系列: 神經網絡,上部
http://www.ibm.com/developerworks/cn/java/j-lo-robocode3/index.html
人工智能 Java 坦克機器人系列: 神經網絡,下部
http://www.ibm.com/developerworks/cn/java/j-lo-robocode4/

使用 Python 構造神經網絡--Hopfield 網絡可以重構失真的圖案並消除噪聲
http://www.ibm.com/developerworks/cn/linux/l-neurnet/

提供一個Matlab的BP神經網絡的基礎資料
http://www.cnblogs.com/galaxyprince/archive/2010/12/20/1911157.html

http://www.codeproject.com/KB/recipes/aforge_neuro.aspx
作者已經給出好幾種形式的應用例子

以下C++代碼實現了BP網絡,通過8個3位二進制樣本對應一個期望輸出,訓練BP網絡,最后訓練好的網絡可以將輸入的三位二進制數對應輸出一位十進制數。

 

[cpp]  view plain  copy
 
  1. //將三位二進制數轉為一位十進制數  
  2.   
  3. #include<iostream>  
  4. #include<cmath>  
  5. using namespace std;  
  6.   
  7. #define  innode 3  //輸入結點數  
  8. #define  hidenode 10//隱含結點數  
  9. #define  outnode 1 //輸出結點數  
  10. #define  trainsample 8//BP訓練樣本數  
  11. class BpNet  
  12. {  
  13. public:  
  14.     void train(double p[trainsample][innode ],double t[trainsample][outnode]);//Bp訓練  
  15.     double p[trainsample][innode];     //輸入的樣本  
  16.     double t[trainsample][outnode];    //樣本要輸出的  
  17.   
  18.     double *recognize(double *p);//Bp識別  
  19.   
  20.     void writetrain(); //寫訓練完的權值  
  21.     void readtrain(); //讀訓練好的權值,這使的不用每次去訓練了,只要把訓練最好的權值存下來就OK  
  22.   
  23.     BpNet();  
  24.     virtual ~BpNet();  
  25.   
  26. public:  
  27.     void init();  
  28.     double w[innode][hidenode];//隱含結點權值  
  29.     double w1[hidenode][outnode];//輸出結點權值  
  30.     double b1[hidenode];//隱含結點閥值  
  31.     double b2[outnode];//輸出結點閥值  
  32.   
  33.     double rate_w; //權值學習率(輸入層-隱含層)  
  34.     double rate_w1;//權值學習率 (隱含層-輸出層)  
  35.     double rate_b1;//隱含層閥值學習率  
  36.     double rate_b2;//輸出層閥值學習率  
  37.   
  38.     double e;//誤差計算  
  39.     double error;//允許的最大誤差  
  40.     double result[outnode];// Bp輸出  
  41. };  
  42.   
  43. BpNet::BpNet()  
  44. {  
  45.     error=1.0;  
  46.     e=0.0;  
  47.   
  48.     rate_w=0.9;  //權值學習率(輸入層--隱含層)  
  49.     rate_w1=0.9; //權值學習率 (隱含層--輸出層)  
  50.     rate_b1=0.9; //隱含層閥值學習率  
  51.     rate_b2=0.9; //輸出層閥值學習率  
  52. }  
  53.   
  54. BpNet::~BpNet()  
  55. {  
  56.   
  57. }  
  58.   
  59. void winit(double w[],int n) //權值初始化  
  60. {  
  61.   for(int i=0;i<n;i++)  
  62.     w[i]=(2.0*(double)rand()/RAND_MAX)-1;  
  63. }  
  64.   
  65. void BpNet::init()  
  66. {  
  67.     winit((double*)w,innode*hidenode);  
  68.     winit((double*)w1,hidenode*outnode);  
  69.     winit(b1,hidenode);  
  70.     winit(b2,outnode);  
  71. }  
  72.   
  73. void BpNet::train(double p[trainsample][innode],double t[trainsample][outnode])  
  74. {  
  75.     double pp[hidenode];//隱含結點的校正誤差  
  76.     double qq[outnode];//希望輸出值與實際輸出值的偏差  
  77.     double yd[outnode];//希望輸出值  
  78.   
  79.     double x[innode]; //輸入向量  
  80.     double x1[hidenode];//隱含結點狀態值  
  81.     double x2[outnode];//輸出結點狀態值  
  82.     double o1[hidenode];//隱含層激活值  
  83.     double o2[hidenode];//輸出層激活值  
  84.   
  85.     for(int isamp=0;isamp<trainsample;isamp++)//循環訓練一次樣品  
  86.     {  
  87.         for(int i=0;i<innode;i++)  
  88.             x[i]=p[isamp][i]; //輸入的樣本  
  89.         for(int i=0;i<outnode;i++)  
  90.             yd[i]=t[isamp][i]; //期望輸出的樣本  
  91.   
  92.         //構造每個樣品的輸入和輸出標准  
  93.         for(int j=0;j<hidenode;j++)  
  94.         {  
  95.             o1[j]=0.0;  
  96.             for(int i=0;i<innode;i++)  
  97.                 o1[j]=o1[j]+w[i][j]*x[i];//隱含層各單元輸入激活值  
  98.             x1[j]=1.0/(1+exp(-o1[j]-b1[j]));//隱含層各單元的輸出  
  99.             //    if(o1[j]+b1[j]>0) x1[j]=1;  
  100.             //else x1[j]=0;  
  101.         }  
  102.   
  103.         for(int k=0;k<outnode;k++)  
  104.         {  
  105.             o2[k]=0.0;  
  106.             for(int j=0;j<hidenode;j++)  
  107.                 o2[k]=o2[k]+w1[j][k]*x1[j]; //輸出層各單元輸入激活值  
  108.             x2[k]=1.0/(1.0+exp(-o2[k]-b2[k])); //輸出層各單元輸出  
  109.             //    if(o2[k]+b2[k]>0) x2[k]=1;  
  110.             //    else x2[k]=0;  
  111.         }  
  112.   
  113.         for(int k=0;k<outnode;k++)  
  114.         {  
  115.             qq[k]=(yd[k]-x2[k])*x2[k]*(1-x2[k]); //希望輸出與實際輸出的偏差  
  116.             for(int j=0;j<hidenode;j++)  
  117.                 w1[j][k]+=rate_w1*qq[k]*x1[j];  //下一次的隱含層和輸出層之間的新連接權  
  118.         }  
  119.   
  120.         for(int j=0;j<hidenode;j++)  
  121.         {  
  122.             pp[j]=0.0;  
  123.             for(int k=0;k<outnode;k++)  
  124.                 pp[j]=pp[j]+qq[k]*w1[j][k];  
  125.             pp[j]=pp[j]*x1[j]*(1-x1[j]); //隱含層的校正誤差  
  126.   
  127.             for(int i=0;i<innode;i++)  
  128.                 w[i][j]+=rate_w*pp[j]*x[i]; //下一次的輸入層和隱含層之間的新連接權  
  129.         }  
  130.   
  131.         for(int k=0;k<outnode;k++)  
  132.         {  
  133.             e+=fabs(yd[k]-x2[k])*fabs(yd[k]-x2[k]); //計算均方差  
  134.         }  
  135.         error=e/2.0;  
  136.   
  137.         for(int k=0;k<outnode;k++)  
  138.             b2[k]=b2[k]+rate_b2*qq[k]; //下一次的隱含層和輸出層之間的新閾值  
  139.         for(int j=0;j<hidenode;j++)  
  140.             b1[j]=b1[j]+rate_b1*pp[j]; //下一次的輸入層和隱含層之間的新閾值  
  141.     }  
  142. }  
  143.   
  144. double *BpNet::recognize(double *p)  
  145. {  
  146.     double x[innode]; //輸入向量  
  147.     double x1[hidenode]; //隱含結點狀態值  
  148.     double x2[outnode]; //輸出結點狀態值  
  149.     double o1[hidenode]; //隱含層激活值  
  150.     double o2[hidenode]; //輸出層激活值  
  151.   
  152.     for(int i=0;i<innode;i++)  
  153.         x[i]=p[i];  
  154.   
  155.     for(int j=0;j<hidenode;j++)  
  156.     {  
  157.         o1[j]=0.0;  
  158.         for(int i=0;i<innode;i++)  
  159.             o1[j]=o1[j]+w[i][j]*x[i]; //隱含層各單元激活值  
  160.         x1[j]=1.0/(1.0+exp(-o1[j]-b1[j])); //隱含層各單元輸出  
  161.         //if(o1[j]+b1[j]>0) x1[j]=1;  
  162.         //    else x1[j]=0;  
  163.     }  
  164.   
  165.     for(int k=0;k<outnode;k++)  
  166.     {  
  167.         o2[k]=0.0;  
  168.         for(int j=0;j<hidenode;j++)  
  169.             o2[k]=o2[k]+w1[j][k]*x1[j];//輸出層各單元激活值  
  170.         x2[k]=1.0/(1.0+exp(-o2[k]-b2[k]));//輸出層各單元輸出  
  171.         //if(o2[k]+b2[k]>0) x2[k]=1;  
  172.         //else x2[k]=0;  
  173.     }  
  174.   
  175.     for(int k=0;k<outnode;k++)  
  176.     {  
  177.         result[k]=x2[k];  
  178.     }  
  179.     return result;  
  180. }  
  181.   
  182. void BpNet::writetrain()  
  183. {  
  184.     FILE *stream0;  
  185.     FILE *stream1;  
  186.     FILE *stream2;  
  187.     FILE *stream3;  
  188.     int i,j;  
  189.     //隱含結點權值寫入  
  190.     if(( stream0 = fopen("w.txt", "w+" ))==NULL)  
  191.     {  
  192.         cout<<"創建文件失敗!";  
  193.         exit(1);  
  194.     }  
  195.     for(i=0;i<innode;i++)  
  196.     {  
  197.         for(j=0;j<hidenode;j++)  
  198.         {  
  199.             fprintf(stream0, "%f\n", w[i][j]);  
  200.         }  
  201.     }  
  202.     fclose(stream0);  
  203.   
  204.     //輸出結點權值寫入  
  205.     if(( stream1 = fopen("w1.txt", "w+" ))==NULL)  
  206.     {  
  207.         cout<<"創建文件失敗!";  
  208.         exit(1);  
  209.     }  
  210.     for(i=0;i<hidenode;i++)  
  211.     {  
  212.         for(j=0;j<outnode;j++)  
  213.         {  
  214.             fprintf(stream1, "%f\n",w1[i][j]);  
  215.         }  
  216.     }  
  217.     fclose(stream1);  
  218.   
  219.     //隱含結點閥值寫入  
  220.     if(( stream2 = fopen("b1.txt", "w+" ))==NULL)  
  221.     {  
  222.         cout<<"創建文件失敗!";  
  223.         exit(1);  
  224.     }  
  225.     for(i=0;i<hidenode;i++)  
  226.         fprintf(stream2, "%f\n",b1[i]);  
  227.     fclose(stream2);  
  228.   
  229.     //輸出結點閥值寫入  
  230.     if(( stream3 = fopen("b2.txt", "w+" ))==NULL)  
  231.     {  
  232.         cout<<"創建文件失敗!";  
  233.         exit(1);  
  234.     }  
  235.     for(i=0;i<outnode;i++)  
  236.         fprintf(stream3, "%f\n",b2[i]);  
  237.     fclose(stream3);  
  238.   
  239. }  
  240.   
  241. void BpNet::readtrain()  
  242. {  
  243.     FILE *stream0;  
  244.     FILE *stream1;  
  245.     FILE *stream2;  
  246.     FILE *stream3;  
  247.     int i,j;  
  248.   
  249.     //隱含結點權值讀出  
  250.     if(( stream0 = fopen("w.txt", "r" ))==NULL)  
  251.     {  
  252.         cout<<"打開文件失敗!";  
  253.         exit(1);  
  254.     }  
  255.     float  wx[innode][hidenode];  
  256.     for(i=0;i<innode;i++)  
  257.     {  
  258.         for(j=0;j<hidenode;j++)  
  259.         {  
  260.             fscanf(stream0, "%f", &wx[i][j]);  
  261.             w[i][j]=wx[i][j];  
  262.         }  
  263.     }  
  264.     fclose(stream0);  
  265.   
  266.     //輸出結點權值讀出  
  267.     if(( stream1 = fopen("w1.txt", "r" ))==NULL)  
  268.     {  
  269.         cout<<"打開文件失敗!";  
  270.         exit(1);  
  271.     }  
  272.     float  wx1[hidenode][outnode];  
  273.     for(i=0;i<hidenode;i++)  
  274.     {  
  275.         for(j=0;j<outnode;j++)  
  276.         {  
  277.             fscanf(stream1, "%f", &wx1[i][j]);  
  278.             w1[i][j]=wx1[i][j];  
  279.         }  
  280.     }  
  281.     fclose(stream1);  
  282.   
  283.     //隱含結點閥值讀出  
  284.     if(( stream2 = fopen("b1.txt", "r" ))==NULL)  
  285.     {  
  286.         cout<<"打開文件失敗!";  
  287.         exit(1);  
  288.     }  
  289.     float xb1[hidenode];  
  290.     for(i=0;i<hidenode;i++)  
  291.     {  
  292.         fscanf(stream2, "%f",&xb1[i]);  
  293.         b1[i]=xb1[i];  
  294.     }  
  295.     fclose(stream2);  
  296.   
  297.     //輸出結點閥值讀出  
  298.     if(( stream3 = fopen("b2.txt", "r" ))==NULL)  
  299.     {  
  300.         cout<<"打開文件失敗!";  
  301.         exit(1);  
  302.     }  
  303.     float xb2[outnode];  
  304.     for(i=0;i<outnode;i++)  
  305.     {  
  306.         fscanf(stream3, "%f",&xb2[i]);  
  307.         b2[i]=xb2[i];  
  308.     }  
  309.     fclose(stream3);  
  310. }  
  311.   
  312.   
  313. //輸入樣本  
  314. double X[trainsample][innode]= {  
  315.     {0,0,0},{0,0,1},{0,1,0},{0,1,1},{1,0,0},{1,0,1},{1,1,0},{1,1,1}  
  316.     };  
  317. //期望輸出樣本  
  318. double Y[trainsample][outnode]={  
  319.     {0},{0.1429},{0.2857},{0.4286},{0.5714},{0.7143},{0.8571},{1.0000}  
  320.     };  
  321.   
  322. int main()  
  323. {  
  324.     BpNet bp;  
  325.     bp.init();  
  326.     int times=0;  
  327.     while(bp.error>0.0001)  
  328.     {  
  329.         bp.e=0.0;  
  330.         times++;  
  331.         bp.train(X,Y);  
  332.         cout<<"Times="<<times<<" error="<<bp.error<<endl;  
  333.     }  
  334.     cout<<"trainning complete..."<<endl;  
  335.     double m[innode]={1,1,1};  
  336.     double *r=bp.recognize(m);  
  337.     for(int i=0;i<outnode;++i)  
  338.        cout<<bp.result[i]<<" ";  
  339.     double cha[trainsample][outnode];  
  340.     double mi=100;  
  341.     double index;  
  342.     for(int i=0;i<trainsample;i++)  
  343.     {  
  344.         for(int j=0;j<outnode;j++)  
  345.         {  
  346.             //找差值最小的那個樣本  
  347.             cha[i][j]=(double)(fabs(Y[i][j]-bp.result[j]));  
  348.             if(cha[i][j]<mi)  
  349.             {  
  350.                 mi=cha[i][j];  
  351.                 index=i;  
  352.             }  
  353.         }  
  354.     }  
  355.     for(int i=0;i<innode;++i)  
  356.        cout<<m[i];  
  357.     cout<<" is "<<index<<endl;  
  358.     cout<<endl;  
  359.     return 0;  
  360. }  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM