Lenet-5卷积神经网络结构详解(一)——原理解析
Lenet-5卷积神经网络结构详解(一)——原理解析
前言
本系列博文将阐释深度学习网络Lenet-5的具体原理,网络结构及其代码实现。本文不适用任何高级框架,只使用numpy从底层实现lenet-5。本系列博文共计3篇,本文是第一篇。系列中代码参考地址:
Click Here.
Yes Right Here
网络综述
Lenet-5 神经网络出自论文 Gradient-Based Learning Applied to Document Recognition,是一种用于手写体字符识别的非常高效的卷积神经网络。Lenet-5 神经网络一共有 7 层,每层包含不同数量的训练参数。将一批数据输入进神经网络,经过卷积,**,池化,全连接和Softmax 回归等操作,最终返回一个概率数组,从而达到识别图片的目的。
配置环境
python 3.7.3
numpy 1.16.4
数据集
在我们直视整个网络前,我们先来看一下网络要处理的数据集。
由于网络处理手写字符比较高效,我们选用知名度很高的Minst数据集,下载地址:download minst dataset
上述的下载网址中说明了数据集的构成:60000张手写体数字图片,每张图片的大小为28x28。下载网址中详细说明了压缩文档的构成,我们可以利用这些信息编写函数将数据集下载下来。
下载
我们编写load_data.py实现从网址下载函数的功能。需要的模块有gizp 和 pickle,没有的同学请自行pip。
import gzip
import pickle
import numpy as np
file_name = [
["x_train_set","train-images-idx3-ubyte.gz"],
["x_test_set","t10k-images-idx3-ubyte.gz"],
["y_train_set","train-labels-idx1-ubyte.gz"],
["y_test_set","t10k-labels-idx1-ubyte.gz"]
] # 文件名,下载地址: http://yann.lecun.com/exdb/mnist/
"""
存储数据到dataset_minst.pkl文件夹。
"""
def save_dataset ():
dataset ={}
for name in file_name[:2]:
with gzip.open(name[1],"rb") as f:
dataset[name[0]] = np.frombuffer(f.read(),np.uint8,offset = 16).reshape(-1,28*28)# 为什么要offset16?因为前16个字符是
for name in file_name[-2:]:
with gzip.open(name[1],"rb") as f:
dataset[name[0]] = np.frombuffer(f.read(),np.uint8,offset = 8)
with open("dataset_mnist.pkl","wb") as f:
pickle.dump(dataset,f)
print("save complete")
def load_data():
with open("dataset_mnist.pkl","rb") as f:
dataset = pickle.load(f)
return dataset["x_train_set"],dataset["x_test_set"],dataset["y_train_set"],dataset["y_test_set"]
上述代码提供两个功能函数:save_data()和load_data()。save_data()将数据集下载,解压并储存在dataset_mnist.pkl文档里,而load_data()负责将pkl文件里的数据加载到程序中。关于代码中的np.formbuffer里offset参数设置的原因请参照下图:
下载完成后,我们在程序中看一下其前16张图片是什么样子的。需要使用到绘图库matplotlib.pyplot
import matplotlib.pyplot as plt # 画图库
from load_data import save_dataset,load_data # 加载数据集的
save_dataset()
x_train_set,x_test_set,y_train_set,y_test_set = load_data()
# 查看前16张图片
for i in range(16):
A = x_train_set[i].reshape(28,28)
Y = y_train_set[i]
plt.subplot(4,4,1+i)
plt.axis("off")
plt.imshow(A,cmap=plt.cm.gray)
输出应该是这个样子的:
网络详解
网络结构
Lenet-5是一个七层网络,两层卷积,两层池化,三层全连接,输出数据用softmax进行处理。其架构如下图所示:
网络是这样预测单张图片的:
首先,我们将图片归一化下采样为28×28,之后进行卷积操作,将一张28×28的图片卷积成6张24×24的图片。在这一步中,图片的大小减少了,但深度增加了,直观理解就是“特征”更强了。之后进行最大池化,过滤掉相对不重要的像素,得到6张12×12的图片。在进行一遍相似的卷积和池化操作,最终得到16张4×4的图片。将这16张图片拉成一个向量,进行全连接层操作,得到120->84->10的向量,再经过softmax层进行预测,得到预测结果。
我们要训练的参数是卷积核。每一层的卷积核都是5X5的,通过对卷积核的参数值进行学习,我们最终会获得一组合适的卷积核,用它们进行卷积可以让网络的预测准确值稳定在98%左右。
训练流程
在目前的神经网络中,参数的更新按照如下算法迭代:
- 初始化卷积核和偏置
- 利用初始化的参数前向传播
- 计算损失
- 反向传播,更新参数
神经网络就是这样不断地进行前向传播,反向传播,更新参数,最终学习到合适的卷积核参数。当然,在每一步都有很多细节值得探讨,接下来我们将按照网络层的顺序进行讨论。
卷积层细节
要想明白卷积层在做什么,我们首先要明白卷积的概念。
卷积,一种特殊的线性运算。用这种运算代替传统的矩阵运算,会在数据成网格状(比如图片)时表现得更为出色。卷积,形象地说,就是用卷积核在图像上面框,框到地部分进行矩阵相乘运算。运算结束后,将卷积核按照步长滑动,继续进行卷积运算。
卷积层就在进行这样地运算。得益于python的灵活的切片,我们可以进行批处理,也不用写很复杂的代码实现卷积核的移动。
卷积计算的公式十分简单:
需要注意的是其中进行的都是矩阵运算。
但是,为什么我们要使用卷积层进行该操作?卷积层复杂化了整个运算过程,而直接使用全连接层会大大提高运算效率。一个简单的三层全连接层网络训练速度很快,并且准确率也不低。对于这个问题,我的理解是:卷积操作可以识别到图像特征的相对位置而不是绝对位置。图片会发生平移,旋转,而卷积层拥有平移不变性和旋转不变性。卷积层可以处理的图像更为特殊,学习到的特征更为稳定,准确率的上限更高。下面的gif显示了卷积层学习到的特征图的旋转不变性。
Click here to see an amazing gif
**层细节
在进行完每一层的运算后,都要通过**函数。**函数的作用是让网络有能力处理非线性的分类关系。因为不管是卷积运算还是全连接层,其本质都是在进行向量或矩阵的相乘,是线性的。
本例中使用的ReLu**函数,其作用是过滤掉小于零的数,让神经网络不再线性。其函数图像及表达式如下:
池化层细节
池化层的工作是剔除掉不重要的像素。不进行池化操作,运算量就会过大,拖慢训练进度。通过池化,我们可以过滤掉原图像的噪声和一些小的畸变。
最大池化就是下采样的过程。给定池化核(lenet-5中是2×2),在池化核框定的范围内取最大值并保留,将其他值丢弃。经过池化操作之后,图片的大小将减半。
梯度下降
网络在前向传播结束后,会进行反向传播来更新参数。更新的方法是梯度下降。
梯度下降,就是让参数向最小化损失函数的方向移动。损失函数可以想象成一个山群,谷底是损失函数的最小值。梯度下降就是计算参数当前所有的梯度,并沿着梯度最大的方向向下走。这样,参数会慢慢地收敛到谷底,让损失函数最小,取得最优解。在lenet-5中应用的不是单纯的梯度下降,而是带动量的随机梯度下降算法,这能让梯度更快地收敛到最优解,并减少震荡。
普通的梯度下降算法的公式如下:
其中,lr是学习率。这个超参数决定了损失函数最终的收敛效果。学习率太大可能会导致损失函数冲出谷底,造成损失函数不降反升,学习率太小会导致损失函数下降地很慢,增大迭代次数。学习率是神经网络地一个十分重要的参数,在对网络的进一步优化中,会使用学习率衰减或自适应学习率来加快收敛,减少震荡。
损失函数
进行前向传播之后,我们要得到损失函数。得到损失函数的方法是负对数似然估计(NLL)和softmax回归。
经过神经网络的一次前向传播之后,我们得到了一组(1,10)的向量,分别代表每张图属于0-9中的每个数字的概率大小。数字越大,这张手写体图片所写的就越可能是那个数字。这时,我们的神经网络经过一次迭代,准确度一定不高,我们不希望直接在这里取最大值,从而忽略掉图片是其他数字的可能性。所以使用softmax回归,令较大的数有较大概率被取到。
Softmax细节
Softmax的公式如下:
这个公式的直观理解如下:
假设我们有一个向量[3,1,-3],将这组向量传入softmax层进行前向传播,我们会得到约等于[0.88,0.12,0]这样的向量。注意到,各分量的和为1,这是公式决定的。这样,这个向量就可以表示取到0,1,2各个值的概率了。
经过softmax层之后,我们得到了一组概率。之后利用这组概率,我们计算损失。
NLL细节
我们希望我们的网络尽可能准确,故我们的损失要越小越好。损失函数的公式定义如下:
其中,M为这组概率的真实值位置对应的分量。
假设我的概率为[0.2,0.3,0.1,0.01,0.19],分别对应[0,1,2,3,4]这几个类别。我的真实值为类别2,我希望我的损失函数尽可能小,由于-lnM是单调递减的函数,于是M的值要尽可能地大。由于M是一组概率,所以它不会超过1,也意味着L的最小值不会低于0.在这个简单例子中,损失为-ln(0.1),是一个相对比较大的数字。想让L接近0,就必须让对应真实值位置的概率接近1。于是最大似然损失估计能够真实地反映网络对训练集地预测情况。
总结
至此,lenet-5网络结构梳理解释完毕。其中涉及到一些数学公式,也尽量做了通俗易懂的解释。