本文介绍 ResNet的背景,想要解决的问题,基本思路和框架 和对于其的一种解读方式。另外,和 Resnet 思路相反的是 Inception 系列。

沐神讲解 resnet

防止梯度消失/梯度爆炸

  • 初始化:不要太小也不要太大

  • BN

更大的网络并不是 overfitting,而是 train 和 test 时候网络都在变差。

如果浅层网络能做好,那么深层网络中新加的层从理论上说可以学成 identity mapping,至少不会变差。resnet 不会使得深层网络变得差。

residual connection

shortcut connection

经典的文章,不见得是原创,将之前的工作汇总一下,能够解决现在的某个新问题。

抄袭:最近的工作差不多,那叫做抄袭。如果 30年了,要客观一些,可以 to our best knowledge。

The image is resized with its shorter side ran- domly sampled in [256, 480] for scale augmentation

按照短边(shorter side) 进行resize

drop out 一般使用在全连接层中。(resnet 中没有使用 drop out,因为没有全连接)

flops:网路进行多少浮点运算。

lr 如果分成两段,那么开始的一段需要相对比较长一些,这样学习得更加充分一些(有积累?),然后学习率在降低。

res18,res34 是 plain 网络结构,没有 bottleneck

image-20220120113125277

当你的通道数不变的时候,是左图的连接方式;当通道数发生变化的时候,比如右图开始 256 维度,通过 $11$ 降维成 64,然后相加的时候,通过 $11$ 升维,还原成 256 的维度。

左右的两个算法复杂度差不多。

resnet50 使用 2048 维度作为向量的表示,从理论上可以做到更深。

Imagenet 图像大小在 300 以上

cifar 基本上是 32 *32 ,5w 样本

这个论文没有结论。(hhh)

(收敛 和 train 不动 是有区别的)使用 SGD 要保证梯度一直够大,这样才能一直优化 train 下去。

事后发现,resnet 训练比较快的原因:梯度保持得比较好。

image-20220120120116840

为什么 transformer 中的参数量那么大,然而还没有过拟合?

过拟合还和网络的结构有关,比如 resnet 中加上了残差,使得网络结构复杂度降低,其实过拟合就没有那么严重。找到一个不那么复杂的模型去拟合数据。

挖坑,理论有创新新或者实践效果好,只要有一点好,那么就可以很好的论文。如果是真的好,那么大家会 follow,然后把坑给填完的。

resnet 网络

(1)背景

网络的深度是容易出现梯度爆炸和梯度消失,造成网络的不收敛。一些方法已经在很大程度上可以缓解这个问题,比如使用 ReLU激活函数、 良好的权值初始化方法 、还有 intermediate normalization layers(即网络中间的batch normalization)。并且对于网络过程中的过拟合问题,也提出了一些办法如,使用 regularization、权值衰减和dropout方法。

但解决了深度网络收敛问题之后,又出现了另外一个问题。

(2)残差网络要解决的问题

一般来说在没有过拟合的情况下,可以逐步增加网络的深度。但在实验中发现了这样的问题。网络退化: 网络越深,训练误差越大。(accuracy开始饱和,原文中这样说的)这种退化并不是由于过拟合造成的,并且在适当深度模型中增加更多的层会导致更多的训练误差

(3)基本思路和结构

作者基于增加层如果为恒等映射那么更深层网络不应该比浅层网络产生更高错误率的思想

image-20210302165536275

如图所示左边的是传统的plain networks的结构,右边的是修改为ResNet的结构。 改变前目标: 训练 F(x) 逼近 H(x) 改变后目标:训练 F(x) 逼近 H(x) -x

即增加一个identity mapping(恒等映射),将原始所需要学的函数H(x)转换成F(x)+x,而作者认为这两种表达的效果相同,但是优化的难度却并不相同,作者假设F(x)的优化 会比H(x)简单的多。这一想法也是源于图像处理中的残差向量编码,通过一个reformulation,将一个问题分解成多个尺度直接的残差问题,能够很好的起到优化训练的效果

在论文中尝试了 skip 2层或者 3层

5.jpg

ResNet 中 BasicBlock 和 Bottleneck 的区别(左图是 BasicBlock,右图是 Bottleneck)

BasicBlock 是 ResNet 中的一种网络结构,包含了残差支路(short-cut 支路),相比于传统卷积网络结构该支路能用于传递底层的信息,使得网络能能够训练得更深。

