公众号关注 “ML-CVer”

设为 “星标”,DLCV消息即可送达!

作者丨刘昕宸@知乎来源丨https://zhuanlan.zhihu.com/p/268308900编辑丨极市平台

导读

通过堆叠神经网络层数(增加深度)可以非常有效地增强表征,提升特征学习效果,但是会出现深层网络的性能退化问题,ResNet的出现能够解决这个问题。本文用论文解读的方式展现了ResNet的实现方式、分类、目标检测等任务上相比SOTA更好的效果。

论文标题:Deep Residual Learning for Image Recognition


1 motivation

通过总结前人的经验,我们常会得出这样的结论:通过堆叠神经网络层数(增加深度)可以非常有效地增强表征,提升特征学习效果。为什么深度的网络表征效果会好?
深度学习很不好解释,大概的解释可以是:网络的不同层可以提取不同抽象层次的特征,越深的层提取的特征越抽象。因此深度网络可以整合low-medium-high各种层次的特征,增强网络表征能力。那好,我们就直接增加网络深度吧!但是事情好像并没有那么简单!梯度优化问题:我们不禁发问:Is learning better networks as easy as stacking more layers?首先,深度网络优化是比较困难的,比如会出现梯度爆炸/梯度消失等问题。不过,这个问题已经被normalized initialization和batch normalization等措施解决得差不多了。退化问题:好,那就直接上deeper network吧!但是新问题又来了:deeper network收敛是收敛了,却出现了效果上的degradationdeeper network准确率饱和后,很快就退化了为什么会这样呢?网络更深了,参数更多了,应该拟合能力更强了才对啊!噢,一定是过拟合了。但似乎也不是过拟合的问题:


因为56-layer网络(红线)的training error(左图)也比20-layer网络(黄线)要高,这就应该不是过拟合了啊!那么究竟是什么原因导致了deeper network degradation问题呢?现在,我们换一种思路来构建deeper network:the added layers are identity mapping, and the other layers are copied from the learned shallower model.(在原始的浅层网络基础上增加的层视为是identity mapping)也就是假设浅层网络已经可以得到一个不错的结果了,那我接下来新增加的层啥也不干,只是拟合一个identity mapping,输出就拟合输入,这样总可以吧。这样的话,我们觉得:这样构建的深层网络至少不应该比它对应的浅层training error要高。对吧。但是实验又无情地表明:这样却又不能得到(与浅层网络)一样好的结果,甚至还会比它差!看来,深度网络的优化并不容易的!总结一下:直觉上深度网络应该会有更好的表征能力,但是事实却是深度网络结果会变差,由此我们认为深度网络的优化部分出了问题,深度网络的参数空间变得更复杂提升了优化的难度。那么,ResNet来了。我们就想啊,与其直接拟合一个desired underlying mapping ,不如让网络尝试拟合一个residual mapping 。也就是:原先的映射 ,被转换为了 我们在这里假设优化残差映射(residual mapping) 是比优化原映射 要容易的。
比如如果现在恒等映射(identity mapping)是最优的,那么似乎通过堆叠一些非线性层的网络将残差映射为0,从而拟合这个恒等映射,最种做法是更容易的。可以通过如上图所示的短路连接(shortcut connection)结构来实现。shortcut就是设计的可以skip几层的结构,在ResNet中就是起到了相当于一个最最简单的identity mapping,其输出被加到了stacked layers的输出上。这样做既没有增加新的参数,也没有增加计算复杂性。ResNet的具体结构,后面会详细介绍。接下来,本文在ImageNet和CIFAR-10等数据集上做实验,主要是想验证2个问题:

  1. deep residual nets比它对应版本的plain nets更好优化,training error更低。

  2. deep residual nets能够从更深的网络中获得更好的表征,从而提升分类效果。

2 solution

ResNet想做什么?learning residual functions with reference to the layer inputs, instead of learning unreferenced functions.理解不了没关系,接着往下看。

