残差网络(Residual Networks, ResNets)

本文深入探讨了残差网络(ResNets)的概念及其如何解决深层神经网络的退化问题。残差网络通过引入shortcut connections,使得深层网络更容易优化,即使在网络深度增加时,也能保持良好的性能。

残差网络(Residual Networks, ResNets)

1. 什么是残差(residual)?

  “残差在数理统计中是指实际观察值与估计值(拟合值)之间的差。”“如果回归模型正确的话, 我们可以将残差看作误差的观测值。”

  更准确地,假设我们想要找一个 xx,使得 f(x)=bf(x)=b,给定一个 xx 的估计值 x0x0,残差(residual)就是 b−f(x0)b−f(x0),同时,误差就是 x−x0x−x0。

  即使 xx 不知道,我们仍然可以计算残差,只是不能计算误差罢了。

2. 什么是残差网络(Residual Networks,ResNets)?

  在了解残差网络之前,先了解下面这个问题。

  Q1:神经网络越深越好吗?(Deeper is better?)

  A1:如图 1 所示,在训练集上,传统神经网络越深效果不一定越好。而 Deep Residual Learning for Image Recognition 这篇论文认为,理论上,可以训练一个 shallower 网络,然后在这个训练好的 shallower 网络上堆几层 identity mapping(恒等映射) 的层,即输出等于输入的层,构建出一个 deeper 网络。这两个网络(shallower 和 deeper)得到的结果应该是一模一样的,因为堆上去的层都是 identity mapping。这样可以得出一个结论:理论上,在训练集上,Deeper 不应该比 shallower 差,即越深的网络不会比浅层的网络效果差。但为什么会出现图 1 这样的情况呢,随着层数的增多,训练集上的效果变差?这被称为退化问题(degradation problem),原因是随着网络越来越深,训练变得原来越难,网络的优化变得越来越难。理论上,越深的网络,效果应该更好;但实际上,由于训练难度,过深的网络会产生退化问题,效果反而不如相对较浅的网络。而残差网络就可以解决这个问题的,残差网络越深,训练集上的效果会越好。(测试集上的效果可能涉及过拟合问题。过拟合问题指的是测试集上的效果和训练集上的效果之间有差距。)

图 1 不同深度的传统神经网络效果对比图

(“plain” network指的是没有使用 shortcut connection 的网络)

  残差网络通过加入 shortcut connections,变得更加容易被优化。包含一个 shortcut connection 的几层网络被称为一个残差块(residual block),如图 2 所示。(shortcut connection,即图 2 右侧从 xx 到 ⨁⨁ 的箭头)

图 2 残差块

  2.1 残差块(residual block)

  如图 2 所示,xx 表示输入,F(x)F(x) 表示残差块在第二层激活函数之前的输出,即 F(x)=W2σ(W1x)F(x)=W2σ(W1x),其中 W1W1 和 W2W2 表示第一层和第二层的权重,σσ 表示 ReLU 激活函数。(这里省略了 bias。)最后残差块的输出是 σ(F(x)+x)σ(F(x)+x)。

  当没有 shortcut connection(即图 2 右侧从 xx 到 ⨁⨁ 的箭头)时,残差块就是一个普通的 2 层网络。残差块中的网络可以是全连接层,也可以是卷积层。设第二层网络在激活函数之前的输出为 H(x)H(x)。如果在该 2 层网络中,最优的输出就是输入 xx,那么对于没有 shortcut connection 的网络,就需要将其优化成 H(x)=xH(x)=x;对于有 shortcut connection 的网络,即残差块,如果最优输出是 xx,则只需要将 F(x)=H(x)−xF(x)=H(x)−x 优化为 0 即可。后者的优化会比前者简单。这也是残差这一叫法的由来。

  2.2 残差网络举例

  图 3 最右侧就是就是一个残差网络。34-layer 表示含可训练参数的层数为34层,池化层不含可训练参数。图 3 右侧所示的残差网络和中间部分的 plain network 唯一的区别就是 shortcut connections。这两个网络都是当 feature map 减半时,filter 的个数翻倍,这样保证了每一层的计算复杂度一致。

  ResNet 因为使用 identity mapping,在 shortcut connections 上没有参数,所以图 3 中 plain network 和 residual network 的计算复杂度都是一样的,都是 3.6 billion FLOPs.


