resnet

深度引起的退化问题
特征表示的深度(或者说网络的深度)对于许多视觉识别任务而言至关重要. VGGNet, GoogleNet 也都说明了深度对于神经网络的重要性.
那么堆叠越多的层, 网络真的能学习的越好吗?
下面来看看一组实验数据

可以看出当网络深度到达一定程度时, 在训练集上, 深层网络的训练误差也浅层的高, 总体表现不如浅层的网络, 对于这种现象我们称为退化问题(degradation problem).
残差网络的提出就是为了解决这种退化问题.
退化问题有何引起?
臭名昭著的梯度消失和梯度爆炸问题已经通过提出的标准初始化(如 Xavier, Msra)和中间层标准化(BN)解决. 退化问题也不是由过拟合造成, 因为在训练集上, 深层网络的性能就不如浅层网络.
退化问题表明并非所有系统都同样易于优化, 推测退化问题可能是由于深度传统卷积网络的收敛速度可能呈指数级低, 当前的计算能力没法等到它收敛.
让我们考虑一个更浅的架构和更深层次的架构,后者在前者上添加更多层。 在深层模型中存在这样一个优化方案:
增加的层是使用恒等映射(identity mapping),其他层是从学习的浅层模型复制的。这种构建解决方案的存在表明,深层模型不应该比浅层模型产生更高的训练误差。 然而, 实验表明,我们目前的优化算法无法找到与构建的解决方案一样好或更好的方案.
于是作者提出, 既然网络不能找到与构建的解决方案相当的方案, 我们就手动在不同层间加入恒等映射(恒等映射的好处: 既不增加额外的参数也不增加计算复杂度), 然后让网络学习残差映射(residual mapping). 并 假设网络优化残差映射比优化原始映射要简单, 比如说,就拿极端情况来说, 如果恒等映射是最优的, 让残差(f(x)=0)为零要比让堆叠的几个非线性层去学习恒等映射要简单.

残差学习
残差定义: 数理统计上, 残差表示实际观测值与估计值(拟合值)的差, 蕴含模型的重要信息.
如果假设多个非线性层可以渐近地逼近复杂函数 \(H(x)\),那么可以假设它们也可以渐近地逼近残差函数 \(H(x)-x\). 因此,不是期望非线性层表示的函数接近 \(H(x)\),我们明确地让这些层接近残差函数 \(F(x)=H(x)-x\)。原始函数因此变成 \(H(x)=F(x)+x\)。
尽管神经网络的非线性表示可以渐近地接近这两种函数, 但学习的难易可能不同。
实验数据
在实际情况下,恒等映射不太可能是最优的,但重定义该问题可能有助于解决这个问题。 如果最优函数比零映射更接近恒等映射,那么优化函数应该更容易找出参照恒等映射的扰动,而不是将函数学习为恒等映射。 实验显示, 通常所学习的残差函数具有很小的响应,这表明恒等映射提供了合理的预处理。

有人一定会想, \(\color{red}{只给标准差为何就能说明响应小呢? 这不是只能说明波动性质吗?}\)
我们看图片说明, 响应所取得位置在 BN 之后, ReLU 之前, 所以响应的均值是 0, 所以标准差越小说明响应总体取值越靠近 0.
resnet 网络架构设计
vggnet 哲学:
- 卷积层主要是 3×3 卷积核(filter) (比如如果你想得到5x5的视野, 可以通过2个3x3的 kernel 堆叠, 不过参数数量减少了, 5x5=25, (3x3)x2=18)
- 具有有相同的输出特征图(feature maps)大小的层,具有相同数量的 filter
- 如果特征图大小减半, filter 的数量加倍以保持每层的计算复杂度。
不过 resnet 的 conv1 设计采用的是 7x7 的 kernel size, 不清楚出于什么考虑?
同事通过问xiangyu得知, 如果想要得到 7x7 的感受野, 需要 3 个 3x3 的卷积堆叠, 但是由于输入图片的通道数比较小, 而通过堆叠3x3卷积层的方式, 后面两层的输入通道64, 这样计算量就过于大了.
网络直接采用 stride 为 2 的卷积层执行下采样, 整体网络分为 5 大层, 每层的 Feature Map 尺寸大小相同, 网络以 Global Average Pooling 的输出和 1000- softmax 的完全连接层结束。
resnet 网络架构设计
Basic Block


