惊落一身雪 发表于 2022-6-25 12:09:49

用Python实现BP神经网络(附代码)

用Python实现BP神经网络(附代码)

 大家好 我是毕加锁(锁!)
今天教大家用Python实现BP神经网络(附代码)
用Python实现出来的机器学习算法都是什么样子呢? 前两期线性回归及逻辑回归项目已发布(见文末链接),今天来讲讲BP神经网络。
BP神经网络
神经网络model
先介绍个三层的神经网络,如下图所示
输入层(input layer)有三个units(
https://img-blog.csdnimg.cn/img_convert/29b0d12986170cfb97704715c5bdac4a.png
为补上的bias,通常设为1)
https://img-blog.csdnimg.cn/img_convert/3ffae4957650567d53541073d6936a2c.png
表示第j层的第i个激励,也称为为单元unit
https://img-blog.csdnimg.cn/img_convert/ed07d5d5c225e74abd5efbbcb5ce31c2.png
为第j层到第j+1层映射的权重矩阵,就是每条边的权重
https://img-blog.csdnimg.cn/img_convert/aa019f3c17180abb710e320beffc4774.png
所以可以得到:
隐含层:
https://img-blog.csdnimg.cn/img_convert/3301cca7053dec691cb433f8803f2e17.png
https://img-blog.csdnimg.cn/img_convert/40ee8b14e3308e5faeffd949d5873a74.png
https://img-blog.csdnimg.cn/img_convert/aeb9d7ba4716921085fc2e41237a94b0.png
输出层
https://img-blog.csdnimg.cn/img_convert/92c3d45ac1ac5125fb95d718bbc16599.png

其中,S型函数
https://img-blog.csdnimg.cn/img_convert/20650ecb09b05290d09bfdf533e99935.png
,也成为激励函数
可以看出
https://img-blog.csdnimg.cn/img_convert/ea018ea75282c8a8981d0cfabdb8cab9.png
为3x4的矩阵,
https://img-blog.csdnimg.cn/img_convert/cf9ff38597047aa45941d4c66899c3af.png
为1x4的矩阵
https://img-blog.csdnimg.cn/img_convert/10759dcc727d4f24f9b9ad491b685e14.png
==》j+1的单元数x(j层的单元数+1)
代价函数
假设最后输出的
https://img-blog.csdnimg.cn/img_convert/ff6b7b0b5bc5532dcd2c799b4d8d56d5.png
,即代表输出层有K个单元
https://img-blog.csdnimg.cn/img_convert/e7a5416fecc5d70d7d2cdd9dded368d5.png

其中,
https://img-blog.csdnimg.cn/img_convert/a015fd0eceec0634025ca15bba6fa8ee.png
代表第i个单元输出与逻辑回归的代价函数
https://img-blog.csdnimg.cn/img_convert/113c7aa54e12665aa1b91c2f922c8fca.png
差不多,就是累加上每个输出(共有K个输出)
正则化
L-->所有层的个数
https://img-blog.csdnimg.cn/img_convert/212f79232081ec9f2daf4970cb02c24d.png
-->第l层unit的个数
正则化后的代价函数为
https://img-blog.csdnimg.cn/img_convert/880ada5e1ca4edca129ad26718f2c3c0.png
https://img-blog.csdnimg.cn/img_convert/7e70cac23b1f12fab841449a575a10f3.png
共有L-1层,然后是累加对应每一层的theta矩阵,注意不包含加上偏置项对应的theta(0)
正则化后的代价函数实现代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 代价函数

def nnCostFunction(nn_params,input_layer_size,hidden_layer_size,num_labels,X,y,Lambda):

length = nn_params.shape # theta的中长度

# 还原theta1和theta2

Theta1 = nn_params.reshape(hidden_layer_size,input_layer_size+1)

Theta2 = nn_params.reshape(num_labels,hidden_layer_size+1)

# np.savetxt("Theta1.csv",Theta1,delimiter=',')

m = X.shape

