c++ map底层_卷积神经网络(CNN)Python的底层实现——以LeNet为例

7c8a80cc690a506a7b215dc85ba9c8fc.png

本文介绍了如何使用Python从底层实现一个简单但经典的卷积神经网络结构——LeNet,并用它完成MNIST手写数字识别任务。

具体的完整代码以及代码的使用方法,可以光顾我的Github

ProfessorHuang/Python_LeNet_UnderlyingImplementation​github.com
ae8682680b2f5022d3d6b4e47d89c31e.png

我的代码对算法实现方法比较直观,基本上是直接对着公式翻译过来,使用的也主要是函数式编程,只涉及基础的Python语法以及基础的Numpy使用。优点在于对于新手小白比较友好,缺点在于算法效率略低。


之前我写了三篇文章,分别介绍全连接神经网络反向传播理论,全连接神经网络底层Python实现,以及卷积神经网络的反向传播理论。这三篇文章涉及的知识点主要是为这篇卷积神经网络的实现打基础。避免这篇文章中需要牵扯太多的细节。

南柯一梦宁沉沦:全连接神经网络中反向传播算法数学推导​zhuanlan.zhihu.com
54f0382d79cfde75a9412097498bffcd.png
南柯一梦宁沉沦:用Python从底层实现一个多层感知机​zhuanlan.zhihu.com
54f0382d79cfde75a9412097498bffcd.png
南柯一梦宁沉沦:卷积神经网络(CNN)反向传播算法推导​zhuanlan.zhihu.com
54f0382d79cfde75a9412097498bffcd.png


在这三篇文章中我分别介绍了:

  1. 全连接层反向传播的数学推导,随机梯度下降法进行训练的原理。
  2. MNIST数据集下载以及预处理的方法,全连接层反向传播以及随机梯度下降法的Python实现。
  3. 卷积神经网络中,卷积层以及池化层的反向传播数学推导。

对于上面提到的一些知识点,在这篇文章中会简要带过,如果读者朋友还不太熟悉可以参考我的文章或的别人写的教程。


MNIST数据集预处理:

要进行训练,首先需要准备好数据集,我们使用的仍是从Yann Lecun网站上下载下来的是idx格式的数据。

与之前文章中全连接神经网络对MNIST数据集处理方法略有不一样的是。图片需要存储为一个4维的张量,格式为:图片数×行数×列数×通道数。标签需要存储为一个3维的张量,格式为:标签数×类别数×1.

另外由于LeNet神经网络输入为32×32的图像,而原始的MNIST数据集图片大小为28×28,我们需要将MNIST图片先进行零填充,使它符合LeNet的输入要求。

# 零填充

最终我们得到的数据格式如下:

训练图片:60000×32×32×1,训练标签:60000×10×1

验证图片:10000×32×32×1,验证标签:10000×10×1

LeNet卷积神经网络前向传播:

LeNet包含两个卷积层,两个池化层以及三层全连接层。

输入是32×32×1的图片张量,经过6×5×5×1的卷积核卷积后,图片格式变为28×28×6。再经过最大池化,图片尺寸缩小一半,变为14×14×6。再经过16×5×5×6的卷积核卷积后,图片格式变为10×10×16。再经过一次最大池化,图片再缩小一半,变为5×5×16。

接下来是全连接神经网络了,不过需要先将图片张量拉直,变为400×1的列向量。再分别经过120个结点的隐藏层和84个结点的隐藏层后,输出到输出层的10个结点作为预测结果。

卷积的实现:

第一种实现卷积的方法是依据卷积计算的定义,直接去进行实现。即让卷积核在图片上移动,每移动一次,逐元素相乘再相加,得到输出图片的一个值。

def 

conv函数将一个三维图片张量与一个四维卷积核张量进行卷积操作。就像之前文章说的,卷积神经网络中的卷积实际上是若干次二维卷积操作,在conv函数中调用了conv_函数,conv_函数用于实现二维卷积操作。

def 

尽管上面卷积的实现尽管非常直观,但缺点是计算效率低,在训练神经网络时会消耗大量的时间。为了提高的计算效率,我们可以将三维图片张量转为二维矩阵,四维卷积核张量也转为二维矩阵,将张量卷积操作转化为矩阵的乘法操作,以此大幅提高计算效率。具体转化方法见下图:

595eb52cc0d3842a87cbda7aeda0bc50.png

具体参见贾神的回答:

在 Caffe 中如何计算卷积? - 贾扬清的回答 - 知乎 https://www.zhihu.com/question/28385679/answer/44297845

下面给出第二个版本的卷积代码:

def 

最大池化的实现:

在进行最大池化操作时,不仅需要找出每个区域的最大值,还需要同时记录下最大值在区域中所处的位置,确保之后delta误差反向传播的正确进行。

