『Deep Residual Learning for Image Recognition』论文笔记

一 为什么读这篇

传说中的残差网络原文,何大神开始传奇之路的起点(当然之前的去雾也很牛逼,但是没这篇影响广泛和深远)。算是开创了CNN网络中的ResNet一派,另一派是google的Inception系列。之前读了很多其他人的解读,这次真正从原作出发,看看能吸收到什么知识点

二 截止阅读时这篇论文的引用次数

2018.11.16 14327次

三 相关背景介绍

2015年12月刊发于arXiv,彼时几个作者还在MSRA,一作何恺明现在在FAIR;二作Xiangyu Zhang是普渡大学的教授;三作任少卿从07年开始在中科大上本科,16年博士毕业,现在是Momenta的研发总监,也是Faster R-CNN的一作;四作孙剑大神现在是旷视的首席科学家。本文提出的ResNet在2015年的ImageNet各项比赛中都拿了第1名。也是2016年的CVPR最佳论文。

四 关键词

ResNet
identity mapping(恒等映射)
residual mapping(残差映射)
shortcut connections(捷径连接)

五 论文的主要贡献

1 提出了一个比以往都深的网络—ResNet(152层深度的ResNet是VGG的8倍,同时保持了一个很低的复杂度)

2 证实了越深越好的事实,解决了『plain net』训练不了深网络的问题

六 详细解读

1 介绍

已经有很多『深度』模型在视觉任务上表现良好。很自然的一个问题就是『一个学习良好的网络是否通过简单的堆叠更多的层就行了』。一个明显的障碍就是梯度消失,梯度爆炸问题。通过初始时的归一化和中间层的归一化(BN)极大的解决了这个问题。

退化问题:随着网络的深度增加,准确率开始饱和,然后急剧下降。但这种退化并不是由过拟合引起的,增加更多的层反而会导致更大的训练误差。

image

identity mapping(恒等映射):f(a) = a。其他人提出的一些恒等映射玩不转。

提出假设:residual mapping(残差映射)相比原始,未引用的映射更容易优化。

shortcut connections(捷径连接):跳过一个或多个层。

这个case里捷径连接只是做了简单的恒等映射,它即没有增加参数量也没增加计算复杂度

image

152层的残差网络是ImageNet上出现的最深的网络(2015年之前),比VGG的复杂度还要低。拿到了2015年ILSVR的各项比赛的第一。

2 相关工作

残差表示

VLAD和Fisher Vector都是残差向量浅层表示的代表。

这些方法表明好的reformulation或preconditioning能够简化优化。

捷径连接

有关捷径连接的理论和实践研究已经有一阵子了。

把highway networks拿出来批判一番,因为它的门函数是需要参数的,另外当门接近关闭时,高速网络的层表示非残差函数,而且没有证明层越深效果越好。

3 深度残差学习

3.1 残差学习

表示几个层的潜在映射

表示残差函数

表示显示的将这些层近似为残差函数

表示原始函数变为现在这样

如果增加的层能被构建为恒等映射,那么更深的模型的训练误差不应该高于浅层部分。

退化问题指出很难通过多个非线层来近似恒等映射。而通过残差学习的定义,如果恒等映射是最佳的,那么优化器将很容易让多个非线性层的权重趋于0,从而接近恒等映射。

实际场景中,恒等映射不太可能是最佳的,不过我们的定义对预处理这个问题也有帮助。如果优化函数是接近恒等映射而不是接近零值映射,将会使优化器很容易找到恒等映射的扰动,而不是去学习一个新的函数。

3.2 通过捷径连接的恒等映射

表示要学习的残差映射。

一个两层的例子。

捷径连接即没有增加参数也没有增加计算复杂度,因此也有利于进行『plain/redisual』网络的比较。

和的维度必须一致,当不一致时可以给乘一个方形矩阵以使维度一致

通常是2到3层,但有更多的层也是可行的。

不止可以用于全连接层,也可用于卷积层。

3.3 网络架构

Plain Network

从VGG学来的2个设计准则:

(1) 输出feature map大小一样的层,filters数量相同
(2) 如果每层的feature map大小减半,那么filters数量翻倍用以保留时间复杂度

相比VGG19,本文设计的plain network的FLOPs只有VGG19的18%,另外用全局平均池化层取代了VGG的2个全连接层。

Residual Network

当输入和输出维度一致时,直接用恒等捷径就可以了。

当输入和输出维度不一致时,有两种做法

​ A. 捷径仍然用恒等映射,用额外的0值填充增加的维度,这种方法不会引入额外的参数

