在上一篇文章中,我们从数学理论对多层感知机的反向传播进行了推导。
南柯一梦宁沉沦:神经网络中反向传播算法数学推导zhuanlan.zhihu.com
这一篇文章中我们将基于上一篇文章最后给出的算法使用Python语言来实现一个多层感知机。
完整代码以及代码的使用方法,可以光顾我的Github
ProfessorHuang/Python_LeNet_UnderlyingImplementationgithub.com
MNIST数据准备
要进行训练,我们第一步需要先准备好训练数据,在这里我们使用经典的MNIST数据集。MNIST数据集的获取有多种方式,存储的格式也各不相同。在这里,我建议直接在MNIST的官网,即Yann Lecun的个人网站上获取。http://yann.lecun.com/exdb/mnist/index.html
我们可以在网站上下载得到四个压缩包,分别是训练图片,训练标签,测试图片和测试标签,解压之后获得四个文件的存储格式为idx。我们需要使用Python的struct库中unpack函数进行解析。然后使用numpy库将数据转换成numpy数组的形式便于我们之后处理。
图片数据解析为一个三维数组(也可称为张量),格式为图片数量×784×1.需要提前将图片数据从0-255的整数转换成0-1的浮点数。标签数据解析为一维数据,只存储了标签值的一个标量。我们需要将它也转换成一个三维数组,格式为标签数量×10×1,每个标签以one-hot格式存储,即一个10维列向量,正确标签为下标的数为1,其它的为0.
train_image维度为60000×784×1,train_label维度为60000×10×1
test_image维度为10000×784×1,test_label维度为10000×10×1
编写神经网络类
初始化神经网络
我们定义一个神经网络类NetWork,将涉及到的函数与神经网络各层参数封装在里面。
在初始化一个神经网络时,我们只需要以列表的形式提供神经网络各层的大小,神经网络各层的权重矩阵以及偏置项便会随机初始化并以二维numpy数组的进行存储,并以列表的形式按顺序存放在self.weights以及self.biases中,可以让该神经网络中其它方法使用与更新。
注意权重矩阵的初始化使用np.random.randn提供标准正态分布的随机数,它有正也有负。而我之前不小心使用了np.random.random提供的是0-1的浮点数,结果导致神经网络无法训练。
神经网络前向传播
神经网络前向传播很简单,取出权重矩阵和偏置项,通过矩阵乘法运算和矩阵相加运算,再经过激活函数即可根据前一层的输出得到当前层的输出,递归运算下去即可得到神经网络最终的输出。
矩阵的乘法用np.dot函数实现。我们使用的是sigmoid函数
作为激活函数。
神经网络反向传播
根据输入列向量x,前向传播出各层激活前的输出
和激活后的输出
为了之后计算delta误差以及损失函数对权重矩阵的导数。
将最后一层输出
与标签列向量y代入到损失函数对
的导数,求得最后一层的delta误差
。
利用公式
可以依次求出每层的delta误差,Hadmard积直接用*符号即可,表示逐元素相乘。
每求出一层的delta误差,便可以很快的带入公式,该层的偏置的导数与delta误差相等,该层权重矩阵的导数等于该层delta误差右乘上上一层激活后输出的转置。
backprop方法只是根据一副图像以及对应的标签求得神经网络参数的导数,而我们使用随机梯度下降法,需要使用一个batch的数据来更新数据。sigmoid_prime是对sigmoid函数的一阶导数:
我们使用方法update_mini _batch来调用backprop方法实现对一个batch的数据进行更新
update_mini_batch方法一次接收一个batch的训练图片和对应的训练标签,根据该batch数据求得的参数的平均导数,使用梯度下降法对神经网络中各层权重矩阵与偏置进行更新。
我们需要使用整个60000张训练数据来对神经网络进行训练,因此我们需要一个更高层的函数SGD,接收训练数据,并将训练数据分成一个个batch,再调用update_mini_batch方法对参数进行更新。
SGD接收的参数中,epochs代表训练轮数,将60000张数据全部训练一遍称为一个epoch。mini_batch_size表示batch大小,即一次使用多少张图片对参数进行更新。eta表示学习率。
验证神经网络准确率
我们在SGD方法中可以看见evaluate方法,它在每训练完一个epoch数据后,使用10000张测试数据来验证我们神经网络的准确率。
验证的方法很简单,依次从验证数据集中取出图片,经过神经网络前向传播,看最终预测值与图片的标签是否一致即可。evaluate方法返回10000张图片中预测正确的数量。
测试我们的神经网络
我们使用两行代码即可对我们的神经网络定义以及训练
我们设置一个三层的神经网络,唯一的一个隐藏层只有30个神经元,可以加快我们的训练速度。我们调用SGD方法,训练30个epoch,batch大小为10,学习率为3.这些参数都是我们可以调整的,但相应地会取得不同的训练效果,不合适的参数有时候会导致训练无法正确进行。
完成30个epoch的训练,在我的电脑上大概只需要3分钟即可,而我们神经网络对MNIST验证集的预测正确率已经可以达到94.85%。bingo!
参考:
[1]刘建平Pinard:深度神经网络(DNN)反向传播算法(BP)
深度神经网络(DNN)反向传播算法(BP) - 刘建平Pinard - 博客园www.cnblogs.com
[2] Neural Networks and Deep Learning by By Michael Nielsen
Neural networks and deep learningneuralnetworksanddeeplearning.com
[3] 孤独暗星: MNIST手写数字数据集的读取,基于python3
https://blog.csdn.net/weixin_40522523/article/details/82823812blog.csdn.net