2.1 Residual Learning

前提:如果假设多个非线性层能够渐近一个复杂的函数,那么多个非线性层也一定可以渐近这个残差函数。令 表示目标拟合函数。所以与其考虑拟合 ,不如考虑拟合其对应的残差函数 .这两种拟合难度可能是不同的。回到上面的讨论,如果被增加层能够被构建成identity mapping,那么深层网络的性能至少不应该比其对应的浅层版本要差。这个表明:网络是在使用多层非线性网络趋近identity mapping做优化这里出了问题。残差学习的这种方式,使得“如果identity mapping是最优的,网络的优化器直接将残差学习为0”就可以了,这样做是比较简单的。但其实在真实情况下,identity mapping不一定是最优的映射啊!原文说这种设计仍有意义,这种设计help to precondition the problem.也就是如果optimal function更接近identity mapping的话,优化器应该能够比较容易找到残差,而不是重新学习一个新的。


后面实验也表明了:残差网络各层输出的标准差是比较低的(如上图,后面会解释),这也验证了在identity mapping的基础上学习残差,确实是会更容易(identity mappings provide reasonable preconditioning.).这里解释得还是比较含糊,但总结来说就是作者想解释,训练学习残差会有效降低学习的难度,可能据此来解决深层网络的性能退化问题。

2.2 Identity Mapping by Shortcuts

再回顾一下这个著名的残差块图片:identity mapping实现得非常之简单,直接就用了个shortcut形式化就是:表示residual mapping,比如上图,实际上就是2层网络,也就是 然后直接将 与 element-wise相加。最后,给 套一个激活函数 .这么设计(shortcut)有个巨大的好处,就是没有引入任何新的参数,也没有增加计算复杂度。下面还有2个小问题:问题1:关于 因为 是element-wise相加,那么如果 和 维度不一样怎么办?方案一:直接对 补0.方案二:增加一个网络层(参数为 ),改变 的维度。即:事实上,每个shortcut我们都可以加一个映射层 (实现起来可以就是个感知机)。不需要做维度转化时, 就是个方阵。但是后面实验表明,直接shortcut就已经足够好了,不需要再加那么多参数浪费计算资源。问题2:关于 的结构应该是什么样的呢?可以是2层或者3层,也可以是更多;但是不要是1层,效果会不好。最后,shortcut设计不仅针对全连接网络,卷积网络当然也是没问题的!

2.3 网络架构


受VGGNet(左边)启发,设计了34层的plain network(中间),以及其对应的residual network版本(右边)。注意:中间plain network和右边residual network层数一致,网络参数也可以设计得完全一样(element-wise相加维度不match时直接补0就不会增加任何learnable parameters)。34-layer plain network设计原则(遵循VGGNet):

  1. for the same output feature map size, the layers have the same number of filters

  2. if the feature map size is halved, the number of filters is doubled so as to preserve the time complexity per layer

3 dataset and experiments

3.1 ImageNet on Classification3.1.1 与plain network的对比实验这个实验是核心,为了说明residual network能够非常完美地解决“深度增加带来的degradation”问题!!!


左边是plain network,右边是ResNet;细线是train error,加粗线是val errorPlain network会出现网络的层数增加,train error和val error都会升高什么原因呢???
首先排除过拟合,因为train error也会升高
其次排除梯度消失,网络中使用了batch normalization,并且作者也做实验验证了梯度的存在
事实上,34-layers plain network也是可以实现比较好的准确率的,这说明网络在一定程度上也是work了的。
作者猜测:We conjecture that the deep plain nets may have exponentially low convergence rates. 层数的提升会在一定程度上指数级别影响收敛速度。下面是Residual Network与plain network的量化对比:观察上面两张图,我们可以得出结论:

  1. 而ResNet却真正实现了网络层数增加,train error和val error都降低了,证明了网络深度确实可以帮助提升网络的性能。degradation problem在一定程度上得到了解决。

  2. 相对于plain 34-layers,ResNet 34-layers的top-1 error rate也降低了3.5%。resnet实现了在没有增加任何参数的情况下,获得了更低error rate,网络更加高效。

  3. 从plain/residual 18-layers的比较来看,两者的error rate差不多,但是ResNet却能够收敛得更快。

