Deep Residual Learning for Image Recognition
深度残差网络的图像识别
-
作者:Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun
-
单位:微软研究院
摘要
更深的神经网络更难训练。本文提出了一种残差学习框架来减轻网络训练,这些网络比以前使用的网络更深。明确地将层变为学习关于层输入的残差函数,而不是学习未参考的函数。提供了全面的经验证据说明这些残差网络很容易优化,并可以显著增加深度来提高准确性。在ImageNet数据集上评估了深度高达152层的残差网络——比VGG[40]深8倍但仍具有较低的复杂度。这些残差网络的集合在ImageNet测试集上取得了3.57%的错误率,该结果在ILSVRC 2015分类任务上赢得了第一名。同时也在CIFAR-10上分析了100层和1000层的残差网络。
对于许多视觉识别任务而言,表示的深度是至关重要的。仅由于非常深的表示,本文便在COCO目标检测数据集上的性能提升了28%。深度残差网络是我们向ILSVRC和COCO 2015竞赛提交的基础,也赢得了ImageNet检测任务,ImageNet定位任务,COCO检测和COCO分割任务的第一名。
1 引言
深度网络自然地将低/中/高级特征和分类器以端到端多层方式进行集成,特征的“级别”可以通过堆叠层的数量(深度)来丰富。
网络是否堆叠的更深就能学习到更好的网络?答案是否定的,梯度消失和梯度爆炸从一开始就阻碍了模型的收敛。
通过标准初始化[23, 8, 36, 12]和中间标准化层[16]在很大程度上已经解决,这使得数十层的网络能通过具有反向传播的随机梯度下降(SGD)开始收敛。
当更深层的网络能够开始收敛时,退化问题就暴露了出来:随着网络深度的增加,精度达到饱和,然后迅速下降。 这种下降不是由过拟合引起的,并且在适当的深度模型上添加更多的层会导致更高的训练误差。
退化(训练准确率)表明不是所有的系统都很容易优化。让我们考虑一个较浅的架构及其更深层次的对象,为其添加更多的层。**存在通过构建得到更深层模型的解决方案:添加的层是恒等映射,其他层是从学习到的较浅模型的拷贝。这种构造解决方案的存在表明,较深的模型不应该产生比其对应的较浅模型更高的训练误差。**但是实验表明,目前现有的解决方案无法找到与构建的解决方案相比相对不错或更好的解决方案(或在合理的时间内无法实现)。
在本文中,通过引入深度残差学习框架解决了退化问题。明确地让这些层拟合残差映射,而不是希望每几个堆叠的层直接拟合期望的基础映射。形式上,将期望的基础映射表示为H(x),将堆叠的非线性层拟合另一个映射 F ( x ) : = H ( x ) − x F(x) := H(x) − x F(x):=H(x)−x,原始的映射重写为 F ( x ) + x F(x) + x F(x)+x。假设残差映射比原始的、未参考的映射更容易优化。在极端情况下,如果一个恒等映射是最优的,那么将残差置为零比通过一堆非线性层来拟合恒等映射更容易。
公式 F ( x ) + x F(x) + x F(x)+x可以通过带有“快捷连接(shortcut connections)”的前向神经网络(图2)来实现。快捷连接[2, 33, 48]是那些跳过一层或更多层的连接。在本文案例中,快捷连接简单地执行恒等映射,并将其输出添加到堆叠层的输出(图2)。恒等快捷连接既不增加额外的参数也不增加计算复杂度。整个网络仍然可以由带有反向传播的SGD进行端到端的训练,并且可以使用公共库(例如,Caffe [19])轻松实现,而无需修改求解器。
在ImageNet上呈现了综合性的试验,用以表明衰退问题及评估本文提出的方法。得出了:
1) 非常深的残差网络很容易最优化,但是当网络的深度加深时,与残差网络相对应的朴素网络(without shortcut connections)则表现了更高的训练错误;
2) 深度残差网络可以很容易的从急剧增加的深度中获得精确度,产生的结果大体上更好于先前的网络。
在CIFAR-10上数据的表现一致,表明本文提出的方法不是针对某个特定数据集,可以迁移到其它的数据集上。
模型集合在ImageNet测试集上有3.57% top-5的错误率,并在ILSVRC 2015分类比赛中获得了第一名。极深的表示在其它识别任务中也有极好的泛化性能,并带领我们在进一步赢得了第一名:包括ILSVRC & COCO 2015竞赛中的ImageNet检测,ImageNet定位,COCO检测和COCO分割等任务,**坚实的证据表明残差学习准则是通用的,**并且期望它适用于其它的视觉和非视觉问题。
2 相关工作
残差表示。在图像识别中,VLAD[18]是一种通过关于字典的残差向量进行编码的表示形式,Fisher矢量[30]可以表示为VLAD的概率版本[18]。它们都是图像检索和图像分类[4,47]中强大的浅层表示。对于矢量量化,编码残差矢量[17]被证明比编码原始矢量更有效。
In low-level vision and computer graphics(在低层级的视觉及计算机图形学中),为了解决偏微分方程式(PDEs)的问题,广泛使用了Multigrid(多表格)的方法去将系统重新表示为多尺度的子问题,其中每一个子问题由残差解决方案在一个更加粗糙及一个更加精细的尺度范围内负责。
快捷连接。实践和理论导致快捷连接[2,33,48]已经被研究了很长时间。训练多层感知机(MLP)的早期实践是添加一个线性层来连接网络的输入和输出[33,48]。在[43,24]中,一些中间层直接连接到辅助分类器,用于解决梯度消失/爆炸。论文[38,37,31,46]提出了通过快捷连接实现层间响应,梯度和传播误差的方法。在[43]中,一个“inception”层由一个快捷分支和一些更深的分支组成。
与此同时进行的工作,“highway networks” [41, 42]提出了门控功能[15]的快捷连接。这些门是数据依赖且有参数的,与本文不具有参数的恒等快捷连接不同。**当门控快捷连接“关闭”(接近零)时,高速网络中的层表示非残差函数。**相反,本文的公式总是学习残差函数,恒等快捷连接永远不会关闭,所有的信息总是通过,还有额外的残差函数要学习。此外,高速网络还没有证实极度增加的深度(例如,超过100个层)带来的准确性收益。
3 深度残差学习
3.1 残差学习
把H(x)看作几个堆叠层(不必是整个网络)要拟合的基础映射,x表示这些层中第一层的输入。如果假设多个非线性层可以渐近地近似复杂函数,那么它等价于假设可以渐近地近似残差函数,即H(x) – x(假设输入输出是相同维度的)。因此,明确让这些层近似参数函数F(x) := H(x) − x,而不是期望堆叠层近似H(x)。因此原始函数变为F(x) + x。尽管两种形式应该都能渐近地近似目标函数(如假设),但学习的难易程度可能是不同的。
重新公式化表示(即H(x) = F(x) + x)。关于衰退问题(朴素神经网络越深,训练错误越高)的违反自觉的现象所激励。正如在引言中讨论的,如果增加的网络层可以构造为identity mappings,一个更深的网络模型应该具有比之更浅层的对应网络更低的训练错误。(训练精确性的)衰退问题表明,求解程序在通过多个非线性网络层去逼近恒等映射时也许会存在困难。由于残差学习的重新公式化表示,如果恒等映射是最优的,求解程序也许只要简单地将多个非线性网络层的权重向0逼近,以此逼近恒等映射(设H(x) = F(x) + x,如果F(x) = 0,那么H(x) = F(x) + x = x 即为恒等映射;而F(x)等于权重w乘以输入x的复杂函数,如果要使得F(x) = 0,那么权重w得为0)。
在现实情况下,恒等映射是最优的是不太可能的,但是我们的重新公式化表示也许有助于预处理网络衰退的问题。如果,相较于接近一个0映射,最优化的函数更接近于一个恒等映射;那么对于求解程序来说,相较于像一个新的网络架构去学习目标函数那样,去发现关于恒等映射的扰动是更容易的。通过实验表明了,通常来说,学习性的残差函数具有很小的响应,这表明恒等映射提供了一个令人满意的网络衰退的预处理。
3.2 快捷恒等映射
每隔几个堆叠层采用残差学习。构建块如图2所示。在本文中我们考虑构建块正式定义为:
y = F ( x , W i ) + x . . . . . . . ( 1 ) y = F(x, {W_i}) + x .......(1) y=F(x,Wi)+x.......(1)
x和y是考虑的层的输入和输出向量。函数 F ( x , W i ) F(x, {W_i}) F(x,Wi)表示要学习的残差映射。对于图2中具有两层网络的例子来说, F = w 2 δ ( w 1 x ) F=w_2δ(w_1x) F=w2δ(w1x),其中,δ指代为ReLU函数(f(x) = max(0,x),rectified linear units 修正的线性单元);此处为了简化概念,将偏置予以忽略。函数操作 F + x F+x F+x是由一个捷径连接和一个element-wise addition(对位加法)构成的。在加法之后采用了第二个非线性函数(即 δ ( F + x ) δ(F+x) δ(F+x)).
在公式1中,x和F的维度必须相同。如果不相同(例如,输入和输出的通道数量改变了),可以通过对残差连接执行一个权重为 w s w_s ws的线性的映射,用以匹配输入和输出的维度:
y = F ( x , W i ) + w s ∗ x . . . . . . . ( 2 ) y = F(x, {W_i}) + w_s*x ....... (2) y=F(x,Wi)+ws∗x.......(2)
也可以在方程式(1)中使用方阵 w s w_s ws;对于convolution layer来说, w s w_s ws是一个4维数组,例如其shape[1, 1, in_channel, out_channel])。但是将会用实验表明:恒等映射足以解决网络衰退的问题,并且恒等映射是经济性的(introduce neither extra parameter nor computation complexity); 因此, w s w_s ws只在需要匹配维度时才用。
残差函数F的形式是灵活的。本文中的实验涉及一个 具有2到3层的函数F,尽管更多的层次也是有可能的。但是,如果函数F仅仅只有一层,则公式(1)类似于一个线性层:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GGYT8jZt-1650361482306)(https://www.zhihu.com/equation?tex=y+%3D+w_%7B1%7D+x+%2B+x)],而这并没有产生价值。
还注意到,为了简单起见,尽管上述符号是关于全连接层的,但它们同样适用于卷积层。函数 y = F ( x , W i ) y = F(x, {W_i}) y=F(x,Wi)可以表示多个卷积层。元素加法在两个特征图上逐通道进行(通道对通道,feature map 的shape一般为:[批次图片的数量 , feature map的长,feature map的宽,通道的数量 ])。
3.3 网络架构
测试了各种简单/残差网络,并观察到了一致的现象。为了提供讨论的实例,我们描述了ImageNet的两个模型如下。
普通网络(Plain Network):简单网络的基准(图3,中间)主要受到VGG网络[40](下图左图)的启发。卷积层主要有3×3的滤波器,并遵循两个简单的设计规则:
(i)为了得到相同的输出特征图尺寸,所有层具有相同数量的滤波器;
(ii)如果特征图尺寸减半,则滤波器数量加倍,以便保持每层的时间复杂度;
我们通过步长为2的卷积层直接执行下采样。网络以全局平均池化层和具有softmax的1000维全连接层结束。图3(中间)的加权层总数为34。值得注意的是我们的模型与VGG网络(图3左)相比,有更少的滤波器和更低的复杂度。我们的34层基准有36亿FLOP(乘加),仅是VGG-19(196亿FLOP)的18%。
图3 网络架构例子。左:作为参考的VGG-19模型[41](196亿FLOPs)。中:具有34个参数层的简单网络(36亿FLOPs)。右:具有34个参数层的残差网络(36亿FLOPs)。带点的快捷连接增加了维度。下标显示了更多细节和其它变种。
残差网络。 基于上述的简单网络,插入快捷连接(图3右),将网络转换为其对应的残差版本。当输入和输出 具有相同的维度时(图3中的实线快捷连接)时,可以直接使用恒等快捷连接(方程1) 。当维度增加(图3中的虚线快捷连接)时,考虑两个选项:
- (A)快捷连接仍然执行恒等映射,额外填充零输入以增加维度。此选项不会引入额外的参数
- (B)方程2中的投影快捷连接用于匹配维度(由1×1卷积完成)
对于这两个选项,当快捷连接跨越两种尺寸的特征图时,它们执行时步长为2。
3.4 实现
对于ImageNet数据集,本文的实现遵循[21,41]的实践。调整图像大小,其较短的边在[256,480]之间进行随机采样,用于尺度增强[41]。从图像或其水平翻转中随机采样224×224裁剪大小,并逐像素减去均值[21]。使用[21]中的标准颜色增强。参照[16],在每个卷积之后和激活之前,采用批量归一化(BN)[16](conv -> BN -> relu)。按照[12]的方法初始化权重,从零开始训练所有的简单/残差网络。使用批大小为256的SGD方法。学习速度从0.1开始,当误差稳定时学习率除以10,并且模型训练 60 × 1 0 4 60×10^4 60×104次迭代。使用的权重衰减为0.0001,动量为0.9。根据[16]的实践,不使用dropout[13]。
在测试阶段,为了比较学习采用标准的10-crop测试[21]。对于最好的结果,采用如[40, 13]中的全卷积形式,并在多尺度上对分数进行平均(图像归一化,短边位于{224, 256, 384, 480, 640}中)。
4 实验
4.1 ImageNet分类
在ImageNet 2012分类数据集[36]上对本文提出的方法进行了评估,该数据集由1000个类别组成。这些模型在128万张训练图像上进行训练,并在5万张验证图像上进行评估,也获得了测试服务器报告的在10万张测试图像上的最终结果,评估top-1和top-5错误率。
简单网络。首先评估18层和34层的简单网络。34层简单网络在图3(中间)。18层简单网络是一种类似的形式。有关详细的体系结构,请参见表1。
表2中的结果表明,较深的34层简单网络比较浅的18层简单网络有更高的验证误差。为了揭示原因,在图4(左图)中,比较训练过程中的训练/验证误差。观察到退化问题——虽然18层简单网络的解空间是34层简单网络解空间的子空间,但34层简单网络在整个训练过程中具有较高的训练误差。
本文认为这种优化难度不可能是由于梯度消失引起的。这些简单网络使用BN[16]训练,这保证了前向传播信号有非零方差。我们还验证了反向传播的梯度,结果显示其符合BN的正常标准。因此既不是前向信号消失也不是反向信号消失。实际上,34层简单网络仍能取得有竞争力的准确率(表3),这表明在某种程度上来说求解器仍有效。本文推测深层简单网络可能有指数级低收敛特性,这影响了训练误差的降低。这种优化困难的原因将来会研究。
残差网络。接下来评估18层和34层残差网络(ResNets)。基准架构与上述的简单网络相同,如图3(右)所示,预计每对3×3滤波器都会添加快捷连接。在第一次比较(表2和图4右侧)中,对所有快捷连接都使用恒等映射和零填充以增加维度(选项A),与对应的简单网络相比,它们没有额外的参数。
从表2和图4中可以看到三个主要的观察结果。
- 首先,通过残差学习这种情况发生的逆转——34层ResNet比18层ResNet更好(2.8%)。更重要的是,**34层ResNet显示出相当低的训练误差,并且可以泛化到验证数据。**这表明在这种情况下,退化问题得到了很好的解决,我们从增加的深度中设法获得了准确性收益
- 第二,与对应的简单网络相比,由于成功的减少了训练误差,34层ResNet降低了3.5%的top-1错误率。这种比较证实了在极深系统中残差学习的有效性
- 最后,还注意到18层的简单/残差网络同样地准确(表2),但18层ResNet收敛更快(图4右和左)。当网络“不过度深”时(18层),目前的SGD求解器仍能在简单网络中找到好的解。在这种情况下,ResNet通过在早期提供更快的收敛简便了优化
恒等与投影快捷连接。之前已经表明没有参数、恒等快捷连接有助于训练。接下来探讨投影快捷连接(方程2)。在表3中比较了三个选项:(A)零填充快捷连接用来增加维度,所有的快捷连接是没有参数的(与表2和图4右相同);(B)投影快捷连接用来增加维度,其它的快捷连接是恒等的;(C)所有的快捷连接都是投影。
表3显示,所有三个选项都比对应的简单网络好很多。选项B比A略好。我们认为这是因为A中的零填充确实没有残差学习。选项C比B稍好,我们把这归因于许多(十三)投影快捷连接引入了额外参数。但A/B/C之间的细微差异表明,投影快捷连接对于解决退化问题不是至关重要的。因此,在本文的剩余部分不再使用选项C,以减少内存/时间复杂性和模型大小。恒等快捷连接对于不增加下面介绍的瓶颈结构的复杂性尤为重要。
更深的瓶颈结构。接下来描述ImageNet中使用更深的网络。由于考虑到能承受的训练时间,将构建块修改为瓶颈设计。对于每个残差函数F,使用3层堆叠而不是2层(图5)。这三层是1×1,3×3和1×1卷积,其中1×1层负责减小然后增加(恢复)维度,使3×3层成为具有较小输入/输出维度的瓶颈。图5展示了一个示例,两个设计具有相似的时间复杂度。
无参数恒等快捷连接对于瓶颈架构尤为重要。如果图5(右)中的恒等快捷连接被投影替换,则可以显示出时间复杂度和模型大小加倍,因为快捷连接是连接到两个高维端。因此,恒等快捷连接可以为瓶颈设计得到更有效的模型。
50层ResNet:用3层瓶颈块替换34层网络中的每一个2层块,得到了一个50层ResNet(表1)。使用选项B来增加维度。该模型有38亿FLOP。
101层和152层ResNet:通过使用更多的3层瓶颈块来构建101层和152层ResNets(表1)。值得注意的是,尽管深度显著增加,但152层ResNet(113亿FLOP)仍然比VGG-16/19网络(153/196亿FLOP)具有更低的复杂度。
50/101/152层ResNet比34层ResNet的准确性要高得多(表3和4)。没有观察到退化问题,因此可以从显著增加的深度中获得显著的准确性收益。所有评估指标都能证明深度的收益(表3和表4)。
与最先进的方法比较。在表4中,与以前最好的单一模型结果进行比较。基准的34层ResNet已经取得了非常有竞争力的准确性。 152层ResNet单模型具有4.49%的top-5错误率。这个单一模型的结果胜过以前的所有集成结果(表5)。结合了六种不同深度的模型,形成一个集成模型(在提交时仅有两个152层)。这在测试集上得到了3.5%的top-5错误率(表5),这次提交在2015年ILSVRC中荣获了第一名。
4.2 CIFAR-10和分析
对CIFAR-10数据集[20]进行了更多的研究,其中包括10个类别中的5万张训练图像和1万张测试图像。我们介绍了在训练集上进行训练和在测试集上进行评估的实验。关注点在于极深网络的表现,而不是产生最先进的结果,所以有意使用如下的简单架构。
简单/残差架构如图3(中/右)的形式。网络输入是32×32的图像,每个像素减去均值。第一层是3×3卷积。然后在大小为{32,16,8}的特征图上分别使用了带有3×3卷积的6n个堆叠层,每个特征图大小使用2n层。滤波器数量分别为{16,32,64}。下采样由步长为2的卷积进行。网络以全局平均池化,一个10维全连接层和softmax作为结束。共有6n+2个堆叠的加权层。下表总结了这个架构:
当使用快捷连接时,连接到成对的3×3卷积层上(共3n个快捷连接)。在这个数据集上,在所有案例中都使用恒等快捷连接(即选项A),因此该类残差模型与对应的简单模型具有完全相同的深度,宽度和参数数量。
使用的权重衰减为0.0001和动量为0.9,并采用[12]和BN[16]中的权重初始化,但没有使用dropout。这些模型在两个GPU上进行训练,批处理大小为128。开始使用的学习率为0.1,在32k次和48k次迭代后学习率除以10,并在64k次迭代后终止训练,这是由45k/5k的训练/验证集分割决定的。按照[24]中的简单数据增强进行训练:每边填充4个像素,并从填充图像或其水平翻转图像中随机采样32×32的裁剪图像。对于测试,只评估原始32×32图像的单一视图。
实验比较了n = {3,5,7,9},得到了20层,32层,44层和56层的网络。图6(左)显示了简单网络的性能表现。深度简单网络经历了深度增加,随着深度增加表现出了更高的训练误差。这种现象类似于ImageNet中(图4,左)和MNIST中的现象,表明这种优化困难是一个基本的问题。
图6(中)显示了ResNet性能,这也与ImageNet的情况类似(图4,右),ResNet设法克服优化困难,并随着深度的增加呈现了准确性收益。
进一步探索了n = 18得到了110层的ResNet。在这种情况下,发现0.1的初始学习率对于收敛来说太大了。因此使用0.01的学习率开始训练,直到训练误差低于80%(大约400次迭代),然后学习率变回到0.1并继续训练。学习过程的剩余部分与前面做的一样。这个110层网络收敛的很好(图6,中)。它与其它的深且窄的网络例如FitNet[35]和Highway[42]相比有更少的参数,然而结果仍在目前最好的结果中(6.43%,表6)。
层响应分析。图7显示了层响应的标准偏差(std)。这些响应每个3×3层的输出,在BN之后和其他非线性(ReLU/加法)之前。对于ResNets,该分析揭示了残差函数的响应强度。图7显示ResNet的响应比其对应的简单网络的响应更小。这些结果支持了我们的基本动机(第3.1节),即残差函数通常比非残差函数更接近零。通过比较图7中ResNet-20,ResNet-56和ResNet-110,还注意到,更深的ResNet具有较小的响应幅度。当层数更多时,单层ResNet趋向于更少地修改信号。
探索超过1000层。探索了一个超过1000层非常深的模型。设置n = 200,得到了1202(6*200+2)层的网络,其训练如上所述。该方法显示没有优化困难,这个103层的网络能够实现训练误差<0.1%(图6,右图),其测试误差仍然很好(7.93%,表6)。
但是,这种极深的模型仍然存在着一些未解决的问题。这个1202层网络的测试结果比我们的110层网络的测试结果更差,虽然两者都具有类似的训练误差,我们认为这是过拟合造成的。对于这种小型数据集,1202层网络可能过大(19.4M)。在这个数据集应用强大的正则化,如maxout[9]或者dropout[13]来获得最佳结果([9,25,24,34])。在本文中,不使用maxout/dropout,只是简单地通过设计深且窄的架构简单地进行正则化,而不会分散集中在优化难点上的注意力。但结合更强的正规化可能会改善结果,我们将来会做研究。
4.3 在PASCAL和MS COCO上的目标检测
本文提出的方法对其他识别任务有很好的泛化性能。表7和表8显示了PASCAL VOC 2007和2012[5]以及COCO[26]的目标检测基准结果。采用Faster R-CNN[32]作为检测方法。在这里,**感兴趣的是用ResNet-101替换VGG-16[40]。**这两种模型的检测方法(见附录)是一样的,所以收益只能归因于更好的网络。最显著的是,在有挑战性的COCO数据集中,COCO的标准度量指标(mAP@[.5,.95])增长了6.0%,相对改善了28%,这种收益完全是由于学习到的表示。
基于深度残差网络,在ILSVRC和COCO 2015竞赛的几个任务中获得了第一名,分别是:ImageNet检测,ImageNet定位,COCO检测,COCO分割。。
5 结论
在这项工作中,提出了一个残差结构,让模型学习的是残差关系,相较于传统的层次模型,残差模型更好学习一些;对于层数较少的网络结构,使用3x3,3x3两层结构;如果是多层,使用1x1, 3x3, 1x1三层结构。残差结构能够很好地解决深度增加以后,模型出现退化的情况。
参考
[1] Y. Bengio, P. Simard, and P. Frasconi. Learning long-term dependencies with gradient descent is difficult. IEEE Transactions on Neural Networks, 5(2):157–166, 1994.
[2] C. M. Bishop. Neural networks for pattern recognition. Oxford university press, 1995.
[3] W. L. Briggs, S. F. McCormick, et al. A Multigrid Tutorial. Siam, 2000.
[4] K. Chatfield, V. Lempitsky, A. Vedaldi, and A. Zisserman. The devil is in the details: an evaluation of recent feature encoding methods. In BMVC, 2011.
[5] M. Everingham, L. Van Gool, C. K. Williams, J. Winn, and A. Zisserman. The Pascal Visual Object Classes (VOC) Challenge. IJCV, pages 303–338, 2010.
[6] S. Gidaris and N. Komodakis. Object detection via a multi-region & semantic segmentation-aware cnn model. In ICCV, 2015.
[7] R. Girshick. Fast R-CNN. In ICCV, 2015.
[8] R. Girshick, J. Donahue, T. Darrell, and J. Malik. Rich feature hierarchies for accurate object detection and semantic segmentation. In CVPR, 2014.
[9] X. Glorot and Y. Bengio. Understanding the difficulty of training deep feedforward neural networks. In AISTATS, 2010.
[10] I. J. Goodfellow, D. Warde-Farley, M. Mirza, A. Courville, and Y. Bengio. Maxout networks. arXiv:1302.4389, 2013.
[11] K. He and J. Sun. Convolutional neural networks at constrained time cost. In CVPR, 2015.
[12] K. He, X. Zhang, S. Ren, and J. Sun. Spatial pyramid pooling in deep convolutional networks for visual recognition. In ECCV, 2014.
[13] K. He, X. Zhang, S. Ren, and J. Sun. Delving deep into rectifiers: Surpassing human-level performance on imagenet classification. In ICCV, 2015.
[14] G. E. Hinton, N. Srivastava, A. Krizhevsky, I. Sutskever, and R. R. Salakhutdinov. Improving neural networks by preventing coadaptation of feature detectors. arXiv:1207.0580, 2012.
[15] S. Hochreiter and J. Schmidhuber. Long short-term memory. Neural computation, 9(8):1735–1780, 1997.
[16] S. Ioffe and C. Szegedy. Batch normalization: Accelerating deep network training by reducing internal covariate shift. In ICML, 2015.
[17] H. Jegou, M. Douze, and C. Schmid. Product quantization for nearest neighbor search. TPAMI, 33, 2011.
[18] H. Jegou, F. Perronnin, M. Douze, J. Sanchez, P. Perez, and C. Schmid. Aggregating local image descriptors into compact codes. TPAMI, 2012.
[19] Y. Jia, E. Shelhamer, J. Donahue, S. Karayev, J. Long, R. Girshick, S. Guadarrama, and T. Darrell. Caffe: Convolutional architecture for fast feature embedding. arXiv:1408.5093, 2014.
[20] A. Krizhevsky. Learning multiple layers of features from tiny images. Tech Report, 2009.
[21] A. Krizhevsky, I. Sutskever, and G. Hinton. Imagenet classification with deep convolutional neural networks. In NIPS, 2012.
[22] Y. LeCun, B. Boser, J. S. Denker, D. Henderson, R. E. Howard, W. Hubbard, and L. D. Jackel. Backpropagation applied to handwritten zip code recognition. Neural computation, 1989.
[23] Y. LeCun, L. Bottou, G. B. Orr, and K.-R.M¨uller. Efficient backprop. In Neural Networks: Tricks of the Trade, pages 9–50. Springer, 1998.
[24] C.-Y. Lee, S. Xie, P. Gallagher, Z. Zhang, and Z. Tu. Deeplysupervised nets. arXiv:1409.5185, 2014.
[25] M. Lin, Q. Chen, and S. Yan. Network in network. arXiv:1312.4400, 2013.
[26] T.-Y. Lin, M. Maire, S. Belongie, J. Hays, P. Perona, D. Ramanan, P. Doll´ar, and C. L. Zitnick. Microsoft COCO: Common objects in context. In ECCV. 2014.
[27] J. Long, E. Shelhamer, and T. Darrell. Fully convolutional networks for semantic segmentation. In CVPR, 2015.
[28] G. Mont´ufar, R. Pascanu, K. Cho, and Y. Bengio. On the number of linear regions of deep neural networks. In NIPS, 2014.
[29] V. Nair and G. E. Hinton. Rectified linear units improve restricted boltzmann machines. In ICML, 2010.
[30] F. Perronnin and C. Dance. Fisher kernels on visual vocabularies for image categorization. In CVPR, 2007.
[31] T. Raiko, H. Valpola, and Y. LeCun. Deep learning made easier by linear transformations in perceptrons. In AISTATS, 2012.
[32] S. Ren, K. He, R. Girshick, and J. Sun. Faster R-CNN: Towards real-time object detection with region proposal networks. In NIPS, 2015.
[33] S. Ren, K. He, R. Girshick, X. Zhang, and J. Sun. Object detection networks on convolutional feature maps. arXiv:1504.06066, 2015.
[34] B. D. Ripley. Pattern recognition and neural networks. Cambridge university press, 1996.
[35] A. Romero, N. Ballas, S. E. Kahou, A. Chassang, C. Gatta, and Y. Bengio. Fitnets: Hints for thin deep nets. In ICLR, 2015.
[36] O. Russakovsky, J. Deng, H. Su, J. Krause, S. Satheesh, S. Ma, Z. Huang, A. Karpathy, A. Khosla, M. Bernstein, et al. Imagenet large scale visual recognition challenge. arXiv:1409.0575, 2014.
[37] A. M. Saxe, J. L. McClelland, and S. Ganguli. Exact solutions to the nonlinear dynamics of learning in deep linear neural networks. arXiv:1312.6120, 2013.
[38] N. N. Schraudolph. Accelerated gradient descent by factor-centering decomposition. Technical report, 1998.
[39] N. N. Schraudolph. Centering neural network gradient factors. In Neural Networks: Tricks of the Trade, pages 207–226. Springer, 1998.
[40] P. Sermanet, D. Eigen, X. Zhang, M. Mathieu, R. Fergus, and Y. Le-Cun. Overfeat: Integrated recognition, localization and detection using convolutional networks. In ICLR, 2014.
[41] K. Simonyan and A. Zisserman. Very deep convolutional networks for large-scale image recognition. In ICLR, 2015.
[42] R. K. Srivastava, K. Greff, and J. Schmidhuber. Highway networks. arXiv:1505.00387, 2015.
[43] R. K. Srivastava, K. Greff, and J. Schmidhuber. Training very deep networks. 1507.06228, 2015.
[44] C. Szegedy,W. Liu, Y. Jia, P. Sermanet, S. Reed, D. Anguelov, D. Erhan, V. Vanhoucke, and A. Rabinovich. Going deeper with convolutions. In CVPR, 2015.
[45] R. Szeliski. Fast surface interpolation using hierarchical basis functions. TPAMI, 1990.
[46] R. Szeliski. Locally adapted hierarchical basis preconditioning. In SIGGRAPH, 2006.
[47] T. Vatanen, T. Raiko, H. Valpola, and Y. LeCun. Pushing stochastic gradient towards second-order methods–backpropagation learning with transformations in nonlinearities. In Neural Information Processing, 2013.
[48] A. Vedaldi and B. Fulkerson. VLFeat: An open and portable library of computer vision algorithms, 2008.
[49] W. Venables and B. Ripley. Modern applied statistics with s-plus. 1999.
[50] M. D. Zeiler and R. Fergus. Visualizing and understanding convolutional neural networks. In ECCV, 2014.
实践
基于tf2.0实现resnet50, 数据集是cifar-10,resnet50的结构参见下表
### 导入相应的包
import numpy as np
import tensorflow as tf
from tensorflow import keras
from matplotlib import pyplot as plt
### Load cifar-10 dataset
(train_img, train_label), (test_img, test_label) = tf.keras.datasets.cifar10.load_data()
### normalize the images to pixel values (0, 1)
train_img, test_img = train_img/255.0, test_img/255.0
### Check the format of the data
print ("train_img, train_label types: ", type(train_img), type(train_label))
### check the shape of the data
print("the shape of images and labels array train:", train_img.shape, train_label.shape)
print("the shape of images and labels array test:", test_img.shape, test_label.shape)
### Check the distribution of unique elements
(unique, counts) = np.unique(train_label, return_counts=True)
frequencies = np.asarray((unique, counts)).T
print(frequencies)
print(unique)
class_type = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'] # cifar-10 label
plt.figure(figsize=(10, 10))
for i in range(12):
plt.subplot(4, 3, i+1)
plt.xlabel([])
plt.ylabel([])
plt.grid(False)
plt.imshow(train_img[i], cmap='gray')
plt.xlabel(class_type[train_label[i][0]], fontsize=13)
plt.tight_layout()
plt.show()
### 转成one-hot标签样式
train_label_categorical = tf.keras.utils.to_categorical(train_label, num_classes=10, dtype='uint8')
test_label_categorical = tf.keras.utils.to_categorical(test_label, num_classes=10, dtype='uint8')
### 将训练集划分成训练集和验证集
from sklearn.model_selection import train_test_split
train_img, valid_img, train_label, valid_label = train_test_split(
train_img, train_label_categorical, test_size=0.20,
stratify=train_label_categorical,
random_state=10, shuffle=True)
print("train data shape after the split: ", train_img.shape)
print("valid data shape: ", valid_img.shape)
print("valid labels shape: ", valid_label.shape)
### 搭建网络
# resnet50,残差块是1x1, 3x3, 1x1
# 有两种样式的残差块,一种树输入x与输出有相同的维度,另外一种需要借鉴于卷积核进行升降维保持维度一致
from tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, ZeroPadding2D, Flatten, BatchNormalization, AveragePooling2D, Dense, Activation, Add
from tensorflow.keras.models import Model
from tensorflow.keras import activations
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.regularizers import l2
batch_size = 64
train_dataGen = tf.keras.preprocessing.image.ImageDataGenerator(
zoom_range=0.2, width_shift_range=0.1, height_shift_range=0.1, horizontal_flip=True)
valid_dataGen = tf.keras.preprocessing.image.ImageDataGenerator()
train_set_conv = train_dataGen.flow(train_img, train_label, batch_size=batch_size)
valid_set_conv = valid_dataGen.flow(valid_img, valid_label, batch_size=batch_size)
# 1x1 3x3 1x1 conv->bn->relu
def res_identity(x, filters):
''' renet block where dimension doesnot change.
The skip connection is just simple identity conncection
we will have 3 blocks and then input will be added
'''
x_skip = x # this will be used for addition with the residual block
f1, f2 = filters
#first block
x = Conv2D(f1, kernel_size=(1, 1), strides=(1, 1), padding='valid', kernel_regularizer=l2(0.001))(x)
x = BatchNormalization()(x)
x = Activation(activations.relu)(x)
#second block # bottleneck (but size kept same with padding)
x = Conv2D(f1, kernel_size=(3, 3), strides=(1, 1), padding='same', kernel_regularizer=l2(0.001))(x)
x = BatchNormalization()(x)
x = Activation(activations.relu)(x)
# third block activation used after adding the input
x = Conv2D(f2, kernel_size=(1, 1), strides=(1, 1), padding='valid', kernel_regularizer=l2(0.001))(x)
x = BatchNormalization()(x)
# x = Activation(activations.relu)(x)
# add the input
x = Add()([x, x_skip])
x = Activation(activations.relu)(x)
return x
def res_conv(x, s, filters):
'''
here the input size changes, when it goes via conv blocks
so the skip connection uses a projection (conv layer) matrix
'''
x_skip = x
f1, f2 = filters
# first block
x = Conv2D(f1, kernel_size=(1, 1), strides=(s, s), padding='valid', kernel_regularizer=l2(0.001))(x)
# when s = 2 then it is like downsizing the feature map
x = BatchNormalization()(x)
x = Activation(activations.relu)(x)
# second block
x = Conv2D(f1, kernel_size=(3, 3), strides=(1, 1), padding='same', kernel_regularizer=l2(0.001))(x)
x = BatchNormalization()(x)
x = Activation(activations.relu)(x)
#third block
x = Conv2D(f2, kernel_size=(1, 1), strides=(1, 1), padding='valid', kernel_regularizer=l2(0.001))(x)
x = BatchNormalization()(x)
# shortcut
x_skip = Conv2D(f2, kernel_size=(1, 1), strides=(s, s), padding='valid', kernel_regularizer=l2(0.001))(x_skip)
x_skip = BatchNormalization()(x_skip)
# add
x = Add()([x, x_skip])
x = Activation(activations.relu)(x)
return x
### Combine the above functions to build 50 layers resnet.
def resnet50():
input_im = Input(shape=(train_img.shape[1], train_img.shape[2], train_img.shape[3])) # cifar10 img size
x = ZeroPadding2D(padding=(3, 3))(input_im)
# 1st stage
# here we perform maxpooling
x = Conv2D(64, kernel_size=(7, 7), strides=(2, 2))(x)
x = BatchNormalization()(x)
x = Activation(activations.relu)(x)
x = MaxPooling2D((3, 3), strides=(2, 2))(x)
# 2nd stage
# from here only conv block and identity block no pooling
x = res_conv(x, s=1, filters=(64, 256))
x = res_identity(x, filters=(64, 256))
x = res_identity(x, filters=(64, 256))
# 3rd stage
x = res_conv(x, s=2, filters=(128, 512))
x = res_identity(x, filters=(128, 512))
x = res_identity(x, filters=(128, 512))
x = res_identity(x, filters=(128, 512))
# 4rd stage
x = res_conv(x, s=2, filters=(256, 1024))
x = res_identity(x, filters=(256, 1024))
x = res_identity(x, filters=(256, 1024))
x = res_identity(x, filters=(256, 1024))
x = res_identity(x, filters=(256, 1024))
x = res_identity(x, filters=(256, 1024))
# 5rd stage
x = res_conv(x, s=2, filters=(512, 2048))
x = res_identity(x, filters=(512, 2048))
x = res_identity(x, filters=(512, 2048))
# ends with average pooling and dense connection
x = AveragePooling2D((2, 2), padding='same')(x)
x = Flatten()(x)
x = Dense(len(class_type), activation='softmax', kernel_initializer='he_normal')(x)
# define the model
model = Model(inputs=input_im, outputs=x, name='Resnet50')
return model
### Define some Callbacks
def lrdecay(epoch):
lr = 1e-3
if epoch > 180:
lr *= 0.5e-3
elif epoch > 160:
lr *= 1e-3
elif epoch >120:
lr *= 1e-2
elif epoch > 80:
lr *= 1e-1
return lr
lrdecay = tf.keras.callbacks.LearningRateScheduler(lrdecay) # learning rate decay
def earlystop(mode):
if mode == 'acc':
estop = tf.keras.callbacks.EarlyStopping(monitor='val_acc', patience=15, mode='max')
elif mode == 'loss':
estop = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=15, mode='min')
return estop
resnet50_model = resnet50()
resnet50_model.summary() # 查看网络结构
# resnet50_model.compile(loss='categorical_crossentropy', optimizer=Adam(learning_rate=1e-3), metrics=['acc']) # 不同版本的优化器学习率key不一样
resnet50_model.compile(loss='categorical_crossentropy', optimizer=Adam(lr=1e-3), metrics=['acc'])
batch_size=batch_size # test with 64, 128, 256
resnet_train = resnet50_model.fit(train_set_conv,
epochs=180,
steps_per_epoch=train_img.shape[0]/batch_size,
validation_steps=valid_img.shape[0]/batch_size,
validation_data=valid_set_conv,
callbacks=[lrdecay])
### Plot train and validation curves
loss = resnet_train.history['loss']
v_loss = resnet_train.history['val_loss']
acc = resnet_train.history['acc']
v_acc = resnet_train.history['val_acc']
epochs = range(len(loss))
fig = plt.figure(figsize=(9, 5))
plt.subplot(1, 2, 1)
plt.yscale('log')
plt.plot(epochs, loss, linestyle='--', linewidth=3, color='orange', alpha=0.7, label='Train Loss')
plt.plot(epochs, v_loss, linestyle='-.', linewidth=2, color='lime', alpha=0.8, label='Valid Loss')
plt.ylim(0.3, 100)
plt.xlabel('Epochs', fontsize=11)
plt.ylabel('Loss', fontsize=12)
plt.legend(fontsize=12)
plt.subplot(1, 2, 2)
plt.plot(epochs, acc, linestyle='--', linewidth=3, color='orange', alpha=0.7, label='Train Acc')
plt.plot(epochs, v_acc, linestyle='-.', linewidth=2, color='lime', alpha=0.8, label='Valid Acc')
plt.xlabel('Epochs', fontsize=11)
plt.ylabel('Accuracy', fontsize=12)
plt.legend(fontsize=12)
plt.tight_layout()
plt.savefig('./train_acc.png', dpi=250)
plt.show()
### 画出混淆矩阵
from sklearn.metrics import confusion_matrix, classification_report
import seaborn as sns
class_types = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
def conf_matrix(predictions):
'''
plots confusion matrix and classification report
'''
cm = confusion_matrix(test_label, np.argmax(np.round(predictions), axis=1))
print("Classification Report:\n")
cr = classification_report(
test_label, np.argmax(np.round(predictions), axis=1), target_names=[class_types[i] for i in range(len(class_types))])
print(cr)
plt.figure(figsize=(12,12))
sns_hmp = sns.heatmap(cm, annot=True, xticklabels=[class_types[i] for i in range(len(class_types))],
yticklabels=[class_types[i] for i in range(len(class_types))], fmt="d")
fig = sns_hmp.get_figure()
fig.savefig('./heatmap.png', dpi=250)
pred_class_resnet50 = resnet50_model.predict(test_img)
conf_matrix(pred_class_resnet50)
### 测试集上进行评测
## Check the performance on the test data
test_result = resnet50_model.evaluate(test_img, test_label_categorical, verbose=0)
print ("ResNet50 loss: ", test_result[0])
print ("ResNet50 accuracy: ", test_result[1])
参考
- https://zhuanlan.zhihu.com/p/27082562
- https://blog.csdn.net/C_chuxin/article/details/82948733
- https://blog.csdn.net/Jwenxue/article/details/107793294
- https://blog.csdn.net/weixin_26630173/article/details/108898927
- https://towardsdatascience.com/understand-and-implement-resnet-50-with-tensorflow-2-0-1190b9b52691
- https://github.com/suvoooo/Learn-TensorFlow/blob/master/resnet/Implement_Resnet_TensorFlow.ipynb