Bottleneck 是由 $11$, $33$ 和 $11$ 三个卷积构成,其中 $11$ 卷积层作用是先减少再恢复维度,Bottleneck 用于特征降维,减少特征图的层数,减少参数量从而减少计算量。(一般在网络较深才使用 Bottleneck 结构,比如 Resnet18, Resnet34 用 BasicBlock,而 Resnet50, Resnet101 使用 Bottleneck)

一种解读方式

残差网络单元其中可以分解成右图的形式,从图中可以看出,残差网络其实是由多种路径组合的一个网络,直白了说,残差网络其实是很多并行子网络的组合,整个残差网络其实相当于一个多人投票系统(Ensembling)

4.png 从这可以看出其实ResNet是由大多数中度网络和一小部分浅度网络和深度网络组成的,

说明虽然表面上ResNet网络很深,但是其实起实际作用的网络层数并没有很深我们可以看出大多数的梯度其实都集中在中间的路径上,论文里称为effective path。 (网络越深,也是容易出现梯度消失或者梯度爆炸,这个是没有问题的)ResNet其实就是一个多人投票系统。

现在深度网络基本上分成两个方向,一个像 resnet 向着”深度“发展,一个向着”宽度“的inception network

(4)网路结构

以 pytorch 官方code 为例进行分析: https://github.com/pytorch/vision/blob/master/torchvision/models/resnet.py

resnet 家族中包括 resnet-18, resnet-34, resnet-50, resnet-101, resnet-152。可以发现都是在 baseblock 中通过增多 block 的数量实现的。

image-20210302172226490

如下是 basicBlock。conv $3*3$-BN-relu。

 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class BasicBlock(nn.Module):
    expansion: int = 1

    def __init__(
        self,
        inplanes: int,
        planes: int,
        stride: int = 1,
        downsample: Optional[nn.Module] = None,
        groups: int = 1,
        base_width: int = 64,
        dilation: int = 1,
        norm_layer: Optional[Callable[..., nn.Module]] = None
    ) -> None:
        super(BasicBlock, self).__init__()
        if norm_layer is None:
            norm_layer = nn.BatchNorm2d
        if groups != 1 or base_width != 64:
            raise ValueError('BasicBlock only supports groups=1 and base_width=64')
        if dilation > 1:
            raise NotImplementedError("Dilation > 1 not supported in BasicBlock")
        # Both self.conv1 and self.downsample layers downsample the input when stride != 1
        self.conv1 = conv3x3(inplanes, planes, stride)
        self.bn1 = norm_layer(planes)
        self.relu = nn.ReLU(inplace=True)
        self.conv2 = conv3x3(planes, planes)
        self.bn2 = norm_layer(planes)
        self.downsample = downsample
        self.stride = stride

    def forward(self, x: Tensor) -> Tensor:
        identity = x

        out = self.conv1(x)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)

        if self.downsample is not None:
            identity = self.downsample(x)

        out += identity
        out = self.relu(out)
        # 这里体现了 resnet 的核心思想, skip-connecting,直接把 identity 加入了两层 conv 之后 

        return out