总结来说就是,ResNet在不增加任何参数的情况下,仅使用shortcuts and zero-padding for matching dimensions结构,就实现了:解决了degradation problem,更高的准确率,更快的收敛速度简直太强了!!!

3.1.2 Identity v.s. Projection shortcuts

所谓projection shortcuts,就是:shortcuts包括了一个可学习参数(可以用来对齐维度,使得element-wise相加可以实现):设计了A,B,C三种实验:A:单纯使用identity shortcuts:维度不能对齐时使用zero padding来提升维度此方案没有增加任何参数B:仅仅在维度需要对齐时使用projection shortcuts,其余均使用parameter-free的identity shortcutsC:全部使用projection shortcuts下面是三种方案的实验结果:ABC三种方案均明显好于plain版本C虽然结果稍微优于B、C,但是却引入了大量的参数,增加了时空计算复杂度。作者认为:projection shortcuts are not essential for addressing the degradation problem.因此后面的实验仍然采用A或者B结构。

3.1.3 Deeper Bottleneck Architectures.

为了探索更深层的网络,保证训练时间在可控范围内,作者重又设计了bottleneck版本的building block左边是原版本,右边是bottleneck版本bottleneck版本是将卷积核换成了1*1,3*3,1*1的size,虽然层数增加到了3层,但是降低了参数量。
作者在这里是想探索深度的真正瓶颈,而不是追求很低的error rate,因此在这里使用了更加精简的bottleneck building block50-layers:将34-layers的每个2-layer block换成了3-layers bottleneck block101-layers/152-layers:增加更多的3-layers bottleneck block网络具体参数可参考如下图:


实验结果如下所示:网络越深,确实取得了更好的结果。Plain network的degradation problem似乎消失了。

3.2 CIFAR-10实验与分析


实线是test error,虚线是train error左边是plain network,中间是ResNet,右边是ResNet with 110 layers and 1202 layers.结论基本与之前一致,但在1202层时,ResNet还是出现了degradation现象(结果比110层差),作者认为是过拟合。另外:Analysis of Layer Responses


关于response:The responses are the outputs of each layer, after BN and before other nonlinearity (ReLU/addition).从上图我们可以直接看出:ResNet较于plain network,一般来说response std更小。并且:deeper ResNet has smaller magni- tudes of responses这就说明了:

  1. residual functions(即  ) might be generally closer to zero than the non-residual functions.

  2. When there are more layers, an individual layer of ResNets tends to modify the signal less.(也就是后面逐渐就接近identity mapping,要拟合的残差越来越小,离目标越来越近)

4 code review

ResNet实现非常简单,网上各种实现多如牛毛,这里仅随意找了个实现版本作为对照:代码基于CIFAR-10的:2层的BasicBlock:

class BasicBlock(nn.Module):    expansion = 1

def __init__(self, in_planes, planes, stride=1, option='A'):super(BasicBlock, self).__init__()self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(planes)self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)self.bn2 = nn.BatchNorm2d(planes)

