BP神经网络比较简单,其核心就是链式法则.这里分为两个篇幅去记录"BP神经网络公式推导"和"BP神经网络代码实现"....这里是第二篇!
第一点:最简单的神经网络模型
x: 输入参数
a0: 输入偏置
a1: 输入权重
b0: 隐藏层偏置
b1: 隐藏层输入权重
y: 隐藏层输入参数(经过激励函数的输出,下面会讲解激励函数)
Z: 隐藏层输出(经过激励的输出)
各个参数的展开式如下:不用介绍了,都很简单的函数,不懂得话去百度一些模型看看
激励函数:经过sigmoid()函数之后的数值,这是常见的分类函数。
如果是用于函数逼近,使用Y = X 作为激励函数即可。
第二点:公式简介
首先明确梯度下降法:字面的理解是沿着切线的方向,可以更快更准确的找出最小值
我们使用这个规则只需要知道,梯度下降=偏导数,其中
偏导数=k*变化量 抓住这两个点,这是BP神经网络的核心之一。
例如:比例系数,W:权值,E:误差
明白Delta学习规则:有监督的学习,是利用误差作为判断的标准。
形式:y:隐藏层的输出
Z:实际输出,t:期望输出(实际值)
基于这个学习规则进行BP的反向调整权重,核心之二
结合上面两个公式:Delta规则的误差作为标准,误差利用整体的平方可以减少误差波动的影响。下面为了计算方便多乘以一个1/2,求导之后消去,
没意义只是为了计算方便:BP神经网络的权值和偏置调整就可以利用这
个公式,例如:神经网络的权值和偏置计算:
� 因为误差E和隐藏层偏置没有直接关系,
所以需要链式求偏导,返回去看一下模型的参数定义就知道了。
‚ 因为误差E和输入层的输入数据没有直接联系,所以需要链式求偏导
,很简单的链式求导,看一下高数就知道的。
剩下的公式大家自己推导吧。。。。这是核心之三
第三点:简单改进
1.批量学习:顾名思义批量学习就是梯度成批量的计算,总梯度的含义
例如:批量数是10个样本,那就是W1的梯度是十个样本加起来。和一个一个
的计算是一样的,这里不过是选几个样本加起来求平均数而已。
2.遍历学习(在线):本文中使用的改进就是动量法,当前权值的更新需要上一次的和这一次
同时作用,达到平滑高效的作用。公式:注意这里的是没有叠加的,因为上面w存在。是有叠加的,因为是所有的w项。核心之四
第四点:程序设计
核心算法就是更新权值w和更新偏置b,参考了网上程序,但是基本都是有漏洞,可能没下载到好的程序吧,结合了部分网上历程还有一本书《神经网络在应用科学和工程中的应用-----从基本原理到复杂的模式识别》。我弄了一个星期,就是由于公式编写成代码出现很多问题,多细心一点可以减少很多未知的错误!!!
注意:看了Ng的视频,Sigmoid函数<tanh函数<ReLu函数<Leak ReLu函数
CPP文件:
1 #include "BP.h"
2
3 BP_network::BP_network()
4 {
5 srand((unsigned)time(NULL));
6 memset(w, 0, sizeof(w));
7 memset(b, 0, sizeof(b));
8 memset(Yout, 0, sizeof(Yout));
9 memset(DataIn, 0, sizeof(DataIn));
10 memset(DataOut, 0, sizeof(DataOut));
11 memset(dv, 0, sizeof(dv));
12 memset(dw, 0, sizeof(dw));
13 }
14
15 BP_network::~BP_network()
16 {
17
18 }
19
20 double BP_network::Random()
21 {
22 double Rand;
23 Rand = (1.0*rand()) / (RAND_MAX + 1);
24 return Rand;
25 }
26 //--------------写入数据-------------------//
27 void BP_network::FwriteData()
28 {
29 FILE *fp1, *fp2;
30 double data1 = 0.0, data2;
31 if ((fp1 = fopen("G:\\in.txt", "w")) == NULL) printf("can't open in.txt");
32 if ((fp2 = fopen("G:\\out.txt", "w")) == NULL) printf("can't open out.txt");
33 for (int i = 0; i < SampleCount; i++)
34 {
35 //data1 = Random() % 1000 / 100;
36 data1 = double(int(Random() * 1000) % 1000) / 100;
37 data2 = double(int(Random() * 1000) % 1000) / 100;
38 fprintf(fp1,"%lf %lf\n", data1, data2);
39 data1 = data1 * data2;
40 fprintf(fp2,"%lf \n", data1);
41 }
42 fclose(fp1);
43 fclose(fp2);
44 }
45 //--------------读入数据-------------------//
46 void BP_network::FreadData()
47 {
48 FILE *fp1, *fp2;
49 if (((fp1 = fopen("G:\\in.txt", "r")) == NULL))
50 printf("can't open in.txt");
51 if((fp2 = fopen("G:\\out.txt", "r")) == NULL)
52 printf("can't open in.txt");;
53 for (int i = 0; i < SampleCount; i++)
54 {
55 for (int j = 0; j < InCount; j++)
56 {
57 fscanf(fp1, "%lf", &DataIn[i][j]);
58 }
59 fscanf(fp2, "%lf", &DataOut[i][0]);
60 }
61 fclose(fp1);
62 fclose(fp2);
63 }
64 //---------归一化输入和输出的数值,随机化权值和偏置的值---------//
65 //**************** 对于隐藏层->输出层=w[i][j][k] *************//
66 //------------------ i:层数,j:输出层个数,k:隐藏层个数 ------//
67 void BP_network::Init_network()
68 {
69 double InMax, InMin, OutMax, OutMin;
70 InMax = DataIn[0][0];
71 InMin = DataIn[0][0];
72 OutMax = DataOut[0][0];
73 OutMin = DataOut[0][0];
74
75 for (int i = 0; i < SampleCount; i++)
76 {
77 for (int j = 0; j < InCount; j++)
78 {
79 InMax = InMax > DataIn[i][j] ? InMax : DataIn[i][j];
80 InMin = InMin < DataIn[i][j] ? InMin : DataIn[i][j];
81 }
82 OutMax = OutMax > DataOut[i][0] ? OutMax : DataOut[i][0];
83 OutMin = OutMin < DataOut[i][0] ? OutMin : DataOut[i][0];
84 }
85 for (int i = 0; i < SampleCount; i++)
86 {
87 for (int j = 0; j < InCount; j++)
88 {
89 DataIn[i][j] = (DataIn[i][j] - InMin + 1) / (InMax - InMin + 1);
90 }
91 DataOut[i][0] = (DataOut[i][0] - OutMin + 1) / (OutMax - OutMin + 1);
92 }
93 for (int j = 0; j < HiddenCount; j++)
94 {
95 //----------随机化W和B的值------------//
96 for (int i = 0; i < LayerCount - 1; i++)
97 {
98 if (i == 0)//第一层
99 {
100 for (int k = 0; k < InCount; k++) w[i][k][j] = 2 * (0.5 - Random());
101 }
102 if (i == 1)//第二层
103 {
104 w[i][j][0] = 2 * (0.5 - Random());
105 }
106 }
107 b[0][j] = 1;//由于数比较少,不需要做循环,直接赋值
108 b[1][0] = 1;
109 }
110 inMax = InMax;
111 inMin = InMin;
112 outMax = OutMax;
113 outMin = OutMin;
114 }
115 //---------激励函数输出----------//
116 void BP_network::DrivingOut(int m)
117 {
118 Yout[1][0] = 0.0;//!!!!!!!!!!!!!注释:未能清除数据,导致检查两天!!!!!!!!!!!!!!!!!!!!!
119 for (int j = 0; j < HiddenCount; j++)
120 {
121 Yout[0][j] = 0.0;
122 for (int k = 0; k < 2; k++)
123 {
124 Yout[0][j] += DataIn[m][k] * w[0][k][j];
125 }
126 Yout[0][j] += b[0][j];
127 Yout[0][j] = Sigmoid(Yout[0][j]);
128 Yout[1][0] += Yout[0][j] * w[1][j][0];
129 }
130 Yout[1][0] += b[1][0];
131 Yout[1][0] = Sigmoid(Yout[1][0]);
132 }
133 //-----------对W和B进行调整---------------//
134 void BP_network::BackAdjust(int m)
135 {
136 #if 1//加动量法
137 double StudyRate = 0.0, f = 0.0;
138 double Sum = 0.0;
139 StudyRate = (Yout[1][0] - DataOut[m][0]) * Yout[1][0] * (1 - Yout[1][0]);
140 db0[1][0] = U1*db0[1][0] + (1 - U1)*B_Rate*StudyRate;
141 b[1][0] -= db0[1][0];
142 for (int i = 0; i < HiddenCount; i++)
143 {
144 dv[0][i] = U1*dv[0][i] + (1 - U1)*W_Rate * StudyRate * Yout[0][i];
145 w[1][i][0] -= dv[0][i];
146 }
147 for (int i = 0; i < HiddenCount; i++)
148 {
149 f = 0.0;
150 f = StudyRate * w[1][i][0] * Yout[0][i] * (1 - Yout[0][i]);//改正之后
151 for (int j = 0; j < InCount; j++)
152 {
153 dw[j][i] = U1*dw[j][i] + (1 - U1)*W_Rate * f * DataIn[m][j];
154 w[0][j][i] -= dw[j][i];
155 }
156 db1[0][i] = U1*db0[0][i] + (1 - U1)*B_Rate*f;
157 b[0][i] -= db1[0][i];
158 }
159 #endif
160 #if 0//网上找到的加了一点动量,但是程序不稳定
161 int i, j;
162 double t;
163 for (i = 0; i < HiddenCount; ++i)
164 {
165 t = 0;
166 for (j = 0; j < OutCount; ++j) {
167 t += (Yout[1][j] - DataOut[m][j])*w[1][i][j];
168 dv[i][j] = U1*dv[i][j] + (1-U1)*W_Rate*(Yout[1][j] - DataOut[m][j])*Yout[0][i];
169 w[1][i][j] -= dv[i][j];
170 }
171 for (j = 0; j < InCount; ++j) {
172 dw[j][i] = U1*dw[j][i] + (1 - U1)*W_Rate*t*Yout[0][i] * (1 - Yout[0][i])*DataIn[m][j];
173 w[0][j][i] -= dw[j][i];
174 }
175 }
176 #endif
177 #if 0//未加动量法
178 double StudyRate = 0.0, f = 0.0;
179 double Sum = 0.0;
180 StudyRate = ( Yout[1][0] - DataOut[m][0]) * Yout[1][0] * (1 - Yout[1][0]);
181 b[1][0] -= ETA_W*StudyRate;
182 for (int i = 0; i < HiddenCount; i++)
183 w[1][i][0] -= W_Rate * StudyRate * Yout[0][i];
184 for (int i = 0; i < HiddenCount; i++)
185 {
186 f = 0.0;
187 f = StudyRate * w[1][i][0] * Yout[0][i] * (1 - Yout[0][i]);//改正之后
188 for (int j = 0; j < InCount; j++)
189 {
190 w[0][j][i] -= W_Rate* f *DataIn[m][j];
191 }
192 b[0][i] -= ETA_B * f;
193 }
194 #endif
195 }
196 //-----------训练神经网络-----------------//
197 void BP_network::Train_network()
198 {
199 int Tcount = 0;
200 double e = 1.0;
201 while (Tcount<TrainCount && e>Accuracy)
202 {
203 e = 0;
204 for (int i = 0; i < SampleCount; i++)
205 {
206 DrivingOut(i);
207 e += fabs((Yout[1][0]-DataOut[i][0])/ DataOut[i][0]);
208 BackAdjust(i);
209 }
210 Tcount++;
211 e = e / SampleCount;
212 cout << "第" << Tcount << "代精度为:" << e << endl;
213 }
214 }
215
216 double BP_network::Calculate(double x, double y)
217 {
218 x = (x - inMin + 1) / (inMax - inMin + 1);
219 y = (y - inMin + 1) / (inMax - inMin + 1);
220 #if true
221 double sum = 0.0, Tout = 0.0;
222 for (int i = 0; i < HiddenCount; i++)
223 {
224 Yout[0][i] = w[0][0][i] * x + w[0][1][i] * y + b[0][i];
225 Yout[0][i] = Sigmoid(Yout[0][i]);
226 Tout += Yout[0][i] * w[1][i][0];
227 }
228 Tout += b[1][0];
229 Tout = Sigmoid(Tout);
230 return (Tout * (outMax - outMin + 1) + outMin - 1);
231 #endif
232 }
233
234 double BP_network::Sigmoid(double t)
235 {
236 double sum;
237 sum = 1.0 / (1.0+exp(-t));
238 return sum;
239 }
H文件:
1 #pragma once
2 #define _CRT_SECURE_NO_WARNINGS
3
4 #include <iostream>
5 #include <vector>
6 #include "time.h"
7 #include "math.h"
8
9 using namespace std;
10
11 const int SampleCount = 820;//定义样本个数
12 const int InCount = 2;//输入神经元个数
13 const int OutCount = 1;//输出神经元个数
14 const int HiddenCount = 45;//隐藏层神经元个数
15 const int TrainCount = 6000;//训练次数
16 const int LayerCount = 3;//神经网络层数
17 const int NeuronMax = 45;//每层最多的神经元个数(也可以直接定义个数)
18
19 const double U0 = 0.15;
20 const double U1 = 0.0;//0.15
21 const double W_Rate = 0.8;//权值调整率//0.8
22 const double B_Rate = 0.8;//阈值调整率
23 const double Accuracy = 0.01;
24
25 #define ETA_W 0.2
26 #define ETA_B 0.1
27
28 class BP_network
29 {
30 public:
31 BP_network();
32 ~BP_network();
33 void FwriteData();
34 void FreadData();
35 void Init_network();
36 void Train_network();
37 double Calculate(double x, double y);
38 private:
39 double DataIn[SampleCount][InCount];//输入的数据
40 double DataOut[SampleCount][OutCount];//输出的数据
41 double w[LayerCount-1][HiddenCount][HiddenCount];//权值(定义的是最大范围,为了移植方便,用不完)
42 double b[LayerCount-1][NeuronMax];//偏置(第几层,第几个神经元,对应的下一的神经元)
43 double Yout[LayerCount-1][NeuronMax];//经过激励函数的输出
44 double inMax, outMax, inMin, outMin;
45 double dv[HiddenCount][HiddenCount], dw[HiddenCount][HiddenCount];
46 double db0[2][2], db1[HiddenCount][HiddenCount];
47
48
49 double Sigmoid(double t);
50 void DrivingOut(int i);
51 void BackAdjust(int t);
52 double Random();
53 };
main文件:
1 #include "BP.h"
2
3 int main(int argc, char*argv)
4 {
5 BP_network test;
6 test.FwriteData();
7 test.FreadData();
8 test.Init_network();
9 test.Train_network();
10 cout << test.Calculate(5,5);
11 while (1);return 0;
12 }
不训练直接使用文件:
注:这是没有经过测试,可能程序有点小Bug,主题思路是没问题的!
1 //----@InData: 输入为一维的数据
2 //----@Weight: 权重
3 // [] :层数,
4 // [0]: 输入层到隐藏层的权重
5 // [1]: 隐藏层到输出层的权重
6 // [][] :每层个数
7 // [0][i]: 输入层的个数
8 // [1][h]: 隐藏层个数
9 // [][][] :每层个数
10 // [0][i][h]:隐藏层个数
11 // [1][h][o]:输出层个数
12 //----@Bias: 偏置
13 // [0][h] :隐藏层偏置
14 // [1][o] :输出层偏置
15 //----@OutData: 输出为一维数据
16 void Calculate(double* InData,double*** Weight,double* Bias ,char* OutData)
17 {
18 double HidData[HiddenCount][OutCount],HidData_Temp=0;//HidData:存放临时隐藏层到输出层的数据,HidData_Temp:存放临时输入层到隐藏层的单个数据
19 unsigned char flag = 1;//加输出层偏置
20 for (char h = 0; h < HiddenCount; h++)
21 {
22 for(unsigned char i=0; i<sizeof(InData)/sizeof(double); i++)//单个隐藏层的输出
23 {
24 HidData_Temp += Weight[0][i][h] *InData[i];
25 }
26 HidData_Temp += Bias[0][h];
27 HidData_Temp = Sigmoid(HidData_Temp);
28 for(unsigned char o=0; o<sizeof(OutData)/sizeof(char); o++)//单个隐藏层对输出层的输出
29 {
30 HidData[h][o] = Weight[1][h][o]*HidData_Temp;
31 OutData[o] += HidData[h][o];
32 //OutData[o] = flag==1?(OutData[o]+Bias[1][o]):OutData[o];
33 if(flag) OutData[o]+=Bias[1][o];//只能加一次偏置,顾用标志位
34 }
35 flag=0;
36 }
37 }
38 //----@InData: 输入为一维的数据
39 //----@MinMax: 数据最值
40 // [0] :Maximun data
41 // [1] :Minimun data
42 //----@OutData: 输出为一维数据
43 void Normlize(double* InDta,double* MinMax,double* OutData)
44 {
45 for(unsigned char i=0;i<sizeof(InData)/sizeof(double);i++)
46 {
47 OutData[i] = (InDta[i] - MinMax[0] + 1) / (MinMax[1] - MinMax[0] + 1);
48 }
49 }
50 //----Sigmoid函数
51 double Sigmoid(double t)
52 {
53 double sum;
54 sum = 1.0 / (1.0+exp(-t));
55 return sum;
56 }