Loading...

参考链接


1.abstract

1.0 摘要,论文导读
摘要主要内容:
  深度神经网络很难训练,我们使用residual(残差结构)使得网络训练比之前容易很多在ImageNet上使用了152层的ResNet,比VGG多8倍,但是计算复杂度更低,最终赢下了ImageNet2015的分类任务第一名,并演示了如何在cifar-10上训练100-1000层的网络。(通常赢下ImageNet比赛且提出很不一样网络架构、方法的文章会被追捧。)
  对很多任务来说,深度是非常重要的。我们仅仅是把之前的网络换成残差网络,在coco数据集上就得到了28%的改进。同样也赢下了ImageNet目标检测、coco目标检测和coco segmentation的第一名。

上面这张图是没有使用残差结构的网络,更深的层训练误差比浅层更高,即深层网络其实是训练不动的。下面这张图,是是否使用resnet结构的网络效果对比图。可以看到右侧使用残差结构后,34层的网络训练和测试的误差都更低。

Layers\Model Plain ResNet
18 layers 27.94 27.88
34 layers 28.54 25.03


2.导论

2.1 为什么提出残差结构

深度卷积神经网络是非常有效的,因为可以堆叠很多层,不同层可以表示不同level的特征。但是学一个好的网络,就是简简单单的把所有网络堆在一起就行了吗?如果这样,网络做深就行了。

我们知道,网络很深的时候,容易出现梯度消失或者梯度爆炸,解决办法之一是一个好的网络权重初始化,使权重不能太大也不能太小;二是加入一些normalization,比如BN。这样可以校验每个词之间的输出,以及梯度的均值和方差,这样比较深的网络是可以训练的(可以收敛)。但同时有一个问题是,深层网络性能会变差,也就是精度会变差。

深层网络性能变差,不是因为网络层数多、模型变复杂而过拟合,因为训练误差也变高了。那为什么会这样呢?从理论上来说,往一个浅层网络中加入一些层,得到一个深一些的网络,后者的精度至少不应该变差。因为后者至少可以学成新加的层是identity mapping,而其它层直接从前者复制过来。但是实际上做不到,SGD优化器无法找到这个比较优的解。

1
identity mapping可以理解成恒等映射吧,也就是网络输入x,输出也是x。网络权重简单学成输入特征的1/n。

所以作者提出,显式地构造一个identity mapping,使得深层模型的精度至少不会变得更差。作者将其称为deep residual learning framework

假设我们要学的是 H(x),在原有层上添加一些新的层时,新的层不是直接学 H(x),而是学习 H(x) - x,这部分用 F(x) 表示。(其中,x 是原有层的输出。)即,新加入的层不用全部重新学习,而是学习原来已经学习到的 x 和真实的 H(x) 之间的残差就行。最后模型的输出是 F(x) + x。这种新加入的层就是residual,结构如下图所示:

F(x) + x 在数学上就是直接相加,在神经网络中是通过shortcut connections实现(shortcut就是跳过一个或多个层,将输入直接加到这些跳过的层的输出上)。shortcut其实做的是一个identity mapping(恒等映射),而且这个操作不需要学习任何参数,不增加模型的复杂度。就多了一个加法,也不增加计算量,网络结构基本不变,可以正常训练。


2.2 实验验证

接下来作者在imagenet上做了一系列实验进行验证。结果表明,加了残差的网络容易优化,而且网络堆的更深之后,精度也会提高,所以赢下了比赛。在cifar-10上,作者尝试了训练超过1000层的网络。至此,论文的核心就讲完了,下面就是ResNet网络的设计。


3.相关工作

ResNet并不是第一个提出residual的概念。 最早的线性模型的解法就是通过不断迭代residual来求解的。在机器学习中,GBDT通过残差residual不断学习,把弱分类器叠加起来,形成强分类器。不同之处在于,GBDT是在label上做残差,而ResNet是在特征上做残差。

ResNet也不是第一个提出shortcut的。 比如在highway networks中就已经使用了shortcut,但其实现方式更为复杂,不仅仅是简单的加法。

一篇文章之所以成为经典,并不一定是因为它原创性地提出了许多新概念。有时,它的经典之处在于将多个已有概念巧妙地结合在一起,从而有效解决问题。甚至有时大家都可能忘记了之前有谁做过类似的工作。许多想法可能早已被前人提出并发表,但重要的是,这些想法可以被用来解决新的问题,使得旧技术在新的应用中展现出新的意义。

ResNet34比起VGG19,计算复杂度更低,只有前者的18%。其它是一些训练的细节,学习率优化器等等之类,就不细讲了。


