BP神經網絡學習筆記_附源代碼


BP神經網絡基本原理:

誤差逆傳播(back propagation, BP)算法是一種計算單個權值變化引起網絡性能變化的較為簡單的方法。由於BP算法過程包含從輸出節點開始,反向地向第一隱含層(即最接近輸入層的隱含層)傳播由總誤差引起的權值修正,所以稱為“反向傳播”。BP神經網絡是有教師指導訓練方式的多層前饋網絡,其基本思想是:從網絡輸入節點輸入的樣本信號向前傳播,經隱含層節點和輸出層節點處的非線性函數作用后,從輸出節點獲得輸出。若在輸出節點得不到樣本的期望輸出,則建立樣本的網絡輸出與其期望輸出的誤差信號,並將此誤差信號沿原連接路徑逆向傳播,去逐層修改網絡的權值和節點處閾值,這種信號正向傳播與誤差信號逆向傳播修改權值和閾值的過程反復進行,直訓練樣本集的網絡輸出誤差滿足一定精度要求為止。

 

以下是使用C/C++(大多數是C語言代碼,只有數據讀取部分是C++)完成的一個實際案例。

要求:

Iris(鳶尾花)數據集分為訓練集(Iris-train.txt)和測試集(Iris-test.txt),分別含75個樣本,每個集合中每種花各有25個樣本。為了方便訓練,將3類花分別編號為1,2,3 。使用這些數據訓練一個4輸入(分別對應4個特征)、隱含層(10個神經元)、3輸出(分別對應該樣本屬於某一品種的可能性大小)的神經網絡(4*10*3)。

 1 #include <stdio.h>
 2 #include <math.h>
 3 #include <stdlib.h>
 4 #include <time.h>
 5 #include <string>
 6 #include <fstream>
 7 #include <iomanip>
 8 
 9 #define innode 4//輸入層結點數
 10 #define hidenode 10//隱層結點數
 11 #define outnode 3//輸出層結點數
 12 #define trainsample 75//訓練樣本數
 13 #define testsample 75//測試樣本數
 14 
 15 double trainData[trainsample][innode];//輸入樣本
 16 double outData[trainsample][outnode];//輸出樣本
 17 
 18 double testData[testsample][innode];//測試樣本
 19 
 20 double w[innode][hidenode];//輸入層到隱層的權值
 21 double w1[hidenode][outnode];//隱層到輸出層的權值
 22 double b1[hidenode];//隱層閾值
 23 double b2[outnode];//輸出層閾值
 24 
 25 double e=0.0;//誤差計算
 26 double error=1.0;//允許的最大誤差
 27 
 28 double rate_w=0.9;//輸入層到隱層的學習率
 29 double rate_w1=0.9;//隱層到輸出層的學習率
 30 double rate_b1=0.9;//隱層閾值學習率
 31 double rate_b2=0.9;//輸出層閾值學習率
 32 
 33 double result[outnode];//bp輸出  34 
 35 //初始化函數
 36 void init(double w[], int n);  37 //Bp訓練函數
 38 void train(double trainData[trainsample][innode], double label[trainsample][outnode]);  39 //Bp識別 
 40 double *recognize(double *p);  41 //從文件夾讀取數據
 42 void readData(std::string filename, double data[][innode], int x);  43 //數據歸一化處理
 44 void changeData(double data[][innode], int x);  45 
 46 int main()  47 {  48     int i,j;  49     int trainNum=0;//樣本訓練次數
 50     double *r; //測試結果
 51     int count=0;//正確測試結果數
 52     double maxRate = 1.0;//輸出結果中的最大概率  53     //對權值和閾值進行初始化
 54     init((double*)w, innode*hidenode);  55     init((double*)w1, hidenode*outnode);  56  init(b1, hidenode);  57  init(b2, outnode);  58 
 59     //讀取訓練數據
 60     readData("./Iris-train.txt", trainData, trainsample);  61     //對訓練數據進行歸一化處理
 62  changeData(trainData, trainsample);  63 
 64     /*for(i=0; i<trainsample; i++)  65  {  66  printf("%d: ",i+1);  67  for(j=0; j<innode; j++)  68  printf("%5.2lf",trainData[i][j]);  69  printf("\n");  70  }*/
 71     //准備輸出樣本
 72     for(i=0; i<trainsample; i++)  73  {  74         if(i<25)  75  {  76             outData[i][0] = 1;  77             outData[i][1] = 0;  78             outData[i][2] = 0;  79  }  80         else if(i<50)  81  {  82             outData[i][0] = 0;  83             outData[i][1] = 1;  84             outData[i][2] = 0;  85  }  86         else
 87  {  88             outData[i][0] = 0;  89             outData[i][1] = 0;  90             outData[i][2] = 1;  91  }  92  }  93 
 94     printf("開始訓練\n");  95     while(trainNum < 10000)  96  {  97         e = 0.0;  98         trainNum++;  99  train(trainData, outData); 100         printf("訓練第%d次, error=%8.4lf\n", trainNum, error); 101  } 102     printf("訓練完成\n\n"); 103 
104     //讀入測試數據
105     readData("./Iris-test.txt", testData, testsample); 106     //歸一化測試數據
107  changeData(testData, testsample); 108     for(i=0; i<testsample; i++) 109  { 110         r = recognize(testData[i]); 111         for(j=0; j<outnode; j++) 112             printf("\t%7.4lf\t",r[j]); 113         printf("\n"); 114         //判斷檢測結果是否正確
115         if(i<25 && r[0]>r[1] && r[0]>r[2]) 116             count++; 117         if(i>=25 && i<50 && r[1]>r[0] && r[1]>r[2]) 118             count++; 119         if(i>=50 && r[2]>r[0] && r[2]>r[1]) 120             count++; 121  } 122 
123     printf("\n\n共有%d個檢測樣本, 正確檢測出%d個, 准確率: %7.4lf\n\n",testsample, count, (double)count/testsample); 124     system("pause"); 125     return 0; 126 } 127 
128 //初始化函數(0到1之間的數)
129 void init(double w[], int n) 130 { 131     int i; 132     srand((unsigned int)time(NULL)); 133     for(i=0; i<n; i++) 134  { 135         w[i] = 2.0*((double)rand()/RAND_MAX)-1; 136  } 137 } 138 
139 //BP訓練函數
140 void train(double trainData[trainsample][innode], double label[trainsample][outnode]) 141 { 142     double x[innode];//輸入層的個輸入值
143     double yd[outnode];//期望的輸出值
144 
145     double o1[hidenode];//隱層結點激活值
146     double o2[hidenode];//輸出層結點激活值
147     double x1[hidenode];//隱層向輸出層的輸入
148     double x2[outnode];//輸出結點的輸出
149     /********************************************************************* 150  o1: 隱層各結點的激活值等於與該結點相連的各路徑上權值與該路徑上的輸入相乘后全部相加 151  ********************************************************************** 152  x1: 隱層結點的輸出,計算出o1后才能計算x1,等於 1.0/(1.0 + exp-(激活值+該結點的閾值)) 153  *********************************************************************** 154  o2: 輸出層結點的激活值等於與該結點相連的各路徑上的權值與該路徑的輸入相乘后全部相加 155  *********************************************************************** 156  x2: 輸出層結點的輸出,計算出o2后才能計算x2,等於 1.0/(1.0 + exp-(激活值+該結點的閾值)) 157  ***********************************************************************/
158 
159     /*qq計算方式 (期望輸出 - 實際輸出)乘上 實際輸出 乘上 (1-實際輸出) */
160     double qq[outnode];//期望的輸出與實際輸出的偏差
161 
162     double pp[hidenode];//隱含結點校正誤差
163 
164 
165     int issamp; 166     int i,j,k; 167     for(issamp=0; issamp<trainsample; issamp++) 168  { 169         for(i=0; i<innode; i++) 170             x[i] = trainData[issamp][i]; 171 
172         for(i=0; i<outnode; i++) 173             yd[i] = label[issamp][i]; 174 
175         //計算隱層各結點的激活值和隱層的輸出值
176         for(i=0; i<hidenode; i++) 177  { 178             o1[i] = 0.0; 179             for(j=0; j<innode; j++) 180                 o1[i] = o1[i]+w[j][i]*x[j]; 181             x1[i] = 1.0/(1.0+exp(-o1[i]-b1[i])); 182  } 183 
184         //計算輸出層各結點的激活值和輸出值
185         for(i=0; i<outnode; i++) 186  { 187             o2[i] = 0.0; 188             for(j=0; j<hidenode; j++) 189                 o2[i] = o2[i]+w1[j][i]*x1[j]; 190             x2[i] = 1.0/(1.0+exp(-o2[i]-b2[i])); 191  } 192 
193         //得到了x2輸出后接下來就要進行反向傳播了 194 
195         //計算實際輸出與期望輸出的偏差,反向調節隱層到輸出層的路徑上的權值
196         for(i=0; i<outnode; i++) 197  { 198             qq[i] = (yd[i]-x2[i]) * x2[i] * (1-x2[i]); 199             for(j=0; j<hidenode; j++) 200                 w1[j][i] = w1[j][i]+rate_w1*qq[i]*x1[j]; 201  } 202 
203         //繼續反向傳播調整輸出層到隱層的各路徑上的權值
204         for(i=0; i<hidenode; i++) 205  { 206             pp[i] = 0.0; 207             for(j=0; j<outnode; j++) 208                 pp[i] = pp[i]+qq[j]*w1[i][j]; 209             pp[i] = pp[i]*x1[i]*(1.0-x1[i]); 210 
211             for(k=0; k<innode; k++) 212                 w[k][i] = w[k][i] + rate_w*pp[i]*x[k]; 213  } 214 
215         //調整允許的最大誤差
216         for(k=0; k<outnode; k++) 217  { 218             e+=fabs(yd[k]-x2[k])*fabs(yd[k]-x2[k]); //計算均方差 
219  } 220         error=e/2.0; 221 
222         //調整輸出層各結點的閾值
223         for(k=0; k<outnode; k++) 224             b2[k]=b2[k]+rate_b2*qq[k]; 225 
226         //調整隱層各結點的閾值
227         for(j=0; j<hidenode; j++) 228             b1[j]=b1[j]+rate_b1*pp[j]; 229  } 230 } 231 
232 //Bp識別
233 double *recognize(double *p) 234 { 235     double x[innode];//輸入層的個輸入值
236     double o1[hidenode];//隱層結點激活值
237     double o2[hidenode];//輸出層結點激活值
238     double x1[hidenode];//隱層向輸出層的輸入
239     double x2[outnode];//輸出結點的輸出
240 
241     int i,j,k; 242 
243     for(i=0;i<innode;i++) 244         x[i]=p[i]; 245 
246     for(j=0;j<hidenode;j++) 247  { 248         o1[j]=0.0; 249         for(i=0;i<innode;i++) 250             o1[j]=o1[j]+w[i][j]*x[i]; //隱含層各單元激活值 
251         x1[j]=1.0/(1.0+exp(-o1[j]-b1[j])); //隱含層各單元輸出 
252  } 253 
254     for(k=0;k<outnode;k++) 255  { 256         o2[k]=0.0; 257         for(j=0;j<hidenode;j++) 258             o2[k]=o2[k]+w1[j][k]*x1[j];//輸出層各單元激活值 
259         x2[k]=1.0/(1.0+exp(-o2[k]-b2[k]));//輸出層各單元輸出 
260  } 261 
262     for(k=0;k<outnode;k++) 263  { 264         result[k]=x2[k]; 265  } 266     return result; 267 } 268 
269 //從文件夾讀取數據
270 void readData(std::string filename, double data[][innode], int x) 271 { 272     std::ifstream inData(filename, std::ios::in); 273     int i,j; 274     double dataLabel; 275     for(i=0; i<x; i++) 276  { 277         for(j=0; j<innode; j++) 278  { 279             inData >>data[i][j]; 280  } 281         inData >>dataLabel; 282  } 283  inData.close(); 284 } 285 
286 //數據歸一化處理
287 void changeData(double data[][innode], int x) 288 { 289     //歸一化公式:(x-min)/(max-min)
290     double minNum,maxNum; 291     int i,j; 292     minNum = data[0][0]; 293     maxNum = data[0][0]; 294     //找最大最小值
295     for(i=0; i<x; i++) 296  { 297         for(j=0; j<innode; j++) 298  { 299             if(minNum > data[i][j]) 300                 minNum = data[i][j]; 301             if(maxNum < data[i][j]) 302                 maxNum = data[i][j]; 303  } 304  } 305     //歸一化
306     for(i=0; i<x; i++) 307  { 308         for(j=0; j<innode; j++) 309             data[i][j] = (data[i][j]-minNum)/(maxNum-minNum); 310  } 311 }