图 3  VGG-19、plain network、ResNet

  残差网络可以不是卷积神经网络,用全连接层也可以。当然,残差网络在被提出的论文中是用来处理图像识别问题。

  2.3 为什么残差网络会work?

  我们给一个网络不论在中间还是末尾加上一个残差块,并给残差块中的 weights 加上 L2 regularization(weight decay),这样图 1 中 F(x)=0F(x)=0 是很容易的。这种情况下加上一个残差块和不加之前的效果会是一样,所以加上残差块不会使得效果变得差。如果残差块中的隐藏单元学到了一些有用信息,那么它可能比 identity mapping(即 F(x)=0F(x)=0)表现的更好。

  "The main reason the residual network works is that it's so easy for these extra layers to learn the identity function that you're kind of guaranteed that it doesn't hurt performance. And then lot of time you maybe get lucky and even helps performance, or at least is easier to go from a decent baseline of not hurting performance, and then creating the same can only improve the solution from there."

References

残差 百度百科

Residual (numerical analysis) - Wikipedia

Course 4 Convolutional Neural Networks by Andrew Ng

He, K., Zhang, X., Ren, S., & Sun, J. (2015). Deep Residual Learning for Image Recognition, 1–9. Retrieved from http://arxiv.org/abs/1512.03385

残差映射(Residual Mapping)是深度学习领域中的一项重要技术,其核心思想是通过引入跳跃连接(skip connections)来解决深层神经网络训练中的梯度消失和模型退化问题。在传统的深度神经网络中,随着网络层数的增加,优化难度显著上升,导致模型性能难以提升甚至下降。而残差映射通过将输入信息直接传递到后续层,使得网络能够更轻松地学习恒等映射,从而缓解了这一问题。 残差映射的基本构建单元被称为残差块(Residual Block),其数学表达式可以表示为 $ y = F(x, W_i) + x $,其中 $ x $ 是输入,$ F(x, W_i) $ 是需要学习的残差函数,而 $ y $ 是输出。通过这种方式,网络可以更有效地学习输入与输出之间的差异部分,而不是直接拟合原始目标输出,这在很大程度上简化了优化过程。 在深度学习的实际应用中,残差映射最著名的实现是残差网络(ResNet)[^5]。ResNet 在 2015 年的计算机视觉顶级会议 CVPR 上被提出,并迅速成为图像分类、目标检测等多个视觉任务的基准模型。ResNet 的成功不仅体现在其卓越的性能上,还在于其设计理念对后续网络结构的启发,例如 Wide Residual Networks 和 ResNeXt 等变体的提出。 此外,残差映射的思想也被广泛应用于其他领域,如自然语言处理和语音识别等。在这些任务中,通过引入跳跃连接,模型能够更好地捕捉长距离依赖关系,提升整体性能。 ```python # 示例:一个简单的残差块实现(基于PyTorch) import torch import torch.nn as nn class ResidualBlock(nn.Module): def __init__(self, in_channels): super(ResidualBlock, self).__init__() self.conv1 = nn.Conv2d(in_channels, in_channels, kernel_size=3, padding=1) self.bn1 = nn.BatchNorm2d(in_channels) self.relu = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(in_channels, in_channels, kernel_size=3, padding=1) self.bn2 = nn.BatchNorm2d(in_channels) def forward(self, x): residual = x x = self.relu(self.bn1(self.conv1(x))) x = self.bn2(self.conv2(x)) x += residual # 跳跃连接 x = self.relu(x) return x ```
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值