self.shortcut = nn.Sequential()if stride != 1 or in_planes != planes:if option == 'A':"""                For CIFAR10 ResNet paper uses option A.                """self.shortcut = LambdaLayer(lambda x:                                            F.pad(x[:, :, ::2, ::2], (0, 0, 0, 0, planes//4, planes//4), "constant", 0))elif option == 'B':self.shortcut = nn.Sequential(                     nn.Conv2d(in_planes, self.expansion * planes, kernel_size=1, stride=stride, bias=False),                     nn.BatchNorm2d(self.expansion * planes)                )

def forward(self, x):        out = F.relu(self.bn1(self.conv1(x)))        out = self.bn2(self.conv2(out))        out += self.shortcut(x)        out = F.relu(out)return out

ResNet骨架:解释一下:forward函数中定义resnet骨架:

  1. 首:1层conv

  2. 身:由BasicBlock构成layer1、layer2、layer3,个数分别为  ,因为每个BasicBlock有2层,所以总层数是 

  3. 尾:1层fc

所以总共有 层!layer1, layer2, layer3输出维度分别是16,32,64

class ResNet(nn.Module):def __init__(self, block, num_blocks, num_classes=10):super(ResNet, self).__init__()self.in_planes = 16

self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False)self.bn1 = nn.BatchNorm2d(16)self.layer1 = self._make_layer(block, 16, num_blocks[0], stride=1)self.layer2 = self._make_layer(block, 32, num_blocks[1], stride=2)self.layer3 = self._make_layer(block, 64, num_blocks[2], stride=2)self.linear = nn.Linear(64, num_classes)

self.apply(_weights_init)

def _make_layer(self, block, planes, num_blocks, stride):        strides = [stride] + [1]*(num_blocks-1)        layers = []for stride in strides:            layers.append(block(self.in_planes, planes, stride))self.in_planes = planes * block.expansion

return nn.Sequential(*layers)

def forward(self, x):        out = F.relu(self.bn1(self.conv1(x)))        out = self.layer1(out)        out = self.layer2(out)        out = self.layer3(out)        out = F.avg_pool2d(out, out.size()[3])        out = out.view(out.size(0), -1)        out = self.linear(out)return out

最后,像堆积木一样,通过设置layer1、layer2、layer3的BasicBlock个数来堆出不同层的ResNet:

def resnet20():return ResNet(BasicBlock, [3, 3, 3])

def resnet32():return ResNet(BasicBlock, [5, 5, 5])

def resnet44():return ResNet(BasicBlock, [7, 7, 7])

def resnet56():return ResNet(BasicBlock, [9, 9, 9])

def resnet110():return ResNet(BasicBlock, [18, 18, 18])

def resnet1202():return ResNet(BasicBlock, [200, 200, 200])

5 conclusion

ResNet核心就是residual learning和shortcut identity mapping,实现方式极其简单,却取得了极其好的效果,在分类、目标检测等任务上均是大比分领先SOTA,这种非常general的创新是非常不容易的,这也是ResNet备受推崇的原因吧!另外给我的启示就是:不仅仅是"talk is cheap, show me the code"了,而是"code is also relatively cheap, show me ur sense and thinking"!

推荐阅读:

推荐!CV预训练模型全集!

国内外优秀的计算机视觉团队汇总|最新版

YOLOv4中的数据增强

resnet过拟合_重读经典:完全解析特征学习大杀器ResNet相关推荐

  1. 重读经典:完全解析特征学习大杀器ResNet

    作者丨刘昕宸@知乎 来源丨https://zhuanlan.zhihu.com/p/268308900 编辑丨极市平台 论文标题:Deep Residual Learning for Image Re ...

  2. 重读经典:完全解析特征学习大杀器 ResNet

    点上方蓝字计算机视觉联盟获取更多干货 在右上方 ··· 设为星标 ★,与你不见不散 仅作学术分享,不代表本公众号立场,侵权联系删除 转载于:作者丨刘昕宸@知乎 来源丨https://zhuanlan. ...

  3. 重读经典:C和指针学习笔记

    重读经典:<C和指针>学习笔记 <C和指针>. <C专家编程>. <C陷阱和缺陷>并称c语言三本经典著作,笔者在许多年前囫囵吞枣读完了这三本经典,然后把 ...

  4. 硬解析优化_解析!解析!598元的山灵动圈耳机大杀器,横评对比心慌慌

    山灵自千元价位推出超值感三动铁耳机AE3之后,又很快在中端价位放出一款动圈耳机--ME80,目前官方售价598元. 下面谈谈: 包装如上,一如既往的精美和国际化. 看盒子上的图都能发现体积比曾经ME1 ...

  5. 获取返回值作为变量_解决多线程间共享变量线程安全问题的大杀器——ThreadLocal...

    微信公众号:Zhongger 我是Zhongger,一个在互联网行业摸鱼写代码的打工人! 关注我,了解更多你不知道的[Java后端]打工技巧.职场经验等- 上一期,讲到了关于线程死锁.用户进程.用户线 ...

  6. python列表删除重复项_五分钟学会三种Excel重复项删除方法,工作效率大杀器!...

    点击蓝字 关注我们 在统计数据过程中, 同一份数据可能由于渠道的不同而进行了多次统计, 在输入数据时, 可能因为操作失误重复输入数据.种种原因造成数据表中的数据存在重复现象, 删除重复数据是数据清洗的 ...

  7. openfire消息通知推送_微信小游戏内测「订阅消息」能力,这是召回用户的「大杀器」吗?...

    作者:蒋鸿昌 本文来源于「知晓程序」公众号.知晓云后端云服务,让你的小程序开发快人一步,添加「minsupport3」了解详情. 知晓云​cloud.minapp.com 一位投资人曾把最近 3 年微 ...

  8. xcode修改时间后就要重新编译_[NewLife.XCode]反向工程(自动建表建库大杀器)

    NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netstandard,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode. 整个系列教程会大量结合示 ...

  9. ae制h5文字动画_大杀器Bodymovin和Lottie:把AE动画转换成HTML5/Android/iOS原生动画

    前段时间听部门老大说,Airbnb出了个移动端的动画库Lottie,可以和一个名叫Bodymovin的AE插件结合起来,把在AE上做好的动画导出为json文件,然后以Android/iOS原生动画的形 ...

最新文章

  1. leetcode算法题--回文链表
  2. 116-数学运算符更多的使用
  3. db2 空值转换函数_Excel一键转换百分比
  4. 整理了 25 个前端相关的学习网站和一些靠谱的小工具,都来看看吧
  5. promise的理解和应用
  6. 学习python_day2
  7. 快速计算子网掩码和主机块
  8. abp vnext token失效时间设置
  9. bcc语料库下载_BCC语料库使用指南
  10. [ROM制作教程] 【自制ROM工具大集合】各种修改制作ROM工具软件详解以及运用全集合
  11. 第一章 C语言与内存
  12. 红外热成像仪测温模块简要介绍说明
  13. java使用freemarker实现word下载
  14. MATLAB打开nc文件并读取nc文件数据
  15. Java学习笔记 算法 Algorithms Fourth Edition
  16. 实地审核和系统审核_实地审核及调查
  17. MySQL:索引测试大全
  18. 2022-2028年中国网红电商行业市场调查研究及投资策略研究报告
  19. 大数据时代背景下的文物数据资源
  20. 几月份跳槽或者换工作比较好?

热门文章

  1. python中函数的定义实例_Python基础之函数的定义与使用实例
  2. r语言 林元震_科学网—R语言简介 - 林元震的博文
  3. 1046 划拳 PAT乙级(C++)
  4. 【渝粤教育】国家开放大学2018年春季 7407-22T药物治疗学(本) 参考试题
  5. 【渝粤题库】陕西师范大学600001物理化学(上) 作业(专升本)
  6. linux rcu机制,Linux RCU机制详解 (透彻)
  7. Java锤子剪刀布大家应该都会玩“锤子剪刀布”的游戏: 现给出两人的交锋记录,请统计双方的胜、平、负次数,并且给出双方分别出什么手势的胜算最大。
  8. js微信小程序页面左上角返回跳转指定页面
  9. c语言ue,小话C语言中的cotinue和break
  10. cordic ip核 vivado_Xilinx Vivado CORDIC IP求解atan 反正切