使用時把以下數據集復制粘貼到txt文件,分別命名為Iris-train.txt, Iris-test.txt
訓練數據:

5.1 3.5 1.4 0.2 0
4.9 3.0 1.4 0.2 0
4.7 3.2 1.3 0.2 0
4.6 3.1 1.5 0.2 0
5.0 3.6 1.4 0.2 0
5.4 3.9 1.7 0.4 0
4.6 3.4 1.4 0.3 0
5.0 3.4 1.5 0.2 0
4.4 2.9 1.4 0.2 0
4.9 3.1 1.5 0.1 0
5.4 3.7 1.5 0.2 0
4.8 3.4 1.6 0.2 0
4.8 3.0 1.4 0.1 0
4.3 3.0 1.1 0.1 0
5.8 4.0 1.2 0.2 0
5.7 4.4 1.5 0.4 0
5.4 3.9 1.3 0.4 0
5.1 3.5 1.4 0.3 0
5.7 3.8 1.7 0.3 0
5.1 3.8 1.5 0.3 0
5.4 3.4 1.7 0.2 0
5.1 3.7 1.5 0.4 0
4.6 3.6 1.0 0.2 0
5.1 3.3 1.7 0.5 0
4.8 3.4 1.9 0.2 0
7.0 3.2 4.7 1.4 1
6.4 3.2 4.5 1.5 1
6.9 3.1 4.9 1.5 1
5.5 2.3 4.0 1.3 1
6.5 2.8 4.6 1.5 1
5.7 2.8 4.5 1.3 1
6.3 3.3 4.7 1.6 1
4.9 2.4 3.3 1.0 1
6.6 2.9 4.6 1.3 1
5.2 2.7 3.9 1.4 1
5.0 2.0 3.5 1.0 1
5.9 3.0 4.2 1.5 1
6.0 2.2 4.0 1.0 1
6.1 2.9 4.7 1.4 1
5.6 2.9 3.6 1.3 1
6.7 3.1 4.4 1.4 1
5.6 3.0 4.5 1.5 1
5.8 2.7 4.1 1.0 1
6.2 2.2 4.5 1.5 1
5.6 2.5 3.9 1.1 1
5.9 3.2 4.8 1.8 1
6.1 2.8 4.0 1.3 1
6.3 2.5 4.9 1.5 1
6.1 2.8 4.7 1.2 1
6.4 2.9 4.3 1.3 1
6.3 3.3 6.0 2.5 2
5.8 2.7 5.1 1.9 2
7.1 3.0 5.9 2.1 2
6.3 2.9 5.6 1.8 2
6.5 3.0 5.8 2.2 2
7.6 3.0 6.6 2.1 2
4.9 2.5 4.5 1.7 2
7.3 2.9 6.3 1.8 2
6.7 2.5 5.8 1.8 2
7.2 3.6 6.1 2.5 2
6.5 3.2 5.1 2.0 2
6.4 2.7 5.3 1.9 2
6.8 3.0 5.5 2.1 2
5.7 2.5 5.0 2.0 2
5.8 2.8 5.1 2.4 2
6.4 3.2 5.3 2.3 2
6.5 3.0 5.5 1.8 2
7.7 3.8 6.7 2.2 2
7.7 2.6 6.9 2.3 2
6.0 2.2 5.0 1.5 2
6.9 3.2 5.7 2.3 2
5.6 2.8 4.9 2.0 2
7.7 2.8 6.7 2.0 2
6.3 2.7 4.9 1.8 2
6.7 3.3 5.7 2.1 2

 