class_y = np.zeros((m,num_labels)) # 数据的y对应0-9,需要映射为0/1的关系

# 映射y

for i in range(num_labels):

class_y[:,i] = np.int32(y==i).reshape(1,-1) # 注意reshape(1,-1)才可以赋值

'''去掉theta1和theta2的第一列,因为正则化时从1开始'''

Theta1_colCount = Theta1.shape

Theta1_x = Theta1[:,1:Theta1_colCount]

Theta2_colCount = Theta2.shape

Theta2_x = Theta2[:,1:Theta2_colCount]

# 正则化向theta^2

term = np.dot(np.transpose(np.vstack((Theta1_x.reshape(-1,1),Theta2_x.reshape(-1,1)))),np.vstack((Theta1_x.reshape(-1,1),Theta2_x.reshape(-1,1))))

'''正向传播,每次需要补上一列1的偏置bias'''

a1 = np.hstack((np.ones((m,1)),X))

z2 = np.dot(a1,np.transpose(Theta1))

a2 = sigmoid(z2)

a2 = np.hstack((np.ones((m,1)),a2))

z3 = np.dot(a2,np.transpose(Theta2))

h = sigmoid(z3)

'''代价'''

J = -(np.dot(np.transpose(class_y.reshape(-1,1)),np.log(h.reshape(-1,1)))+np.dot(np.transpose(1-class_y.reshape(-1,1)),np.log(1-h.reshape(-1,1)))-Lambda*term/2)/m

return np.ravel(J)
反向传播BP
上面正向传播可以计算得到J(θ),使用梯度下降法还需要求它的梯度
BP反向传播的目的就是求代价函数的梯度
假设4层的神经网络,
https://img-blog.csdnimg.cn/img_convert/8607c179f26baf0b8c27f06a6c074ccb.png
记为-->l层第j个单元的误差
https://img-blog.csdnimg.cn/img_convert/6ff13a5a0804431aed9e721f5907ca22.png
《===》
https://img-blog.csdnimg.cn/img_convert/95d34f396378efb30fedf23d6437bd73.png
(向量化)
https://img-blog.csdnimg.cn/img_convert/33a62a90c66a2894524436ed4d293064.png
https://img-blog.csdnimg.cn/img_convert/f113163d67fad41a6d19645e2f9e3983.png
没有
https://img-blog.csdnimg.cn/img_convert/19b178e60750dd2efa0f0daf990a2309.png
,因为对于输入没有误差
因为S型函数
https://img-blog.csdnimg.cn/img_convert/c9313f85b665200d85842f4da9d2726e.png
的倒数为:
https://img-blog.csdnimg.cn/img_convert/05d850a8a458ed7d376dd41ee6369f6c.png

所以上面的
https://img-blog.csdnimg.cn/img_convert/51955932bb841cc0956dc0b3fabea6c4.png

https://img-blog.csdnimg.cn/img_convert/70f97ec54474d5342020ab9d7cbd3d6f.png
可以在前向传播中计算出来
反向传播计算梯度的过程为:
https://img-blog.csdnimg.cn/img_convert/7998209d034b4298c34ddf108ba4547c.png

https://img-blog.csdnimg.cn/img_convert/36057bd54c1f81e50b46df1a8f119efd.png
是大写的
https://img-blog.csdnimg.cn/img_convert/57c3ef567f19e90691b175a38b6c3e46.png

for i=1-m:-
https://img-blog.csdnimg.cn/img_convert/1f36886ce0b9f9aed0cddd88108df661.png
-正向传播计算
https://img-blog.csdnimg.cn/img_convert/12f78b1678c260675ae511b1657a4efe.png
(l=2,3,4...L)
-反向计算
https://img-blog.csdnimg.cn/img_convert/37d7724d4490954172b976b98a3a4af2.png

https://img-blog.csdnimg.cn/img_convert/9221b725ca023ba6c8ac80f3e0ab33de.png
...
https://img-blog.csdnimg.cn/img_convert/61cadd2f0fbf439419b4e63f1bbccfc0.png

