十、经典架构复现1:LeNet5、AlexNet、VGGNet、NiN
学深度视觉其实学的就是一个个经典架构或SOTA架构。所以上面的内容都是打基础的东西,而且也只是一部分最简单的基础,还有很多内容,后面我们再一点点补充。所以到这里才开始真正学架构。
而讲深度学习经典架构又绕不开ImageNet大规模视觉识别挑战赛,ImageNet Large Scale Visual Recognition Challenge(ILSVRC) 。我看很多资料都是从这个比赛的排名讲起的,我们这里也是从这条线讲起:
下面是ILSVRC大赛历年冠军:
ILSVRC大赛是计算机视觉领域最具权威的学术竞赛之一。
ImageNet数据集由斯坦福大学李飞飞教授主导制作,其包含了超过1400万张全尺寸的有标记图片,ILSVRC从ImageNet数据集中抽出子集作为竞赛数据。
但是这个比赛在2017年就shutdown了,因为这是一个识别任务的比赛,2017年模型的错误率已经降低到2.3%了,人眼的错误率还有5.1%呢,就是已经远超人眼了。而且此后的方案都已经不是一些通用的方法来解决问题了,而是过度使用数据集中的特有特点,来降低错误率,这就相当于针对特定问题而解决问题,而不是找到一些通用方法解决普遍问题了,就是后来的架构可以在imgenet数据集上取得很好的效果,但在其他别的数据集上效果就没这么好了,相当于是进入了过拟合阶段,所以也就没必要继续刷错误率了。所以比赛就停了。现在这个比赛就转移到Kaggle上举办了,但不是识别任务了。
至于这个数据集,据说ImageNet2019的数据集可以在Kaggle上可以免费下载,主要是用于检测任务的。其他就很难找了。我们一般使用的都是2012或2013年的数据版本。仅2012年的ILSVRC就有1281167张训练集,50000张验证集,100000张测试集,大小有154G,不是每个人都有这个算力把它跑起来的,所以对于个人不建议使用这个数据集。
从上图可以看到,在2012年之前,ILSVRC大赛上的图像识别都是用的是机器学习的方法,比如特征提取+支持向量机的方式,每年比上年刷新1-2个百分点的进度提升。上图的分值都是top5错误率,直到2012年,AlexNet轻松将精度提升超过10个百分点而获得挑战赛冠军!此后人们开始把图像识别从机器学习方法转移到神经网络方法上。
2012年之前神经网络之所以一直沉寂,一是因为神经网络容易过拟合,2012年之后我们有了巨量的数据集,过拟合问题一下就缓解了;二是我们的硬件进步了,Alexnet就是第一个用显卡来计算的,也给人们指出了一个方向:就是神经网络的计算可以用显卡来计算,让计算瓶颈问题也一定程度上解决了。所以也是2012年后神经网络出现了突飞猛进的发展。
至于卷积神经网络,其实最开始使用的并不是Alexnet而是Lenet。LeNet是由LeCun等人提出于1998年提出,是一种用于手写体字符识别的卷积神经网络。出自论文《Gradient-Based Learning Applied to Document Recognition》。但是当时LeNet没火是因为,Lenet架构当时解决的问题是邮政编码的识别任务, 当时人们认为我用特征提取+svm就可以很好解决邮政编码的问题,何须用这么复杂的一个东西,所以也没有引起太大的注意,直到Alexnet在Imagenet上刷新冠军记录后,卷积神经网络才重新进入人们的视野。下面我开始一个个梳理卷积神经网络中的经典架构。
1、LeNet5
下图就是LeNet5的架构图。不算输入层,该架构共有7层,包括2个卷积层(C1和C3),2个池化层(就是下图中标的下采样层S2和S4),3个全连接层(C5、C5、OUTPUT层)。
输入:32x32x1 (尺寸是32x32,通道数是1,即输入是32x32的灰度图像)
经过第一层卷积层(kernel_size=5x5, stride=1), 输出:28x28x6 (尺寸是28x28,通道数是6)
经过第一层池化层(avg pool, kernel_size=2, stride=2), 输出:14x14x6 (尺寸是14x14,通道数仍然是6)
经过第二层卷积层(kernel_size=5x5, stride=1),输出:10x10x16(尺寸是10x10, 通道数是16)
经过第二层池化层(avg pool, kernel_size=2, stride=2), 输出:5x5x16(尺寸是5x5,通道数是16)
经过第一层线性全连接层FC(120), 其中120表示这个线性层有120个神经元,也就是输出120个数字
经过第二层线性全连接层FC(84), 其中84表示这个线性层输出84个数值,即输出是84个神经元。
最后一层是输出层,是用softmax分类的,softmax to labels,是10分类。
此外,在上述构架中,除了输出层外,每个卷积层和全连接层后都要使用激活函数tanh或sigmoid。
LeNet5 这个网络虽然很小,但是它包含了深度学习的基本模块:卷积层,池化层,全链接层。是其它深度学习模型的基础。
LeNet5的核心思想是:用“卷积+池化+线性层”这样的架构来搭建网络。其中前面的卷积层是用来提取细粒度特征,卷积层后面的池化层是对提取的特征进行统计的,后面的卷积层是用来提取粗粒度特征的,紧跟的池化层也是用来统计这些特征的。连续使用多个卷积层+池化层,就是连续多次提取并统计不同粒度的特征信息。最后后面跟的线性层是把前面的特征信息融合-变换-输出的作用。
在LeNet5被提出来后,几乎所有的卷积网络都是连用卷积层+池化层,最后用全连接层(也就是线性层)输出的这种架构。下面是代码实现:
A:这是我们测试架构是否能跑通的玩具数据。这里想说的是,要复现别人的架构,输入数据的特点非常重要,因为每个架构都是对其处理的数据的定制架构,所以数据结构一定要遵循原作者的数据结构,否则你就得改架构,而改架构就得改参数,而改参数你就得非常清晰你的数据流入流出每层的变化情况,而要做到这点就看你前面的基础是否打得牢固,如果基础牢固这些就不算问题。
B: 定义init方法第一个参数无脑写self, 后面还可以填写如果你的类需要的话,就可以写你自己的参数,但这里我们不需要自己需要输入的超参数。这个写法我们前面讲DNN的时候已经强调过多次了。
要验证数据在传递过程中的尺寸变化是否和原架构一致,我们可以使用torchinfo库来计算深度网络每一层输出的特征图的尺寸,torchinfo只能用于pytorch,一般电脑上是没有的,需要重新安装:cmd/terminal里面运行:pip install torchinfo即可。
查看架构的各项指标:
说明:上述复现架构的写法是非常麻烦的,但这种写法的优点是适合简单的网络,而且这种写法比较清晰,init函数下面的架构一目了然,forward函数下的数据流也非常清晰。但是写架构不止这一种写法,后面我们演示别的架构时,我会用另外的写法来实现。
2、AlexNet
AlexNet是参加Imagenet大赛的冠军,所以它处理的图片数据就较为复杂。lenet处理的邮政编码图片尺寸才32x32,而且还是单通道,样本量非常小,所以用机器学习模型也可以很好解决。而AliexNet此次处理的图片数据尺寸就是3通道的224x224。
同理,我们也是先看AlexNet的架构图:
上图就是AlexNet的架构图,可能第一次看到的同学会有些迷茫,因为它有很多层都没有画出来,而且从整体上看,它分了上下两层是因为AlexNet是第一个利用GPU并行计算的架构,所以分成上下两路,当然并行计算可以加速网络的训练和收敛速度。
其实AlexNet的完整架构是上图右边的架构图,左图仅仅画了卷积层和线性层,右图就还有最大池化层和局部响应归一化(LRN)层,就是上图的NORM归一化层。但是实际上还有激活层、dropout层,右图也没有显示出来。可见,我们说架构的时候,一是输入层是不算的,二是像激活层、池化层、LRN等其他各种归一化层都是对它们前面卷积层的输出进行处理的层,而且都没有参数,所以一般架构中都不标出来。所以我们在复现别人的架构时,一定要仔细分析它的架构,而不仅仅是看架构图。也所以Alexnet总共是有8个层,其中5个是卷积层,3个是全连接层。但是在卷积网络中,池化层是非常重要的层,也所以有很多资料说Alexnet总共是有11个层,其中5个卷积层,3个最大池化层,3个全连接层。
从架构上看,AlexNet和LeNet的区别其实不大:
lenet5架构:输入->(卷积+池化)->(卷积+池化)->(线性x3)->输出
alexnet架构:输入->(卷积+池化)->(卷积+池化)->(卷积x3+池化)->(线性x3)->输出
都是通过卷积+池化来提取特征,然后用线性层对提取的特征进行分类。
(1)Alexnet的出圈亮点:
一是,首次使用ReLU函数做为神经网络的激活函数。2010年前激活函数基本都是tanh和sigmoid,AlexNet之后卷积网络基本就是relu激活函数一统天下了,tanh和sigmoid激活函数出现的各种问题都可以通过relu激活函数来解决。
二是,首次提出Dropout正则化来控制过拟合。Alexnet的全连接隐藏层前面都加了dropout层,以0.5的概率将两个全连接层神经元的输出随机设置为0,来阻止过拟合。
三是,使用加入动量的小批量梯度下降算法,加速了训练过程中的收敛。
四是,使用数据增强,抑制了训练过程中的过拟合,就是将图像平移、翻转、基于PCA的RGB强度调整等数据增强手段,就相当于增加了样本量,模型每次看到的图片都不一样,也就没法过拟合了。
五是,利用GPU并行计算。AlexNet使用了2个GPU来提升速度,分别放置一半卷积核,实现了神经网络的跨CPU计算。因为当时的计算资源有限,只能一半一半的训练,当然现在的硬件已经不需要了这样做了,但是你要知道这样做是可以的。一个网络即使再好,你训练不出来也白搭。所以proper training也是非常重要的。
六是,AlexNet使用局部响应归一化技巧,就是添加了LRN层(local response normalization,局部响应归一化),这个层的初衷也是抵抗过拟合的,但是这个层在更深的网络中实验证明没有什么用,而且计算量还大,所以现在这个层基本上都没人用了。
(2)代码实现
这里我们再使用另一种构筑神经网络架构的方式:nn.Sequential。nn.Sequential可以将以序列方式从前往后运行的层打包起来,组合成类似机器学习中的管道pipeline的结构,把架构里的层在sequential函数里面串联起来。这种写法就是把架构和数据流混写在一起,好处是代码量要少很多,而且以后我们写深层模型的时候,这种写法可以实现网络的并联和串联等不同的架构搭建。不好处就是这种架构需要所有的变换都写成层的形式。
解读:
-
上面的架构就比较简单,代码量少,而且不用实例化,因为不是类嘛。
-
上面卷积层的参数按照顺序分别是:in_channels、out_channels、kernel_size、stride、padding。
-
上面的标注都是特征图的尺寸变化。可见,第一层卷积层和池化层(卷积+池化) ,为了处理尺寸较大的原始图片,先使用11x11的卷积核和较大步长来快速降低特征图的尺寸。同时,使用比较多的通道数来弥补降低尺寸造成的数据损失。第二层(卷积+池化)中的卷积核尺寸和步长就恢复到业界大小,核尺寸=3,步长=1,同时也是进一步扩大通道提取特征。第三层连续使用3个卷积层,目的主要还是提取不同粒度的特征,所以参数都设置的是kernel_size=3, padding=1搭配,这样就可以不消减特征图的情况下只提取特征。当特征提取得差不多了后,就用池化层把数据缩减到6x6进入全连接层。行业惯例是在线性全连接层前面的网络输出的特征一般都是5x5-7x7之间,一般不会太大。这里的6x6尺寸进入全连接层,都有9216个神经元,这个线性层的参数都高达3775多万个!所以不能太大。
-
Alexnet中的池化层都用了重叠池化,当时的目的是为了对抗轻微目标偏移带来的影响。但是事实上后面我们都用最大池化而非重叠池化。原因在讲卷积操作发展史部分反复说明过。
-
架构的输入数据是:(3,227, 227),就是尺寸是227x227的彩色图片,这个尺寸和架构图有点不一样,因为我们这个代码实现的是AlexNet5,所以架构中也没有LRN层。但是这里要强调的是,其实输入数据不是数据集中的图像数据,而是要先经过归一化操作的,就是把输入数据都减去数据集中所有图像的均值,除以数据集中所有图像的标准差,再喂入网络的。因为绝对值没有意义,而且还容易出现梯度问题。相对值才有意义,还使得网络中的数据流正反向都平稳,而且还方便分类。当然上述的归一化有点笨拙,比如有100万样本,先计算这100万数据的均值和方差就是一件很难的事情,所以我们还会使用其他归一化方法,后面我们讲数据预处理和数据增强时,再展开详细讲这个点。
-
LRN层的作用是让局部响应较大的值变得更大,让局部响应较小的值变得更小。但是后来在深度网络中发现这个层的作用不大,就后来都不用了。
-
比如第一个卷积层的输出特征图个数是96,就表示本层卷积操作使用了96个卷积核组(一组有3个不同的卷积核)来提取原图特征,一组卷积核表示一种纹理基元信息,也就是我们设计了96中纹理基元模板来提取原图信息,所以就生成96个特征相应图。卷积层后面紧跟relu层,表示每张特征相应图中的每个像素都要经过relu变换再输出。可见,每张特征图都是一个稀疏矩阵,因为原图中不同位置不一定都有模板表示的纹理基元。
-
这种堆叠卷积层,或者加深网络,就表示不同层都对应的是原图不同的感受野尺寸,就说明提取了不同粒度的基元信息。所以网络越深提取的信息越高级。
-
架构是如何分类呢?卷积+池化提取特征的统计信息后,我们得到的是下图的左边部分,是一个尺寸是6x6深度是256的数据。此时我们需要把256张特征图一张张拿出来,按行展开(flatten),变成一个36长的列向量,然后256个这样的列向量全部拼接起来,就是一个9216x1维的列向量,再输入全连接层。然后把全连接层的最后一层设置成分类个数个神经元,就可以达到分类目的了。此处也可见,卷积架构最后的全连接层是为了分类目的而存在的。这里还要强调的是,这种将卷积层和全连接层串联到一起的架构,在梯度回传方面是没有问题的。就是flatten操作时,梯度回传是不存在问题的。所以卷积层和全连接线性层是同时训练的。全连接层的最后一层后面再跟softmax激活函数,得到的就是softmax分类分值。有了softmax分值后面就可以接交叉熵损失函数了,有了损失函数就可以反向传播求梯度->更新网络参数,训练网络了。
-
前两个全连接层后面还跟dropout层,最后一个输出层后面得跟softmax层。dropout层可以放在全连接层的前面或者后面都行。放在前面表示的是从卷积层输入的数据进入线性层时的参数被dropout了,dropout层写在线性层的后面是表示第一个线性层的输出进入RELU层时的参数被dropout了p%, 所以dropout层写在线性层的哪里是表示沉默的是不同的神经元参数。而且有dropout就意味着集成的意思,有集成就意味着模型更强大。
(3)AlexNet给我们的经验启发:
-
小卷积核,多通道,更深的网络,数据要巨量,训练要用GPU, 也是当前深度学习的大方向。
-
卷积层的作用:
可见,卷积层的作用就是生成很多卷积核组,从前往后,从原图中从小到大提取和卷积核有响应的特征。而线性层是把这些特征响应展开成向量的形式来分类。
(4)ZFNet
2013年imagenet冠军ZFNet,和AlexNet网络架构基本一致!但是它对AlexNet进行了以下的改进:
一是将第一个卷积层的卷积核大小改为了7x7,因为它觉得AlexNet的第一层用11x11的卷积核太大,损失了很多原图中更细粒度的信息。而且大卷积核将原图尺寸缩减得太小,导致第一层就丢掉很多原图信息,所以ZFNet采用了更小的卷积核。
二是,ZFNet作者通过反向可视化后发现,其实架构的第三层卷积层和第四层卷积层的卷积核已经有一些高层语义的一些概念了。就是前面卷积层可视化后还是简单的纹理基元信息,到三四层就是简单基元组合的高层语义信息了。基元是简单有限的,不就是一些点、线、圈之类,不同就是大小方向会有所不同,但基元的各种组合就是数据巨大而且是变化万千的高层语义了。而变化万千的高层语义用384个卷积核组来提取显得有点不够,所以ZFNet进一步增加了第三四层的卷积核组个数,也就是增加第三四层的特征图输出数量到512个。
也所以我们后面的网络都采用的是:越靠前的卷积层设置的就较少,输出特征图也较少;越靠后的卷积层设置的就越多,输出的特征图也较多。但是最后一层卷积层的输出却不能太多,因为最后一层要接全连接层,如果输出特征图太多,全连接层的参数就太多,计算开销又承受不了。
3、VGG
提出VGG的论文是《Very Deep Convolutional Networks for Large-Scale Image Recognition》,论文给出下面的表格来描述VGG网络结构:
论文中分别使用了A、A-LRN、B、C、D、E这6种网络结构进行测试,这6种网络结构相似,都是由5层卷积块、3层全连接层组成,其中区别在于每个卷积块的子层数量不同,从A至E依次增加(子层数量从1到4),总的网络深度从11层到19层(添加的层以粗体显示),表格中的卷积层参数表示为“conv〈卷积核尺寸〉-通道数〉”,例如con3-128,表示使用3x3的卷积核,通道数为128。为了简洁起见,在表格中没有显示ReLU激活层。而且最后三个全连接层的前两个全连接层前面还有dropout层(p=0.5)也没有标出来。其中,网络结构D就是著名的VGG16,网络结构E就是著名的VGG19。
经作者对A、A-LRN、B、C、D、E这6种网络结构进行单尺度的评估,错误率结果如下:
从上表可以看出:
(1)LRN层无性能增益(A-LRN)。VGG作者通过网络A-LRN发现,AlexNet曾经用到的LRN层并没有带来性能的提升,因此在其它组的网络中均没再出现LRN层。
(2)随着深度增加,分类性能逐渐提高(A、B、C、D、E)。从11层的A到19层的E,网络深度增加对top1和top5的错误率下降很明显。
(3)多个小卷积核比单个大卷积核性能好。VGG作者做了实验用B和自己一个不在实验组里的较浅网络比较,较浅网络用conv5x5来代替B的两个conv3x3,结果显示多个小卷积核比单个大卷积核效果要好。
小结:
(1)通过增加深度能有效地提升性能。VGG最大的特点是:使用多个连续且保持特征图尺寸不变的卷积层来加深网络深度的操作。
(2)最佳模型:VGG16,从头到尾只有3x3卷积与2x2池化,简洁优美高效!
(3)VGG19的效果只比VGG16好了一点点slightly better,但参数量却是极大的增加了,所以平衡效果和效率(计算资源),人们都倾向于VGG16。
代码实现:
这次还用nn.Sequential方式来写,但是写成类的形式:
VGG抛弃11x11、7x7等这样的大卷积核,全部采用3x3这种更小的串联卷积核,来获得更大的感受野。而且小卷积核还可以加深网络,网络变深了,每层卷积层后面都跟relu变换的,就加强了网络的非线性变换。所以VGG将这种串联网络发挥到了极致,也是2014年的imagenet冠军。
4、NiN
网络中的网络(Network In Network,NiN)是发表于2014年ICLR的一篇paper。这篇文章采用较少参数就取得了Alexnet的效果,Alexnet参数大小为230M,而NIN仅为29M。此模型后来先后被Inception与ResNet等所借鉴。所以也非常值得拿出来聊聊。
NiN最大的亮点有下面3个:
(1)NiN最大的亮点就是使用了核尺寸是1*1的卷积层。NiN网络三次重复使用2个连续的MLP层,MLP直白的说就是核尺寸是1*1的卷积层,使得不改变特征图尺寸的情况下,而让网络深度加深了,提升了模型效果。但这在今天看来并不是最重要的。最重要的是NiN第一次把核尺寸是1*1卷积的卷积层摆到了大众面前!从此人们开始研究这个特殊卷积层的使用方法,据此也诞生了很多和这个层相关的巧妙架构,比如瓶颈设计bottleneck desigh(我们后面会分析这个架构)。这也是NiN现在值得我们反复重提的重要原因。受到NiN网络启发而诞生的GoogLeNet以及ResNet都使用了1*1卷积层,并且在各种消减参数的操作下使网络变得更加深。
(2)NiN第二个重大的亮点是:它把舍弃线性层的议题摆在了我们面前。由于VGG架构的参数量极大,而且参数最多的就是VGG的线性层,所以NiN的另一个亮点就是没有线性层!NiN是一路卷积加池化到输出,全程没有线性层。所以NiN是第一个想办法取代全连接层来降低参数量的算法。
(3)NiN第三个亮点就是它的最后一个池化层。NiN最后的一个池化层是一个平均池化层,但是这个池化层承担着用于输出分类的特殊使命,所以我们给最后一个平均池化层又取名GAP层,或者是GAP操作。就是让池化层的核尺寸等于特征图尺寸的平均池化操作,让最后输出变为1*1,就类似了DNN中一个神经元代表一个数字的那种层,让10分类在数据结构的形式上成为可能。
NiN架构:
输入层(3*32*32)--(卷积层+MLP*2+最大池化层+dropout层)--(卷积层+MLP*2+最大池化层+dropout层)--(卷积层+MLP*2+平均池化层)--softmax layer
下面是代码复现NiN: