浅谈BP神经网络模型识别手写字——给排水工程建模课后思考题
把以下三步迭代n次,excel中的例子是迭代50次,最终能得到结果
第一步:先把64个43的矩阵读取到数组中,那个43的矩阵可以np.reshape成一个112的矩阵,所以最终读取到的数组是一个6412的矩阵,每一行就是一张图片的内容,总共有64张图片即64行。
第二步:以本人现有水平,只能先弄一块代码,让64行数据一行一行的来通过这块代码,每通过一行就意味着参数累加一次,最终的结果是累加64次之后的参数,即所有图片输出层的误差之和。
每层的激活函数g(x)=1/(1+e^-x)
单张图片的误差:C=1/2*(g(WX+B)-O)^2
之所以前面加了个1/2,据说是为了求导后不参杂这种累赘,否则2次方求导后前面还带个2,麻烦
这个误差对输出层参数W的偏导:=Xg’(WX+B)(g(WX+B)-O)
这个误差对输出层参数B的偏导:=g’(WX+B)(g(WX+B)-O)
则输出层参数W更新为W=W+aXg’(WX+B)(g(WX+B)-O)
则输出层参数B更新为W=B+ag’(WX+B)(g(WX+B)-O)
其中a为学习率
这个误差对隐藏层参数W‘的偏导:=X’g’(X’W’+B’)Wg’(WX+B)(g(WX+B)-O)
这个误差对隐藏层参数B‘的偏导:=g’(X’W’+B’)Wg’(WX+B)(g(WX+B)-O)
参数更新同上,不再赘述
其中X是13的矩阵,即隐藏层的输出,也即输出层的输入
其中X’是112的向量,就是那个6412矩阵中的一行
O是一个12的矩阵,即图片标签的所有可能,对本题所有图片来说都一样即[0,1],当然你也可以设置成[1,0];如果是识别0~9的话,那矩阵O就是[0,1,2,3,4,5,6,7,8,9]了
这个涉及到矩阵函数求导,看了下也没太看明白,有时候求出的导函数矩阵无法相乘,不过没关系,你可以改变相乘的顺序,也可以在合适的位置插入已有形式的单位矩阵,反正最后凑着让它们能相乘就完事了。当然也有可能是我求错了。
第三步:就这样更新64行之后就完事了,即把第二步放在一个for循环里面即可 for i in range(64):
第四步:以上三步再放进一个for循环中,循环多少次自己定
寒假更新代码
先把刚出炉的结果放上
先试试迭代50次的误差,此时学习率为0.2
感觉误差不小啊,学习率保持不变,试试五百次的,将将就就
保持500次不变,经过多次摸索,找出一个大概比较优的学习率,为0.8
再来看看最终预测结果如何,貌似还不戳(如果是预测0~9,则输出层会输出9个结果,依旧取最小值所在位置索引对应的标签为预测结果)
————————————————————————————
虽说是放寒假再放上代码,不过没想到李老师竟把先交了的名单给发群里了,于是乎有不少同学来要了,只希望在“copy”相关东西的时候手下留情
以下是报告正文
Python翻译“4-NN”BP神经网络(5)
——人工神经网络部分
本人认为此神经网络反向传播过程的核心是矩阵函数求导。
如图1所示,每张图片是由3×4=12个像素组成,共64张图片,故输入层,也即隐藏层的输入X_h是一个64×12的矩阵;
Excel表格中对于每张图片,隐藏层有3个输出,也即输出层的输入h_out或者令其为X,X为64×3的矩阵;
那么由输入层64×12的矩阵到隐藏层64×3的矩阵,中间需要经过一个12×3矩阵的变化,这个矩阵就是隐藏层的参数W_h;
对于每张图片,输出层有两个输出,共64张图片,故输出层out为64×2的矩阵;
同理,隐藏层到输出层直接需要经过输出层参数矩阵W的变化,W为3×2的矩阵。
图1 神经网络各部分矩阵示意图
我们已知的是输入层X_h,隐藏层参数W_h和输出层参数W在最开始为随机生成,之后经过反向传播不断修正,最终得到适合输入层和其真实输出的矩阵。
先构造激活函数,一般sigmod函数即可,g(x)=1/(1+e^(-x) )。
由此可得隐藏层X=g(X_h·W_h+B_h),B_h为最初随机生成的常数项,为3×3的矩阵,式中加号代表为X_h·W_h每一行都加上B_h,下式同。
进一步可知输出层out=g(X·W+B),B为最初生成的常数项,为1×2的矩阵。
误差C=1/2 〖(out-D)〗^2,此式中平方代表矩阵各元素原位平方,不是一般意义上的两矩阵相乘,其中D为真实输出。
当误差C能实际算出后就可以开始修正W、B、W_h、B_h了,令其修正后分别为W’、B’、W_h’、B_h’,则有:
W^’=W-θ dC/dW=W-θ〖out〗W^’ (out-D)=W-θXg’(X·W+B)[g(X·W+B)-D]
B^’=B-θ dC/dB=B-θ〖out〗B^’ (out-D)=B-θg’(X·W+B)[g(X·W+B)-D]
〖W_h〗^’=W_h-θ dC/(dW_h )=W_h-θ〖out〗(W_h)^’ (out-D)
=W_h-θX_hg’(X_h·W_h+B_h)Wg’(X·W+B)[g(X·W+B)-D]
〖B_h〗^’=B_h-θ dC/(dB_h)=B_h-θ〖out〗(B_h)^’ (out-D)=B_h-θg’(X_h·W_h+B_h)Wg’(X·W+B)[g(X·W+B)-D]
在运算的过程中要注意将矩阵适当的转置,以便两矩阵能相乘,其中涉及到复合矩阵函数求导法则。
下面附上本人所写代码及运行效果:
*
```python
```python
```python
```python
```python
```python
import numpy as np
import csv
x=[]#求r[]过程中的中间列表
y=[]#用于储存64张图片的输出层64×2个正确输出
r=[]#用于储存64张图片的64×12个像素值
filename='4-4 NN(误差反向传播法).csv'#文件位置及截图如图2所示
with open(filename,'r',encoding='utf-8')as f:
read=csv.reader(f)
for index,info in enumerate(read):
for j in range(64):
a=1+4*j
b=4+4*j
if index>=4 and index<=7:#读取csv文件中每张图片4~7行的数据
x.append(info[a:b])
if index==8:#读取每张图片第八行的真实值,并以(1-真实值)算得另一个输出值
y.append([info[4+4*j],str(1-int(info[4+4*j]))])
for m in range(64):#整理读取的x[]以形成r[]
q=[]
for n in range(4):
q.append(x[m+64*n])
r.append(q)
r=np.reshape(r,(64,12))
def g(x):#定义sigmoid函数
g=1/(1+np.e**(-x))
return g
def d(x):#定义sigmoid函数的导函数
d=x*(1-x)
return d
y=np.array(y)
r=r.astype(float)
y=y.astype(float)#将r、y列表中的元素转换为浮点数便于后续运算
print(np.shape(r),np.shape(y))#查看矩阵列表r、y的行列数
结果分别为:
接着运行以下代码:
t=500#迭代次数,除开第一代是生成初始参数,共迭代499次,经尝试发现excel中迭代50次还没到头,还能继续下降
s=0.8#学习率, excel中设为0.2,经多次尝试,其效果没有0.8好,学习率设为0.8附近,最终的总误差最小,学习效果最佳
CT=[]#储存各代64张图片的总误差
for m in range(t):
ct=[]#储存当代64张图片的总误差
if m==0:#第一代先生成4个随机参数矩阵
w_h=np.random.random(size=(12,3))#隐藏层参数
w=np.random.random(size=(3,2))#输出层参数常数项
b=np.random.random(size=(1,2))#输出层常数项
b_h=np.random.random(size=(1,3))#隐藏层参数常数项
for k in range(64):#开始后续迭代
h_out=g(np.dot(r[k],w_h))#即输出层的输入
out=g(np.dot(h_out,w))#即输出层
c=np.sum(1/2*(out-y[k])**2)#每代误差之和
dC_W=np.dot(np.reshape(h_out,(3,1)),np.reshape(np.multiply(d(out),(out-y[k])),(1,2)))
dC_B=np.multiply(d(out),(out-y[k]))
dC_Wh=np.dot(np.multiply(np.dot(w,np.reshape(np.multiply(d(out),(out-y[k])),(2,1))),np.reshape(d(h_out),(3,1))),np.reshape(r[k],(1,12)))
dC_Bh=np.multiply(np.dot(w,np.reshape(np.multiply(d(out),(out-y[k])),(2,1))),np.reshape(d(h_out),(3,1)))
if k==0: #这两个if用于将每代每张图片的各参数变化的梯度求和
dCT_W=dC_W.copy()
dCT_B=dC_B.copy()
dCT_Wh=dC_Wh.copy()
dCT_Bh=dC_Bh.copy()
if k>=1:
dCT_W=dCT_W+dC_W
dCT_B=dCT_B+dC_B
dCT_Wh=dCT_Wh+dC_Wh
dCT_Bh=dCT_Bh+dC_Bh
ct.append(c)
w=w-s*dCT_W#修正各层参数
b=b-s*dCT_B
w_h=w_h-s*np.transpose(dCT_Wh)
b_h=b_h-s*dCT_Bh
CT.append(np.sum(ct))
print(w)
print(w_h)
print(b)
print(b_h)#查看迭代结束后各层参数
各参数最终结果分别为:
以下代码展现迭代过程中每一代总误差的变化:
import matplotlib.pyplot as plt
v=0
NUM=[]
for z in range(t):
v=v+1
NUM.append(v)
plt.plot(NUM,CT)
plt.xlabel('The number of iterations')
plt.ylabel('Sum of errors of 64 pictures')
plt.title('Error variation during 500 iterations')
plt.show()
print('迭代过程中总误差最小值为{}'.format(min(CT)))
结果为:
以下代码测试此神经网络训练效果:
r_test=[[0,1,0,1,0,1,1,0,1,1,0,1],[0,1,1,0,1,0,0,1,0,0,1,0]]#用于测试的两张图片的像素
y_test=[[0,1],[1,0]]#第一张图片真值为0,第二张图片真值为1
OUT_test=[]
for e in range(len(r_test)):
h_out_test=g(np.dot(r_test[e],w_h))
out_test=g(np.dot(h_out_test,w))
print('第{}张测试图输出层输出的两个结果为{}'.format(e+1,out_test))#[]内最小值所在的位置即为正确答案,第一个位置代表0,第二个位置代表1
OUT_test.append(np.argmin(out_test))
print("用于测试的两张图预测结果分别是{}和{}".format(OUT_test[0],OUT_test[1]))
结果为:
可见预测效果良好。
本程序所用文件内数据的结构形式如图2所示:
图2 文件位置与内部数据形式
这几个图就在之前的更新里面,word复制进来图没得了~反正每次运行结果是不一样的,因为初始生成的随机矩阵不是一样的。