可以看出, 深层网络相对于浅层网络主要是在 conv4 层增添了很多层.
block | channel | num_layer |
---|---|---|
conv1_x | 64 | 1 |
conv2_x | 64 | 2-3 |
conv3_x | 128 | 4-8 |
conv4_x | 256 | 2-36 |
conv5_x | 512 | 2-3 |
Bottleneck Block

相比较与 Basic Block, Bottleneck Block 将 2 个 3x3 卷积变为 2个 1x1 卷积和一个与原来的 Basic Block 中 3x3 卷积相同通道数的 3x3 卷积. 第一个 1x1 卷积为了降维, 第二个 1x1 卷积为了升维, 通道数扩张因子为 4, 至于为何选择 4, 我的理解是为了大于保证这两种 block 的计算量不变, 具体计算如下.
我们看看将传统的 Basic Block 变为 Bottleneck Block 之后计算量的变化;
就拿 \(\bf conv2\) 来说吧, 结构如上图, 某一层的计算量计算公式(假设输入输出 Feature Map 大小不变)
Basic Block(即为 BB) 和 Bottleneck Block计算量(记为 BNB)
两个 1x1 卷积的计算量约等于一个 3x3 卷积的计算量
这两种设计计算量接近(Bottleneck Block计算量稍微小一点), 但是通过 Bottleneck Block 可以加深网络的深度. 不过我们在上上图中发现, resnet50 其实就是 resnet34 将 BasicBlock 变为 BottleneckBlock, 既然 BottleneckBlock 计算量稍微小一点, 那么为什么 resnet50 要计算量多一点啊(3.6 vs 3.8), 应该是由于 \(\bf conv5\) 的最后一层输出为 2048. global average pooling(GAP) 之后, 为 1x1x2048, 这样 GAP 和 FC 层的计算量都是之前的4倍.

虚线残差连接为残差块输入输出维度不同. 当维度相同时, 使用恒等映射; 当输出维度升高(stride=2)时, 使用 zero-padding(使用全0填充缺少的维度, 然后concat低维数据从而升到高纬度) 或者 projection shortut(通过 1×1 卷积升维), 使用 projection shortut 效果略好一点点
关于虚线残差连接, 即输入输出不同维度时, resnet 在 caffe (作者本人的实现方案)和 pytorch 具体实现结构如下, 即采用 1x1 卷积核和 bn, 注意有 bn.

resnet_v2
Residual Units 可以表示为如下形式:

如果 h(x)和 f(y) 都是恒等映射,则信号可以直接从一个单位传播到任何其他单位,无论是信息前向出还是误差反向传递。 我们的实验证明,当架构更接近上述两个条件时,训练总体上会变得更容易。
预激活的 resnet
定义: 构建恒等映射 f(y) = y, 我们将 \(\color{blue}{"激活函数"}\) (BN 和 ReLU) 视为卷积层的预激活(pre-activation), 与之相对, 传统的方式称为 "后激活"(post-activation), 结构如下图

好处:
- 让优化更简单(由于 f 变为了恒等映射), 加快模型训练速度
- 提升模型的正则化. 在原来的 Residual Unit(下图(a)部分) 中, 虽然 BN 标准化了 signal, 不过标准化结果会与 shortcut 相加, 因此合并的 signal 并没有被正则化.

Identity Skip Connections 的重要性
如果将恒等映射换成一个简单修改版 \(h(x_l) = \lambda_l x_l\), 那么, 梯度传递如下
\(\prod _{ i=l }^{ i=L-1 }{ \lambda _{ i } }\) 对于一个极深的网络(L很大),如果对于所有 i,\(λ_i > 1\) ,则该因子可以是指数级大的; 如果对于所有 i,\(λ_i < 1\),则该因子可以呈指数规律地变小并消失,这阻止了从 skip connection 中进行反向传播的梯度, 并迫使它通过 weight 层流动, 这导致了我们通过实验显示的优化难题。