測試數據:

5.0 3.0 1.6 0.2 0
5.0 3.4 1.6 0.4 0
5.2 3.5 1.5 0.2 0
5.2 3.4 1.4 0.2 0
4.7 3.2 1.6 0.2 0
4.8 3.1 1.6 0.2 0
5.4 3.4 1.5 0.4 0
5.2 4.1 1.5 0.1 0
5.5 4.2 1.4 0.2 0
4.9 3.1 1.5 0.1 0
5.0 3.2 1.2 0.2 0
5.5 3.5 1.3 0.2 0
4.9 3.1 1.5 0.1 0
4.4 3.0 1.3 0.2 0
5.1 3.4 1.5 0.2 0
5.0 3.5 1.3 0.3 0
4.5 2.3 1.3 0.3 0
4.4 3.2 1.3 0.2 0
5.0 3.5 1.6 0.6 0
5.1 3.8 1.9 0.4 0
4.8 3.0 1.4 0.3 0
5.1 3.8 1.6 0.2 0
4.6 3.2 1.4 0.2 0
5.3 3.7 1.5 0.2 0
5.0 3.3 1.4 0.2 0
6.6 3.0 4.4 1.4 1
6.8 2.8 4.8 1.4 1
6.7 3.0 5.0 1.7 1
6.0 2.9 4.5 1.5 1
5.7 2.6 3.5 1.0 1
5.5 2.4 3.8 1.1 1
5.5 2.4 3.7 1.0 1
5.8 2.7 3.9 1.2 1
6.0 2.7 5.1 1.6 1
5.4 3.0 4.5 1.5 1
6.0 3.4 4.5 1.6 1
6.7 3.1 4.7 1.5 1
6.3 2.3 4.4 1.3 1
5.6 3.0 4.1 1.3 1
5.5 2.5 4.0 1.3 1
5.5 2.6 4.4 1.2 1
6.1 3.0 4.6 1.4 1
5.8 2.6 4.0 1.2 1
5.0 2.3 3.3 1.0 1
5.6 2.7 4.2 1.3 1
5.7 3.0 4.2 1.2 1
5.7 2.9 4.2 1.3 1
6.2 2.9 4.3 1.3 1
5.1 2.5 3.0 1.1 1
5.7 2.8 4.1 1.3 1
7.2 3.2 6.0 1.8 2
6.2 2.8 4.8 1.8 2
6.1 3.0 4.9 1.8 2
6.4 2.8 5.6 2.1 2
7.2 3.0 5.8 1.6 2
7.4 2.8 6.1 1.9 2
7.9 3.8 6.4 2.0 2
6.4 2.8 5.6 2.2 2
6.3 2.8 5.1 1.5 2
6.1 2.6 5.6 1.4 2
7.7 3.0 6.1 2.3 2
6.3 3.4 5.6 2.4 2
6.4 3.1 5.5 1.8 2
6.0 3.0 4.8 1.8 2
6.9 3.1 5.4 2.1 2
6.7 3.1 5.6 2.4 2
6.9 3.1 5.1 2.3 2
5.8 2.7 5.1 1.9 2
6.8 3.2 5.9 2.3 2
6.7 3.3 5.7 2.5 2
6.7 3.0 5.2 2.3 2
6.3 2.5 5.0 1.9 2
6.5 3.0 5.2 2.0 2
6.2 3.4 5.4 2.3 2
5.9 3.0 5.1 1.8 2



 


免責聲明!

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



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