​ B. 用投影捷径来匹配维度(通过1x1卷积完成)

3.4 实现细节

用Alexnet和vgg的实践方式。各种图像增强方式,图像输入大小为224x224

在卷积后,激活前加一个BN。

用He初始化参数。

用SGD,batchsize为256。

学习率从0.1开始,当遇到高原时除10。

模型训练迭代60w次。

用0.0001的weight decay,0.9的momentum。

不用dropout。

测试时平均多个图像尺寸(224,256,384,480,640)的分数来达到最佳效果。

4 实验

4.1 ImageNet

1000类的ImageNet2012,128w的训练集,5w的验证集。10w的测试集。评估指标为top-1和top-5错误率。

image
image
image

起初怀疑退化问题是由梯度消失引起的,但经过验证后发现并不是。因为前向传播由BN来保证不发生梯度消失,后向传播时也显式校验过没有梯度消失。

通过表2和图4有3点发现

1. 34层的resnet优于18层的resnet 2.8个百分点。更重要的是34层的训练误差更低,其泛化能力也更强,这说明通过增加深度从而提高准确率达成了,解决了退化问题
2. 34层的resnet比plain好3.5个百分点,说明残差学习在更深的网络中的有效性
3. 当网络不太深时(18层),plain/residual准确率差不多,但resnet收敛的更快

image
恒等和投影捷径的对比

A:维度不一致时用0补齐

B:维度不一致时用投影捷径

C:所有都用投影捷径

效果C好于B好于A,不过只有一点点差别,所以投影捷径并不是解决退化问题的关键。为了减小模型复杂度,本文后面并未用C

深度瓶颈架构

non-bottleneck也能通过增加深度提高准确率,但不如bottleneck划算。

用3层替代2层。3层分别为1x1,3x3,1x1卷积。1x1负责减小和增加维度,来让3x3的瓶颈有更小的输入输出维度。

对于瓶颈架构来说无参数的恒等捷径特别重要,如果用投影捷径的话,模型复杂度会翻倍。

ResNet-50/101/152

都用瓶颈架构

152层的ResNet(11.3billion FLOPs)比VGG16/19(15.3/19.6 billion FLOPs)的复杂度还低。

最后赢得2015ImageNet的是通过ensemble 6个不同深度的模型来实现的,top-5错误率为3.57

4.2 CIFAR10

5w训练集(4.5w/0.5w),1w测试集,10个类。32x32大小。

batch_size为128,总共迭代6.4w次。

这部分关注的重点是相当深的网络,而不是SOTA的结果。

每个图被看100多次(wyj)
imagenet 600000 * 256 / 1280000 = 120
cifar10 64000 * 128 / 45000 = 182

image

layer response?这是什么东东?

image

响应是3x3卷积层和BN层之后,激活函数前的输出。图7表明ResNet相对plain网络有更小的响应。这些结果也证明了残差函数相比于非残差函数更接近于0。同时发现更深的ResNet有更小的响应。

ResNet-1202不如ResNet-110。有可能是过拟合。

4.3 目标检测

都用Faster R-CNN,RestNet-101比VGG-16有很大幅的提高。

Faster R-CNN也是在ImageNet分类数据上初始化,然后在目标检测数据上fine-tuned。

七 读后感

后悔没有早点读ResNet这篇论文,没有想象中的那么难,反而介绍的十分清楚,感觉就像教科书一样,前因后果,一步一步都告诉你,同时对引用相关工作的介绍也非常到位,对于扩充知识面非常有帮助。实验部分也是极好的实践教材。

不知道为啥ImageNet的评估要用错误率而不直接用准确率,自己脑补的是错误率听起来好像更好听点,比方说即使是ResNet-152的top-1错误率是19.38,听着没什么,但换成准确率就是81%,听着感觉就好像不可用了。

ResNet的网络没怎么用pooling做下采样,而是用卷积做的(stride=2)

相比ImageNet,CIFAR就不需要那么多花活,做比赛的套路果然不一样

八 补充

关于ResNet已经有不少文章解读了,读完系列后,后续可以看看这个

https://www.cnblogs.com/liaohuiqiang/p/9606901.html

https://towardsdatascience.com/history-of-convolutional-blocks-in-simple-code-96a7ddceac0c

def residual_block(x, f=32, r=4):
    m = conv(x, f // r, k=1)
    m = conv(m, f // r, k=3)
    m = conv(m, f, k=1)
    return add([x, m])

你可能感兴趣的:(『Deep Residual Learning for Image Recognition』论文笔记)