-
https://img-blog.csdnimg.cn/img_convert/b3787483b74a8669f8aa20c845da4585.png
-
https://img-blog.csdnimg.cn/img_convert/123940ed8560b72fbf83bc6798ceca15.png
https://img-blog.csdnimg.cn/img_convert/e67be34686acd8f903cefe14150bc503.png
最后
https://img-blog.csdnimg.cn/img_convert/223cfdf001d3af3b5a052ad28ab4f355.png
,即得到代价函数的梯度
实现代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# 梯度

def nnGradient(nn_params,input_layer_size,hidden_layer_size,num_labels,X,y,Lambda):

length = nn_params.shape

Theta1 = nn_params.reshape(hidden_layer_size,input_layer_size+1)

Theta2 = nn_params.reshape(num_labels,hidden_layer_size+1)

m = X.shape

class_y = np.zeros((m,num_labels)) # 数据的y对应0-9,需要映射为0/1的关系

# 映射y

for i in range(num_labels):

class_y[:,i] = np.int32(y==i).reshape(1,-1) # 注意reshape(1,-1)才可以赋值

'''去掉theta1和theta2的第一列,因为正则化时从1开始'''

Theta1_colCount = Theta1.shape

Theta1_x = Theta1[:,1:Theta1_colCount]

Theta2_colCount = Theta2.shape

Theta2_x = Theta2[:,1:Theta2_colCount]

Theta1_grad = np.zeros((Theta1.shape)) #第一层到第二层的权重

Theta2_grad = np.zeros((Theta2.shape)) #第二层到第三层的权重

Theta1[:,0] = 0;

Theta2[:,0] = 0;

'''正向传播,每次需要补上一列1的偏置bias'''

a1 = np.hstack((np.ones((m,1)),X))

z2 = np.dot(a1,np.transpose(Theta1))

a2 = sigmoid(z2)

a2 = np.hstack((np.ones((m,1)),a2))

z3 = np.dot(a2,np.transpose(Theta2))

h = sigmoid(z3)

'''反向传播,delta为误差,'''

delta3 = np.zeros((m,num_labels))

delta2 = np.zeros((m,hidden_layer_size))

for i in range(m):

delta3 = h-class_y

Theta2_grad = Theta2_grad+np.dot(np.transpose(delta3.reshape(1,-1)),a2.reshape(1,-1))

delta2 = np.dot(delta3.reshape(1,-1),Theta2_x)*sigmoidGradient(z2)

Theta1_grad = Theta1_grad+np.dot(np.transpose(delta2.reshape(1,-1)),a1.reshape(1,-1))

'''梯度'''

grad = (np.vstack((Theta1_grad.reshape(-1,1),Theta2_grad.reshape(-1,1)))+Lambda*np.vstack((Theta1.reshape(-1,1),Theta2.reshape(-1,1))))/m

return np.ravel(grad)
BP可以求梯度的原因
实际是利用了链式求导法则
因为下一层的单元利用上一层的单元作为输入进行计算
大体的推导过程如下,最终我们是想预测函数与已知的y非常接近,求均方差的梯度沿着此梯度方向可使代价函数最小化。可对照上面求梯度的过程。
https://img-blog.csdnimg.cn/img_convert/4c6b0237b3d6be46e0082909f8cf7f52.png
                                             字比较草 勿喷
求误差更详细的推导过程:
https://img-blog.csdnimg.cn/img_convert/b3ba1a912362181c8ab7d889ff4f9e95.png
梯度检查
检查利用BP求的梯度是否正确
利用导数的定义验证:
https://img-blog.csdnimg.cn/img_convert/abc4ac62178e357d8977979ea7bc8527.png
求出来的数值梯度应该与BP求出的梯度非常接近
验证BP正确后就不需要再执行验证梯度的算法了
实现代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 检验梯度是否计算正确

# 检验梯度是否计算正确

def checkGradient(Lambda = 0):

'''构造一个小型的神经网络验证,因为数值法计算梯度很浪费时间,而且验证正确后之后就不再需要验证了'''