def 

找最大值用np.max函数,找最大值位置用np.argmax函数

LeNet前向传播实现:

在前向传播中,我们只需要将卷积函数,池化函数,全连接操作进行相应地堆叠即可。只要定义好了基本的模块单元,要实现什么样结构的神经网络都可以。

我们先定义ConvNet类,初始化各层的参数,方便类中其它的方法使用或者修改。

class 

再依据LeNet结构,写出前向传播的函数:

def 

限于篇幅,就不将relu函数和卷积层中add_bias函数的实现代码放出来,直接根据定义来写即可,但需要重点说一下softmax函数:

def 

softmax函数写的时候不能直接按照定义来写,而需要增加一步先对每个元素进行缩放,即减去最大值后,再代入公式,不然一定会发生溢出,从而导致无法训练。

LeNet卷积神经网络反向传播训练

反向传播算法主要涉及各层delta误差的计算以及得到某层delta误差后如何计算该层参数的导数。关于LeNet全连接层部分的实现方法,与我之前全连接神经网络的实现文章中一致,在此不再重复。我们在此主要介绍卷积层以及池化层反向传播的实现。

卷积层中delta误差的反向传播

卷积层delta误差反向传播公式如下:

我看了下我的源代码,没有为这个计算单独写函数,而是直接用一行语句实现的

delta_pool1 

公式与我们写的代码有两点不一样。一是公式为了简洁,忽略了

的零填充,因为
本身尺寸就比
小,为了卷积后与
尺寸一致,需要先进行零填充。 二是公式是针对于二维卷积,我们实际上进行的是张量卷积运算,由于之前写过conv函数,所以可以直接拿过来用。但就像矩阵乘法中需要转置一样,我们需要对卷积核第0维和第3维进行互换。

rot180度本质上将一个二维矩阵上下颠倒一次再左右颠倒,numpy中有对应的函数np.flipud和np.fliplr:

def 

池化层中delta误差的反向传播:

由于我们LeNet中使用的是最大池化,因此反向传播时将delta误差误差放到前向传播时记录的最大位置处,其它点置零即可。

def 

计算卷积核的导数

每对一个通道的输入图片和一个通道的输出图片的delta误差进行二维卷积,我们就得到一个二维卷积核的导数。

我们将原图通道数×卷积结果通道数个二维卷积核的导数重新进行组合成4维张量,即可得到整个卷积核的导数。

def 

计算卷积层偏置项的导数

def 

直接使用np.sum即可将一个通道的delta误差全部相加

使用随机梯度下降法更新参数

这部分与我之前介绍的多层感知机的实现一致,限于篇幅就不放代码了。

即每次从训练数据中取出一个batch数据,再每次从一个batch数据中取出一组数据,用反向传播计算参数导数后,将一个batch的导数相加后用梯度下降法更新参数。

测试我们实现的LeNet

net = ConvNet()
net.SGD(train_image, train_label, 30, 10, 3e-5) 

注意我们的学习率设置为3e-5。之前我训练的时候,正确率总上不去,到处找原因。最后瞎改学习率到1e-5,正确率才上去。之后又尝试了各种参数,最终发现3e-5效果比较好。

093e7cc4ac55311b5e47882f8aab7670.png

每训练一个epoch大概需要50分钟,实现算法效率还有优化的空间。训练到第3个epoch之后,准确度反而下降,有可能是学习率过大导致,也有可能是没有进行正则化导致过拟合。


总结:

本文对经典卷积神经网络LeNet的实现方法进行了介绍,算法主要是根据之前数学的推导直接进行实现,唯一的优化是在卷积的地方使用到了im2col,提高了卷积的计算效率,代码在MNIST数据集上基本正常运行。

但实际上在算法上还有较大的优化空间。在参数的设定,比如学习率,batchsize的选择上,大家也可以多进行尝试。


参考:

[1]卷积神经网络的前向传播主要参考了机器之心

如何使用纯NumPy代码从头实现简单的卷积神经网络​www.jiqizhixin.com
1a7a067b62dd9e0d0228d91030c1cfc8.png

[2]卷积神经网络的实现代码是基于全连接神经网络实现代码

用Python从底层实现一个多层感知机 - 南柯一梦宁沉沦的文章 - 知乎

南柯一梦宁沉沦:用Python从底层实现一个多层感知机​zhuanlan.zhihu.com
54f0382d79cfde75a9412097498bffcd.png

[3]卷积神经网络反向传播理论参考

卷积神经网络(CNN)反向传播算法推导 - 南柯一梦宁沉沦的文章 - 知乎

南柯一梦宁沉沦:卷积神经网络(CNN)反向传播算法推导​zhuanlan.zhihu.com
54f0382d79cfde75a9412097498bffcd.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值