同一个家族中的不同 resnet 网络是通过传入了不同的参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def resnet50(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet:
    r"""ResNet-50 model from
    `"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_.
    Args:
        pretrained (bool): If True, returns a model pre-trained on ImageNet
        progress (bool): If True, displays a progress bar of the download to stderr
    """
    return _resnet('resnet50', Bottleneck, [3, 4, 6, 3], pretrained, progress,
                   **kwargs)


def resnet101(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet:
    r"""ResNet-101 model from
    `"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_.
    Args:
        pretrained (bool): If True, returns a model pre-trained on ImageNet
        progress (bool): If True, displays a progress bar of the download to stderr
    """
    return _resnet('resnet101', Bottleneck, [3, 4, 23, 3], pretrained, progress,
                   **kwargs)

Inception 网络

如果 ResNet 是为了更深,那么 Inception 家族就是为了更宽。第一个见解与对层的操作有关。在传统的卷积网络中,每一层都会从之前的层提取信息,以便将输入数据转换成更有用的表征。

见解 1:为什么不让模型选择?

这种模型架构的信息密度更大了,这就带来了一个突出的问题:计算成本大大增加。不仅大型(比如 5×5)卷积过滤器的固有计算成本高,并排堆叠多个不同的过滤器更会极大增加每一层的特征映射的数量。而这种计算成本增长就成为了我们模型的致命瓶颈。

这就涉及到了见解 2:使用 1×1 卷积来执行降维。为了解决上述计算瓶颈,Inception 的作者使用了 1×1 卷积来「过滤」输出的深度。一个 1×1 卷积一次仅查看一个值,但在多个通道上,它可以提取空间信息并将其压缩到更低的维度。比如,使用 20 个 1×1 过滤器,一个大小为 64×64×100(具有 100 个特征映射)的输入可以被压缩到 64×64×20。通过减少输入映射的数量,Inception 可以将不同的层变换并行地堆叠到一起,从而得到既深又宽(很多并行操作)的网络。

Inception Net v3 incorporated all of the above upgrades stated for Inception v2, and in addition used the following:

  • RMSProp Optimizer.
  • Factorized 7x7 convolutions.
  • BatchNorm in the Auxillary Classifiers.
  • Label Smoothing (A type of regularizing component added to the loss formula that prevents the network from becoming too confident about a class. Prevents over fitting).

关于skip-connect 的理解: skip-connect 也就是和残差连接。

将输入和输入的非线性变化的线性叠加。解决的是深层网络的训练问题。

VGG网络

VGG stands for Visual Geometry Group (a group of researchers at Oxford who developed this architecture). The VGG architecture consists of blocks, where each block is composed of 2D Convolution and Max Pooling layers. VGGNet comes in two flavors, VGG16 and VGG19, where 16 and 19 are the number of layers in each of them respectively.

在 ImageNet 大赛中,VGG 在神经网络的深度方面进行了很好的探索。完全沿用了卷积加池化堆叠的设计思路。

和之前 AlexNet 结构相比,无论是 VGG16 还是VGG19,其卷积池化的层数都远远高于 AlexNet 网络。

VGG主要由输入层、卷积层、池化层以及全连接层组成。对应的输入是224×224彩色图像

卷积层:卷积核大小为3×3,步长设置为1,填充(padding)设置为1,并且所有隐藏层都使用ReLu作为激活函数。 池化层:每经过一次池化,图片面积变为原来的四分之一。每个池化层的池化核大小是2×2,步长为2,填充设置为0。 全连接层:VGG网络最后是三个全连接层,其中前两个全连接层均有4096个通道,第三个全连接层有1000个通道。

经过探索,发现16层和19层的结构效果是比较好的。所以我们现在使用最多的是VGG16和VGG19。

VGG的特点

  • 采用了较深的网络,最多达到19层,证明网络越深,从而准确率越高
  • 串联多个小卷积,相当一个大卷积。VGG 中使用两个串联的 3* 3卷积,达到了一个 5*5 卷积的效果,但是参数量却只有之前的 9/25。同时串联多个小卷积,增加了使用 relu 非线性激活的概率,从而增加了模型的非线性特征
  • VGG16 中使用了 1*1 的卷积操作。1x1的卷积是性价比最高的卷积,可以用来实现线性变化,输出通道变换等功能,而且还可以多一次relu非线性激活。

尽管VGG 在 ImageNet 中表现很好,但是将其部署到一个合适大小的GPU 上是困难的,因为需要VGG 在内存和时间上的要求很高。

1*1 卷积的好处

减少了参数,允许增加深度 ;可以降维。 Inception 块就是通过在 33 和 55 后面加入了 1*1 来减少计算; 增加了网络的表达能力。

  1. ResNet 网络

使用一张图表回顾一下深度网络的发展历程

可以发现,网络越深,特征的等级就越高,能够解决更加抽象复杂的问题。但是当网路深度超过 20层之后,网络就难以训练。因为整个深度学习是建立在反向传播理论上的,而反向传播理论天然的缺陷是梯度消失和梯度爆炸。为了解决这个问题,科学家提出了 ResNet 网络。

通过研究,加入 shortcut 的连接方式就可以解决

我们可以观察到ResNet网络结构把链式法则连乘的问题变成了连加,而且有一个信号能够一直跳过N多个Shorcut结构而不衰减,使得我们并不会因为反向传播而衰减信息。

大量的实验证明 ResNet 的结构解决了之前“层数越深,效果越差”的问题。从某种意义上讲, ResNet 使得深度变成了可能。从而使网路深度达到了 152层,但我们实际使用中 100多层就远远足够了