input_layer_size = 3

hidden_layer_size = 5

num_labels = 3

m = 5

initial_Theta1 = debugInitializeWeights(input_layer_size,hidden_layer_size);

initial_Theta2 = debugInitializeWeights(hidden_layer_size,num_labels)

X = debugInitializeWeights(input_layer_size-1,m)

y = 1+np.transpose(np.mod(np.arange(1,m+1), num_labels))# 初始化y

y = y.reshape(-1,1)

nn_params = np.vstack((initial_Theta1.reshape(-1,1),initial_Theta2.reshape(-1,1))) #展开theta

'''BP求出梯度'''

grad = nnGradient(nn_params, input_layer_size, hidden_layer_size,

num_labels, X, y, Lambda)

'''使用数值法计算梯度'''

num_grad = np.zeros((nn_params.shape))

step = np.zeros((nn_params.shape))

e = 1e-4

for i in range(nn_params.shape):

step = e

loss1 = nnCostFunction(nn_params-step.reshape(-1,1), input_layer_size, hidden_layer_size,

num_labels, X, y,

Lambda)

loss2 = nnCostFunction(nn_params+step.reshape(-1,1), input_layer_size, hidden_layer_size,

num_labels, X, y,

Lambda)

num_grad = (loss2-loss1)/(2*e)

step=0

# 显示两列比较

res = np.hstack((num_grad.reshape(-1,1),grad.reshape(-1,1)))

print res
权重的随机初始化
神经网络不能像逻辑回归那样初始化theta为0,因为若是每条边的权重都为0,每个神经元都是相同的输出,在反向传播中也会得到同样的梯度,最终只会预测一种结果。
所以应该初始化为接近0的数
实现代码
1
2
3
4
5
6
7
8
9
10
11
# 随机初始化权重theta

def randInitializeWeights(L_in,L_out):

W = np.zeros((L_out,1+L_in)) # 对应theta的权重

epsilon_init = (6.0/(L_out+L_in))**0.5

W = np.random.rand(L_out,1+L_in)*2*epsilon_init-epsilon_init # np.random.rand(L_out,1+L_in)产生L_out*(1+L_in)大小的随机矩阵

return W
预测
正向传播预测结果
实现代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 预测

def predict(Theta1,Theta2,X):

m = X.shape

num_labels = Theta2.shape

#p = np.zeros((m,1))

'''正向传播,预测结果'''

X = np.hstack((np.ones((m,1)),X))

h1 = sigmoid(np.dot(X,np.transpose(Theta1)))

h1 = np.hstack((np.ones((m,1)),h1))

h2 = sigmoid(np.dot(h1,np.transpose(Theta2)))

'''

返回h中每一行最大值所在的列号

- np.max(h, axis=1)返回h中每一行的最大值(是某个数字的最大概率)

- 最后where找到的最大概率所在的列号(列号即是对应的数字)

'''

#np.savetxt("h2.csv",h2,delimiter=',')

p = np.array(np.where(h2 == np.max(h2, axis=1)))

for i in np.arange(1, m):

t = np.array(np.where(h2 == np.max(h2, axis=1)))

p = np.vstack((p,t))

return p
输出结果
梯度检查:
https://img-blog.csdnimg.cn/img_convert/e590dcbcb2a8ff909c99029099cdbc26.png
随机显示100个手写数字
https://img-blog.csdnimg.cn/img_convert/5d9f4bceaa53df1a92c85f6c0443ddc0.png
显示theta1权重
https://img-blog.csdnimg.cn/img_convert/4e1828db142125408378a6a8e99a299a.png
训练集预测准确度
https://img-blog.csdnimg.cn/img_convert/79882dc8e9035004a81abe7f115a360d.png
归一化后训练集预测准确度
https://img-blog.csdnimg.cn/img_convert/b04dcdb9dbc0f7bb072c15f093bca124.png
 我是毕加锁 期待你的关注

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: 用Python实现BP神经网络(附代码)