4.实验部分

4.1 不同配置的ResNet结构

  • 网络输入是ImageNet图像,短边在[256,480]中随机选取,然后resize到224×224尺寸,输入网络。

  • conv2_x:表示第二个卷积模块,x表示模块里有很多层。

  • [3 × 3, 64]
    [3 × 3, 64] × 3: []内的是一个残差块,其卷积核大小为3*3,channel=64。×3表示有两个这样的残差层。

ResNet34结构图:(3+4+6+3)=16个残差模块,每个模块两层卷积层。再加上第一个7×7卷积层和最后一个全连接层,一共是34层。


4.2 残差结构效果对比

从下图可以看到有残差模块,网络收敛会更快,而且精度会更好:


4.3 残差结构中,输入输出维度不一致如何处理

  • A. pad补0,使维度一致;
  • B. 维度不一致的时候,使其映射到统一维度,比如使用全连接或者是CNN中的1×1卷积(输出通道是输入的两倍)。
  • C. 不管输入输出维度是否一致,都进行投影映射。下面作者对这三种操作进行效果验证。从下面结果可以看到,B和C效果差不多,都比A好。但是做映射会增加很多复杂度,考虑到ResNet中大部分情况输入输出维度是一样的(也就是4个模块衔接时通道数会变),作者最后采用了方案B


4.4 深层ResNet引入瓶颈结构Bottleneck

在ResNet-50及以上的结构中,模型更深了,可以学习更多的参数,所以通道数也要变大。比如前面模型配置表中,ResNet-50/101/152的第一个残差模块输出都是256维,增加了4倍。

如果残差结构还是和之前一样,计算量就增加的太多了(增加16倍),划不来。所以重新设计了Bottleneck结构,将输入从256维降为64维,然后经过一个3×3卷积,再升维回256维。这样操作之后,复杂度和左侧图是差不多的。这也是为啥ResNet-50对比ResNet-34理论计算量变化不大的原因。(实际上1×1卷积计算效率不高,所以ResNet-50计算还是要贵一些)


5.代码实现

resnet中残差块有两种:(use_1x1conv=True/False):

  • 步幅为2 ,高宽减半,通道数增加。所以shortcut连接部分会加一个1×1卷积层改变通道数
  • 步幅为1,高宽不变

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l

class Residual(nn.Module): #@save
def __init__(self, input_channels, num_channels,
use_1x1conv=False, strides=1):
super().__init__()
self.conv1 = nn.Conv2d(input_channels, num_channels,
kernel_size=3, padding=1, stride=strides)
self.conv2 = nn.Conv2d(num_channels, num_channels,
kernel_size=3, padding=1)
if use_1x1conv:
self.conv3 = nn.Conv2d(input_channels, num_channels,
kernel_size=1, stride=strides)
else:
self.conv3 = None
self.bn1 = nn.BatchNorm2d(num_channels)
self.bn2 = nn.BatchNorm2d(num_channels)#每个bn都有自己的参数要学习,所以需要定义两个

def forward(self, X):
Y = F.relu(self.bn1(self.conv1(X)))
Y = self.bn2(self.conv2(Y))
if self.conv3:
X = self.conv3(X)
Y += X
return F.relu(Y)

6.结论

ResNet就是在CNN主干上加了残差连接,这样如果新加的层训练效果不好的话,至少可以fallback变回简单模型,所以精度不会变差。
  
在现在来看,ResNet训练的比较快,是因为梯度保持的比较好。因为新加的层容易导致梯度消失(或者梯度爆炸),但是加了残差连接,梯度多了一部分,包含了之前层的梯度,这样不管加了多深,梯度会保持的比较大(主要是不会梯度消失,学不动),不会太快收敛,SGD跑得多就训练的比较好。(SGD的精髓就是,只要梯度比较大,就可以一直训练。反正有噪音,慢慢的总是会收敛,最后效果就会比较好)

为什么在cifar-10这样一个小的数据集上(32*32图片5w张)训练1202层的网络,过拟合也不是很厉害。为何transformer那些模型几千亿的参数不会过拟合,李沐认为是加了残差连接之后,模型内在复杂度大大降低了。(理论上模型加一些层,模型也至少可以将后面的层学成恒等映射,使精度不会变差。但实际上没有引导做不到这一点。所以本文才会显示的把残差结构加进去,使模型能够更容易的训练出来。比如后面层都是0,前面一些层才学到东西,也就是更容易训练出一个简单模型来拟合数据,所以加入残差连接等于是模型复杂度降低了)