ResNet@CV
深度残差网络(ResNet)
深度网络的退化问题
对于传统的深度学习网络应用来说,我们都有这样的一直觉,那就是网络越深能学到的东西越多,性能越好。到了2014年的时候,网络深度已经到了22层。
并不一定总是这样。在加深网络层数的过程中是会观测到一些出乎意料的现象。例如在CIFAR-10项目上使用56层的 $3 \times 3 $ 的卷积核的网络时候,实验发现深度网络出现了退化问题(Degradation problem),其错误率无论在测试集还是在训练集上,都高于20层的卷积网络,这意味着深层网络反而泛化能力低于浅层网络,通常来说,为了让网络学到更多的东西,是可以通过增加网络深度来让网络学习到更高维的特征,但眼前的事实比预期糟糕很多。这种现象并不是过拟合,因为其在训练集表现也比浅层网络差。网络深度越深,很容易出现梯度消失和梯度爆炸的问题,一般更容易出现梯度消失的问题。
随着网络的深度增加,误差在反向传播的过程中,梯度会进行层层叠加相乘,一个小于1的数(或者大于1)在经过几十层的指数叠加后很小(很大)。0.8的150次方约为 $2.9 \times 10^{-15}$。
臭名昭著的梯度消失和梯度爆炸问题已经通过提出的标准初始化(如 Xavier, Msra)和中间层标准化(BN)解决. 退化问题也不是由过拟合造成, 因为在训练集上, 深层网络的性能就不如浅层网络.
退化问题表明并非所有系统都同样易于优化, 推测退化问题可能是由于深度传统卷积网络的收敛速度可能呈指数级低, 当前的计算能力没法等到它收敛
残差学习
深度网络的退化问题至少说明深度网络不容易训练。但是我们考虑这样一个事实:现在你有一个浅层网络,你想通过向上堆积新层来建立深层网络,一个极端情况是这些增加的层什么也不学习,仅仅复制浅层网络的特征,即这样新层是恒等映射(Identity mapping)。在这种情况下,深层网络应该至少和浅层网络性能一样,也不应该出现退化现象。好吧,你不得不承认肯定是目前的训练方法有问题,才使得深层网络很难去找到一个好的参数。
让我们先思考一个问题:对神经网络模型添加新的层,充分训练后的模型是否只可能更有效地降低训练误差?理论上,原模型解的空间只是新模型解的空间的子空间。
也就是说,如果我们能将新添加的层训练成恒等映射$f(x)=x$,新模型和原模型将同样有效。
让我们聚焦于神经网络局部。如图所示,设输入为$\boldsymbol{x}$。假设我们希望学出的理想映射为$f(\boldsymbol{x})$,从而作为图上方激活函数的输入。左图虚线框中的部分需要直接拟合出该映射$f(\boldsymbol{x})$,而右图虚线框中的部分则需要拟合出有关恒等映射的残差映射$f(\boldsymbol{x})-\boldsymbol{x}$。残差映射在实际中往往更容易优化。以本节开头提到的恒等映射作为我们希望学出的理想映射$f(\boldsymbol{x})$。我们只需将图5.9中右图虚线框内上方的加权运算(如仿射)的权重和偏差参数学成$0$,那么 $f(\boldsymbol{x})$ 即为恒等映射。实际中,当理想映射 $f(\boldsymbol{x})$ 极接近于恒等映射时,残差映射也易于捕捉恒等映射的细微波动。右图也是ResNet的基础块,即残差块(residual block)。在残差块中,输入可通过跨层的数据线路更快地向前传播。
本来一个由2层网络组成的映射关系,可以用称之为$f(x)$的这样一个期望函数来拟合,而现在我们期望用$h(x)=f(x)+x$来拟合, 其中只需要学习残差$f(x)$。当残差为0时,此时堆积层仅仅做了恒等映射,至少网络性能不会下降,实际上残差不会为0,这也会使得堆积层在输入特征基础上学习到新的特征,从而拥有更好的性能。
ResNet沿用了VGG全 $3\times 3$卷积层的设计。残差块里首先有2个有相同输出通道数的 $3\times 3$卷积层。每个卷积层后接一个批量归一化层和ReLU激活函数。然后我们将输入跳过这两个卷积运算后直接加在最后的ReLU激活函数前。这样的设计要求两个卷积层的输出与输入形状一样,从而可以相加。如果想改变通道数,就需要引入一个额外的 $1\times 1$卷积层来将输入变换成需要的形状后再做相加运算。
残差学习的结构如图所示。这有点类似与电路中的“短路”,所以是一种短路连接(shortcut connection)。
为什么残差学习相对更容易,从直观上看残差学习需要学习的内容少,因为残差一般会比较小,学习难度小点。不过我们可以从数学的角度来分析这个问题,首先残差单元可以表示为:
$$ y_{l}=h\left(x_{l}\right)+F\left(x_{l}, W_{l}\right) $$
$$x_{l+1}=f\left(y_{l}\right) $$
其中$x_l$和$x_{l+1}$ 分别表示的是第 $l$个残差单元的输入和输出,注意每个残差单元一般包含多层结构。 $F$是残差函数,表示学习到的残差,而 $h(x_l)=x_l$ 表示恒等映射,$f$是ReLU激活函数。基于上式,我们求得从浅层 $l$到深层 $L$的学习特征为:
$$x_{L}=x_{l}+\sum_{i=l}^{L-1} F\left(x_{i}, W_{i}\right)$$
利用链式规则,可以求得反向过程的梯度:
$$\frac{\partial l o s s}{\partial x_{l}}=\frac{\partial \operatorname{loss}}{\partial x_{L}} \cdot \frac{\partial x_{L}}{\partial x_{l}}=\frac{\partial \operatorname{loss}}{\partial x_{L}} \cdot\left(1+\frac{\partial}{\partial x_{L}} \sum_{i=l}^{L-1} F\left(x_{i}, W_{i}\right)\right)$$
式子的第一个因子 $\frac{\partial \log s}{\partial x_{L}}$表示的损失函数到达 $L$的梯度,小括号中的1表明短路机制可以无损地传播梯度,而另外一项残差梯度则需要经过带有weights的层,梯度不是直接传递过来的。残差梯度不会那么巧全为-1,而且就算其比较小,有1的存在也不会导致梯度消失。所以残差学习会更容易。要注意上面的推导并不是严格的证明。
得注意的地方,后面这项$$ 1+\frac{\partial \sum_{i=1}^{L-1} F\left(x_{i}\right)}{\partial x_{l}} $$它可以使得$\frac{\partial \operatorname{loss}}{\partial x_{L}}$ 到 $\frac{\partial \operatorname{loss}}{\partial x_{l}}$是一个线性叠加的过程而非连乘,所以它自然也不太可能出现梯度消失现象。这些就是从数学推导层面来解释为什么深度残差网络的深度可以允许那么深,并且还没有出现令人恐惧的梯度消失问题和训练效率问题。
ResNet网络结构
为了保证element-wise 运算:
$$ y_{l}=h\left(x_{l}\right)+F\left(x_{l}, W_{l}\right) $$
必须保证恒等映射的$x$维度和残差函数$F(x)$的输出维度必须一致(不能出现维度衰减、通道衰减等)。
下图为VGGNet-19,以及一个34层深的普通卷积神经网络,和34层深的ResNet网络的对比图,传统的卷积层或全连接层在信息传递时,或多或少会存在信息丢失、损耗(本质是下采样)等问题。ResNet在某种程度上解决了这个问题,通过直接将输入信息绕道传到输出,保护信息的完整性,整个网络则只需要学习输入、输出差别的那一部分,简化学习目标和难度。
通过实现挂出,在plain net上观察到明显的退化现象,而ResNet上不仅没有退化,34层网络的效果反而比18层的更好,而且收敛速度更快。
模型结构图中,我们可以清楚的”实线“和”虚线“两种连接方式,
1)实线的的Connection部分都是$3x3x64$的特征图,他们的channel个数一致,所以采用计算方式:
$$H(x)=F(x)+x$$
2)虚线的的Connection部分分别是$3x3x64$和$3x3x128$的特征图,他们的channel个数不同(64和128),所以采用计算方式:
$$H(x)=F(x)+Wx$$
其中$W$是卷积操作,用来调整x的channel维度的。
虚线残差连接为残差块输入输出维度不同. 当维度相同时, 使用恒等映射; 当输出维度升高(stride=2)时, 使用 zero-padding(使用全0填充缺少的维度, 然后concat低维数据从而升到高纬度) 或者 projection shortut(通过 1×1 卷积升维), 使用 projection shortut 效果略好一点点。
两种ResNet设计
在ResNet的论文中,除了两层的残差学习单元,还有三层的残差学习单元。两层的残差学习单元中包含两个相同输出通道数(因为残差等于目标输出减去输入,即$H(x) - x$,因此输入、输出维度需保持一致)的$3 \times 3$卷积;而3层的残差网络则使用了$1 \times 1$卷积,并且是在中间$3 \times 3$的卷积前后都使用了$1 \times 1$的卷积,有先降维再升维的操作。另外,如果有输入、输出维度不同的情况,我们可以对$x$做一个线性映射变换维度,再连接到后面的层。
这两种结构分别针对ResNet34(左图)和ResNet50/101/152(右图),一般称整个结构为一个”building block“。其中右图又称为”bottleneck design”,目的一目了然,就是为了降低参数的数目,第一个1x1的卷积把256维channel降到64维,然后在最后通过1x1卷积恢复,整体上用的参数数目:1x1x256x64 + 3x3x64x64 + 1x1x64x256 = 69632,而不使用bottleneck的话就是两个3x3x256的卷积,参数数目: 3x3x256x256x2 = 1179648,差了16.94倍。
对于常规ResNet,可以用于34层或者更少的网络中,对于Bottleneck Design的ResNet通常用于更深的如101这样的网络中,目的是减少计算和参数量(实用目的)。