深度解析ResNet及其变体
一:前言(问题由来)
在讲resnet之前我们需要知道resnet的由来,以及到底在解决什么问题,然后才是如何解决问题,最终才是工业化实现。本篇文章需要一定的resnet基础,新手可以多看看基础再来。
1,根据无限逼近定理我们知道,任意两层神经网络可以拟合任何函数,(一层不行,因为会出现非线性函数无法被拟合的情况)通过每层中的每个节点来表示一段函数(特征),然后层数相加,即可得到我们要表达的函数。那么是不是网络深度越高,函数拟合能力越强呢?或者说我们能不能通过简单的叠加网络层数来增加网络深度呢?其实不能,第一,会出现过拟合现象,第二,简单叠加会使得梯度弥散/爆炸问题严重,使得深度网络的训练变得异常困难。
2,我们都知道网络越深其带来的好处是明显的。第一:网络的等级随着网络的深度增加而不断增加。第二:深度越高网络的表征的能力更强(因为增加了更多的cov和非线性表达。所以网络拟合数据的能力越高。前面介绍的其他经典模型可以证明)。
3,前面第一点我们讲到网络深度不断增加会带来很多好处,但是同时也会出现一些问题。除了梯度弥散/爆炸问题外,还有一个重要的问题,那就是深层网络相对于浅层网络而言,其误差更大。如下图
欠拟合,适中和过拟合。过拟合的现象是"高方差,低偏差",即测试误差大而训练误差小。但实际上,深层CNN的训练误差和测试误差都很大。何凯明等人通过CIFAR10数据,对比20和56层网络的训练误差和测试误差,得出这样一个结论:深层网络比浅层网络的整体误差更大,即随着深度的增加,网络会出现退化问题。但是该误差不是过拟合导致的。如下图
二:ResNet到底在解决什么问题
由上面分析可以总结出,ResNet是在解决以下两个问题:
1,梯度弥散问题。
-
在这里我们需要简单讲解一下什么是梯度弥散问题。所谓梯度弥散是指在网络反向传播过程中,出现的梯度消失现象。梯度爆炸是指在反向传播过程中,梯度在某个节点增加到很高。导致无法运算,被称为爆炸。为什么会导致梯度弥散或者梯度爆炸呢,是因为在反向传播过程中,每层网络中的节点个数不同,导致的梯度不断分散或者集中(变大或者变小),就产生了梯度弥散或者梯度爆炸,但随着网络深度的增加,梯度弥散是不断回归的。值得一提的是,由于人为的参数设置,梯度更倾向于消失而不是爆炸。
例子:
观察上述反向传播,不难发现,在输出端梯度的模值,经过回传扩大了3~4倍。这是由于反向传播结果的数值大小不止取决于求导的式子,很大程度上也取决于输入的模值。
我们现在无论用Pytorch还是Tensorflow,都会自然而然地加上Bacth Normalization(简称BN),而BN的作用本质上也是控制每层输入的模值,因此梯度的爆炸/消失现象理应在很早就被解决了(至少解决了大半)。
2,深度增加导致的网络退化问题
-
退化问题的存在已经被大量的实验证明。而如何解决这个问题成为了关键,这也是 ResNet的核心–shorttcut结构。说简单点就是如何在浅层网络上增加恒等映射使得网络更深,效果更好的同时不出现网络退化。
-
Residual block结构如下图
-
图中右侧的曲线叫做跳接(shortcut connection),通过跳接在**函数前,将上一层(或几层)之前的输出与本层计算的输出相加,将求和的结果输入到**函数中做为本层的输出。
用数学语言描述,假设Residual Block的输入为x ,则输出 等于:
其中
是我们学习的目标,即输出输入的残差F(X) 。以上图为例,残差部分是中间有一个Relu**的双层权重,即:
其中 α指代Relu,而 2指代两层权重。顺带一提,这里一个Block中必须至少含有两个层,否则就会出现很滑稽的情况:
显然这样加了和没加差不多……
三:ResNet如何解决问题
(一)
前面分析得出,如果深层网络后面的层都是是恒等映射,那么模型就可以转化为一个浅层网络。那现在的问题就是如何得到恒等映射了。
事实上,已有的神经网络很难拟合潜在的恒等映射函数H(x) = x。
由上面的分析可以得出,任意两层网络可以拟合任意函数,假设输入为X,我们要拟合的函数是H(X).即有H(x) = x恒等映射**,由上图问题我们清楚的看到这就是一个典型的深度网络带来的问题。如果前六层已经将网络训练得很好了,那么此时整个网络为H(x),我们可以将F(x)设为0 ,七八层权重设置为1,(目的是为了保证网路性能至少不会退化)因此可以得到新的拟合函数H(X) = F(x)+x.如此,我们就得到了一个新的恒等映射,同时也将一个深度网络通过shortcut结构转成了一个浅层网络,并且我们的问题转化为学习一个残差函数F(x) = H(x) - x,只要F(x)=0,就构成了一个恒等映射H(x) = x。至此,shortcut_block结构就成立了。而且,拟合残差至少比拟合恒等映射容易得多。原因如下(个人意见,仅供参考)
1:第一:趋近于0 比趋近于1计算量更少
2,第二:relu的影响–速度更快
此处省略relu函数图(这个都不知道你就不要看了朋友)
输入小于0,则等于0,输入大于0,则x = y,这样可以过滤掉resnet中小于0的输入。直接加速了F(x)趋近于0 的过程。
至此,我们在理论和模型上证明了residual net 存在的意义和解决问题的方法。
(二)ResNet及其变体
一:抛砖引玉
1,如果输入x和输出F(x)维度不同,则需要在Residual block结构中加入w系数来调节(其实就是增加了一个cov)
2,针对50层以上的网络,resnet如何变化才能提升效率。
这就引出了一个ResNet网络优化问题。
ResNeXt
是ResNet的一种变体,代号为ResNeXt。下图是其基本构件:左边是[2]中所提到的残差块;右边是基数为32的ResNeXt构件
这看起来可能很熟悉,因为它跟Inception模块非常相似。在这个变体中,不同路径输出的合并是通过相加来实现的,除此之外,它们都遵循了“分割-转换-合并”范例,而在[4]中它们却是深度串联(depth concatenated)的。另外一个区别在于每一个路径互不相同,而在这个架构中,所有的路径都遵循了相同的拓扑结构。
文中引入了一个叫作“基数(cardinality)”(独立路径的数量)的超参数,提供了一种调整模型能力的新思路。实验表明,通过扩大基数值,我们能够更加高效地提升模型的表现。与Inception相比,这个全新的架构更容易适应新的数据集或任务,因为它只有一个简单的范例和一个超参数需要调整,而Inception需要调整很多超参数(比如每个路径卷积核的大小)。
这个全新的结构有三个等价形式:
在实际中,这个“分割-转换-合并”范例通常是通过“逐点分组卷积层”实现的,这个卷积层会将它获得的feature map输入分成几组,然后分别执行正常的卷积操作;最终的输出是depth concatenated的,并且会被输入至一个1*1的卷积层中。
DenseNet
这个结构进一步使用了shortcut connections,将所有的层互相连接起来。在这个新架构中,每一层的输入都包含了所有较早的层的feature maps,而且它的输出被传递至每个后续层。这些feature maps通过depth concatenation在一起。
除了解决梯度消失问题,这个架构还支持“特征重用”,这就使得网络更加“参数高效”。其中一个简单的解读是,恒等变换的输出与模块的输出直接相加,如果两个层的feature maps有着完全不同的分布,那么这可能会阻碍信息的流动。因此,用depth-concatenation能够有效避免这种情况的发生,并且增加输出的多样性,进而促进特征的重新使用。
根据这种范例,我们知道第l层输入feature map的数量会有k*(l-1)+k_o个,其中的k_0是输入图像中的通道数目。作者们使用一个叫做“增长率”(k)的超参数防止网络变得过宽,他们还用了一个11的卷积瓶颈层在33卷积前减少特征映射的数量。整体架构如下表所示:
深度随机的神经网络
ResNet的强大性能在很多应用中已经得到了证实,尽管如此,ResNet还是有一个不可忽视的缺陷——更深层的网络通常需要进行数周的训练——因此,把它应用在实际场景下的成本非常高。为了解决这个问题,[10]的作者们引入了一个“反直觉”的方法,即在我们可以在训练过程中任意地丢弃一些层,并在测试过程中使用完整的网络。
作者们用了残差块作为他们网络的构件,因此,在训练中,如果一个特定的残差块被启用了,那么它的输入就会同时流经恒等表换shortcut(identity shortcut)和权重层;否则输入就只会流经恒等变换shortcut。在训练的过程中,每一个层都有一个“生存概率”,并且都会被任意丢弃。在测试过程中,所有的block都将保持被**状态,而且block都将根据其在训练中的生存概率进行调整。
从形式上来看,H_l是第l个残差块的输出结果,f_l是由l第l个残差块的权重映射所决定的映射,b_l是一个Bernoulli随机变量(此变量的值只有1或0,反映出一个block是否是被**的)。具体训练过程如下:
当b_l=1时,这个block就是一个正常的残差块;当b_l=0时,上面的公式就变成了这样:
既然我们已经知道了H_(l-1)是一个ReLU的输出结果,而且这个输出结果已经是非负的了,那么上面的方程式就会减少到只剩下一个恒等层:
如果p_l表示的是第l层在训练中的生存概率,那么在测试过程中,我们就能得到以下的方程式:
作者们将一个“线性衰减规律”应用于每一层的生存概率,他们表示,由于较早的层会提取低级特征,而这些低级特征会被后面的层所利用,所以这些层不应该频繁地被丢弃。这样,最终生成的规则就变成了这样:
上面公式中的L表示block的总数量,因此p_L就是最后一个残差块的生存几率,这个几率在整个实验中一直都保持着0.5的水平。一定要注意的是,在这个情境中的输入被视为第一个层(l=0),所以这个第一层永远不会被丢弃。随机深度训练的整体框架如下图所示:
与Dropout[11]类似,用任意的深度来训练一个深度网络可以看作是训练一个小型ResNet集合,两种训练的区别在于上面的方法是任意地丢弃一整个层的,而Dropout在训练中仅丢弃一个层的部分隐藏单元。
实验表明,同样是训练一个110层的ResNet,以任意深度进行训练的性能,比以固定深度进行训练的性能要好。这就意味着ResNet中的一些层(路径)可能是冗余的。
作为小型网络集合的ResNet
大佬们提出了一个训练一个深度网络的“反直觉”方法,即在训练中任意地丢弃网络的层,并在测试中使用整个网络。同时介绍了一个更加“反直觉”的发现:我们可以删除经过训练后的ResNet中的部分层,同时保持相当不错的网络性能。
为了使训练过程更清晰易懂,Veit等人首先介绍了一个ResNet的分解图。当展开这个网络架构以后,我们就能很清楚地发现,一个有着i个残差块的ResNet架构有2**i个不同的路径(因为每一残差块会提供两个独立的路径)。
根据以上的分解图,我们就能清晰地理解为什么移除ResNet架构中的部分层不会降低其太多性能,这是因为ResNet架构有很多独立有效路径,而且大部分路径在移除了部分层之后会保持完整无损。相反,VGG网络只有一个有效路径,因此移除一个层都会对它的唯一路径的性能产生极大的影响。
通过实验,作者们还发现了ResNet中的路径有着多模型集成的行为倾向。他们在测试时删除不同数量的层,检查网络性能与删除层的数量是否相关。结果显示,网络的表现确实有着整体聚集的倾向,具体如下图所示:
最后,作者们研究了ResNet中路径的特征:
很明显,所有可能路径长度的分布都与一个Binomial分布相关,如下图的(a)所示。大部分的路径都流经了19到35个残差块。
为了得到路径长度k的梯度幅度,作者们首先向网络输入了一批数据,然后任意采样了k个残差块。当反向传递梯度时,他们仅将采样的残差块通过权重层进行传递。(b)图表示随着路径长度的增加,梯度幅度会迅速下降。
我们现在可以将每一路径长度与其期望的梯度大小相乘,看每一路径长度在训练中起到多大的作用,就像©图。令人惊讶的是,大多分布都来自于9到18的路径长度,但它们都只包含少量的总路径,如(a)图。这是一个非常有趣的发现,因为这暗示着ResNet无法解决过长路径的梯度消失问题,ResNet的成功实际上源自于它缩短了它的有效路径(effective path)的长度。