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("
");
 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("开始训练
");
 95     while(trainNum < 10000)
 96     {
 97         e = 0.0;
 98         trainNum++;
 99         train(trainData, outData);
100         printf("训练第%d次, error=%8.4lf
", trainNum, error);
101     }
102     printf("训练完成

");
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("	%7.4lf	",r[j]);
113         printf("
");
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("

共有%d个检测样本, 正确检测出%d个, 准确率: %7.4lf

",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



 

https://i.cnblogs.com/posts?categoryid=0
原文地址:https://www.cnblogs.com/418ks/p/6053689.html