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