0


【论文笔记】一文读懂残差网络ResNet(附代码)

Residual Net论文笔记

残差网络(Residual Net, ResNet)自从2015年面世以来,凭借其优异的性能在ILSVRC中以绝对优势获得第一名,并成功应用于许多领域。

1. 传统深度网络的问题

在深度学习或者神经领域的研究中,一般认为,网络越深(层数越多),网络的性能应该会更好,因为更深的网络倾向于有更强大的表示能力,即网络容量越大。

但是在实际过程中,我们发现过深的网络反而会导致性能的下降。在网络结构的设计中似乎存在一种“阈值”,在到达一定的层数之后,训练误差和测试误差都会加大。下图为一个24层网络和一个56层网络在CIFAR10数据集的训练表现。
在这里插入图片描述
显然,这种性能的下降并不是因为过拟合引起的。因为过拟合意味着训练误差正常减小,而测试误差显著增大。

对这种现象的一种解释是,在网络深度过深的时候,低层参数的细微变动都会引起高层参数的剧烈变化,优化算法没有能力去得到一个最优解。

做这样一个假设,假设有一个50层的网络,但在其优化过程中,最容易优化出最佳解的层数是25,那么这个网络的后25层应当作为一个恒等映射

  1. x
  2. 25
  3. =
  4. f
  5. 1
  6. (
  7. x
  8. )
  9. \mathbf{x}_{25}=f_{1}(\mathbf{x})
  10. x25​=f1​(x)
  11. o
  12. u
  13. t
  14. =
  15. f
  16. 2
  17. (
  18. x
  19. 25
  20. )
  21. \mathbf{out}=f_{2}(\mathbf{x}_{25})
  22. out=f2​(x25​)

由于神经网络由非线性层组合而成,学习一个恒等映射是比较困难的。优化算法的局限性使得“冗余”的网络层学习到了不适合恒等映射的参数。

2. 残差结构和残差网络

2.1 残差是什么

残差的统计学定义:实际观测值和估计值(拟合值)之间的差值。

如果存在某个k层的网络

  1. F
  2. F
  3. F是当前最优的网络,那么可以构造一个更深的网络,其最后几层仅是网络fk层输出的**恒等映射**,就可以取得与
  4. F
  5. F
  6. F一致的结果

如果k还不是最佳层数,那么更深的网络就可以取得更好的结果。所以,如果深层网络的效果不如浅层网络,那么则说明新加入层不好学。

如果不好学,则可以使用类似“分治法”,分开求解恒等映射和非恒等映射。
x代表之前浅层网络已经学到的东西
F(x)代表已经学到的东西和要学的东西的之间的残差

现在只学F(x)就能与x组合起来。

  1. H
  2. (
  3. x
  4. )
  5. =
  6. F
  7. (
  8. x
  9. )
  10. +
  11. x
  12. F
  13. (
  14. x
  15. )
  16. =
  17. H
  18. (
  19. x
  20. )
  21. x
  22. H(\mathbf{x})=F(\mathbf{x})+\mathbf{x} \\ F(\mathbf{x})=H(\mathbf{x})-\mathbf{x}
  23. H(x)=F(x)+xF(x)=H(x)−x

  1. x
  2. x
  3. x成为恒等映射,那么只需要学习残差
  4. F
  5. (
  6. x
  7. )
  8. F(x)
  9. F(x)作为非恒等映射。

残差在这里,指的是直接的映射H(x)与快捷连接x的差值,也就是

  1. F
  2. (
  3. x
  4. )
  5. F(\mathbf{x})
  6. F(x)。

2.2 残差模块 Residual Block

据此,我们设计一个残差模块(Residual Block)的结构如下:
在这里插入图片描述

  1. y
  2. l
  3. =
  4. h
  5. (
  6. x
  7. l
  8. )
  9. +
  10. F
  11. (
  12. x
  13. l
  14. ,
  15. W
  16. l
  17. )
  18. \mathbf{y}_{l}=h(\mathbf{x}_{l})+\mathcal{F}(\mathbf{x}_{l},\mathcal{W}_{l})
  19. yl​=h(xl​)+F(xl​,Wl​)
  20. x
  21. l
  22. +
  23. 1
  24. =
  25. f
  26. (
  27. y
  28. l
  29. )
  30. \mathbf{x}_{l+1}=f(\mathbf{y}_{l})
  31. xl+1​=f(yl​)

在网络实现中:

  1. h
  2. (
  3. x
  4. l
  5. )
  6. =
  7. x
  8. l
  9. f
  10. =
  11. R
  12. e
  13. L
  14. U
  15. h(\mathbf{x}_{l})=\mathbf{x}_{l}\qquad f=\mathrm{ReLU}
  16. h(xl​)=xlf=ReLU
  17. x
  18. l
  19. +
  20. 1
  21. y
  22. l
  23. \mathbf{x}_{l+1}\equiv\mathbf{y}_{l}
  24. xl+1​≡yl

最后得到的残差模块表达式如下:

  1. x
  2. l
  3. +
  4. 1
  5. =
  6. x
  7. l
  8. +
  9. F
  10. (
  11. x
  12. l
  13. ,
  14. W
  15. l
  16. )
  17. \mathbf{x}_{l+1}=\mathbf{x}_{l}+\mathcal{F}(\mathbf{x}_{l},\mathcal{W}_{l})
  18. xl+1​=xl​+F(xl​,Wl​)

2.3 基本模块BasicBlock和BottleNeck

在残差网络中,基本的残差模块由两个3×3的卷积层和ReLU激活函数、BatchNorm层组成。其结构如下(以64个channel的输入为例):

  1. BasicBlock((conv1): Conv2d(64,64, kernel_size=(3,3), stride=(1,1), padding=(1,1), bias=False)(bn1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)(relu): ReLU(inplace=True)(conv2): Conv2d(64,64, kernel_size=(3,3), stride=(1,1), padding=(1,1), bias=False)(bn2): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True))

在网络层数过深的时候,考虑到训练成本,作者提出了一种新的结构设计BottleNeck。将原来的两个3×3的卷积层变为两个1×1的卷积层和一个3×3的卷积层。其中,两个1×1的卷积层负责降低/恢复通道维度,3×3的卷积层负责“真正”的卷积运算。其结构如下图右图所示,BottleNeck的运算具有更小的时间复杂度。
在这里插入图片描述

2.4 残差网络ResNet设计

在文中,作者给出了ResNet18、ResNet34、ResNet50、ResNet101、ResNet152等网络设计,分别对应不同层数的卷积运算层。在50层及以上的网络中,都使用BottleNeck结构进行网络构建。
在这里插入图片描述

2.4.1 恒等映射与残差的连接

在网络设计中,特别需要注意的是在不同layer之间,例如conv2_x和conv3_x间,输出和输入的尺寸是不一样的,如下图虚线所示。

对于残差运算,这可以很简单的通过卷积进行尺寸的变换,对于恒等映射,作者考虑了如下几个方法进行变换:

  1. 给恒等映射 x \mathbf{x} x添加0,扩充其维度
  2. 用一个1×1的卷积进行下采样在这里插入图片描述

在代码实现中,采用下采样的方法对恒等映射进行变换。

3. Forward/Backward Propagation

3.1 Forward propogation

传统网络

  1. F
  2. (
  3. x
  4. ,
  5. w
  6. )
  7. =
  8. x
  9. w
  10. F(x,w)=xw
  11. F(x,w)=xw
  12. x
  13. L
  14. =
  15. F
  16. (
  17. x
  18. L
  19. 1
  20. ,
  21. w
  22. L
  23. 1
  24. )
  25. =
  26. F
  27. (
  28. F
  29. (
  30. x
  31. L
  32. 2
  33. ,
  34. w
  35. L
  36. 2
  37. )
  38. ,
  39. w
  40. L
  41. 1
  42. )
  43. =
  44. i
  45. =
  46. 1
  47. L
  48. 1
  49. x
  50. i
  51. w
  52. i
  53. x_{L}=F(x_{L-1},w_{L-1})=F(F(x_{L-2},w_{L-2}),w_{L-1})\cdots=\prod_{i=1}^{L-1}{x_{i}w_{i}}
  54. xL​=F(xL1​,wL1​)=F(F(xL2​,wL2​),wL1​)⋯=∏i=1L1xiwi

残差网络

  1. x
  2. 2
  3. =
  4. x
  5. 1
  6. +
  7. F
  8. (
  9. x
  10. 1
  11. ,
  12. w
  13. 1
  14. )
  15. x_{2}=x_{1}+F(x_1,w_{1})
  16. x2​=x1​+F(x1​,w1​)
  17. x
  18. 3
  19. =
  20. x
  21. 2
  22. +
  23. F
  24. (
  25. x
  26. 2
  27. ,
  28. w
  29. 2
  30. )
  31. =
  32. x
  33. 1
  34. +
  35. F
  36. (
  37. x
  38. 1
  39. ,
  40. w
  41. 1
  42. )
  43. +
  44. F
  45. (
  46. x
  47. 2
  48. ,
  49. w
  50. 2
  51. )
  52. x_{3}=x_{2}+F(x_2,w_{2})=x_{1}+F(x_{1},w_{1})+F(x_{2},w_{2})
  53. x3​=x2​+F(x2​,w2​)=x1​+F(x1​,w1​)+F(x2​,w2​)
  54. \cdots
  55. x
  56. L
  57. =
  58. x
  59. 1
  60. +
  61. i
  62. =
  63. 1
  64. L
  65. 1
  66. F
  67. (
  68. x
  69. i
  70. ,
  71. w
  72. i
  73. )
  74. x_{L}=x_{1}+\sum_{i=1}^{L-1}{F(x_{i},w_{i})}
  75. xL​=x1​+∑i=1L1F(xi​,wi​)

3.2 Back Propogation

传统网络
浅层网络是

  1. g
  2. (
  3. x
  4. )
  5. g(x)
  6. g(x),加入层以后变成
  7. f
  8. (
  9. g
  10. (
  11. x
  12. )
  13. )
  14. f(g(x))
  15. f(g(x))
  16. f
  17. (
  18. g
  19. (
  20. x
  21. )
  22. )
  23. x
  24. =
  25. f
  26. (
  27. g
  28. (
  29. x
  30. )
  31. )
  32. g
  33. (
  34. x
  35. )
  36. g
  37. (
  38. x
  39. )
  40. x
  41. \frac{\partial{f(g(x))}}{\partial{x}}=\frac{\partial{f(g(x))}}{\partial{{g(x)}}}\frac{\partial{g(x)}}{\partial{x}}
  42. xf(g(x))​=∂g(x)∂f(g(x))​∂xg(x)​

残差网络

  1. (
  2. f
  3. (
  4. g
  5. (
  6. x
  7. )
  8. )
  9. +
  10. g
  11. (
  12. x
  13. )
  14. )
  15. x
  16. =
  17. f
  18. (
  19. g
  20. (
  21. x
  22. )
  23. )
  24. g
  25. (
  26. x
  27. )
  28. g
  29. (
  30. x
  31. )
  32. x
  33. +
  34. g
  35. (
  36. x
  37. )
  38. x
  39. \frac{\partial{(f(g(x))+g(x))}}{\partial{x}}=\frac{\partial{f(g(x))}}{\partial{{g(x)}}}\frac{\partial{g(x)}}{\partial{x}}+\frac{\partial{g(x)}}{\partial{x}}
  40. x∂(f(g(x))+g(x))​=∂g(x)∂f(g(x))​∂xg(x)​+∂xg(x)​

可以看到,在求梯度的过程中,残差网络相比传统网络多加了一项,这有利于解决梯度消失,是网络训练的更快

损失函数对网络的第

  1. l
  2. l
  3. l求梯度:

传统网络

  1. L
  2. o
  3. s
  4. s
  5. x
  6. l
  7. =
  8. L
  9. o
  10. s
  11. s
  12. x
  13. L
  14. x
  15. L
  16. x
  17. l
  18. =
  19. L
  20. o
  21. s
  22. s
  23. x
  24. L
  25. i
  26. =
  27. 1
  28. L
  29. 1
  30. x
  31. i
  32. w
  33. i
  34. x
  35. l
  36. \frac{\partial{Loss}}{\partial{x_{l}}}=\frac{\partial{Loss}}{\partial{x_{L}}}\frac{\partial{x_{L}}}{\partial{x_{l}}}=\frac{\partial{Loss}}{\partial{x_{L}}}\frac{\partial{\prod_{i=1}^{L-1}{x_{i}w_{i}}}}{\partial{x_{l}}}
  37. xl​∂Loss​=∂xL​∂Loss​∂xl​∂xL​​=∂xL​∂Loss​∂xl​∂∏i=1L1xiwi​​

残差网络

  1. L
  2. o
  3. s
  4. s
  5. x
  6. l
  7. =
  8. L
  9. o
  10. s
  11. s
  12. x
  13. L
  14. x
  15. L
  16. x
  17. l
  18. =
  19. L
  20. o
  21. s
  22. s
  23. x
  24. L
  25. (
  26. 1
  27. +
  28. i
  29. =
  30. l
  31. L
  32. 1
  33. F
  34. (
  35. x
  36. i
  37. ,
  38. w
  39. i
  40. )
  41. x
  42. l
  43. )
  44. \frac{\partial{Loss}}{\partial{x_{l}}}=\frac{\partial{Loss}}{\partial{x_{L}}}\frac{\partial{x_{L}}}{\partial{x_{l}}}=\frac{\partial{Loss}}{\partial{x_{L}}}(1+\frac{\partial{\sum_{i=l}^{L-1}{F(x_{i},w_{i})}}}{\partial{x_{l}}})
  45. xl​∂Loss​=∂xL​∂Loss​∂xl​∂xL​​=∂xL​∂Loss​(1+∂xl​∂∑i=lL1F(xi​,wi​)​)

可以看到,在残差网络中,梯度由乘法变加法,这可以有效缓解梯度消失和梯度爆炸。

4. 代码分析

PyTorch现已将ResNet整合为python库,可以直接调用。源码的地址如下:
https://github.com/pytorch/vision/blob/main/torchvision/models/resnet.py
基本的卷积层

  1. defconv3x3(in_planes, out_planes, stride=1, groups=1, dilation=1):"""3x3 convolution with padding"""return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride,
  2. padding=dilation, groups=groups, bias=False, dilation=dilation)defconv1x1(in_planes, out_planes, stride=1):"""1x1 convolution"""return nn.Conv2d(in_planes, out_planes, kernel_size=1, stride=stride, bias=False)

其中,对输入进行下采样在卷积层中有两种实现方式:

Basic Block
其中,expension代表了经过一个Block之后,channel数量的变化。这里输出channel维度与预设一样,expension为1。

  1. classBasicBlock(nn.Module):
  2. expansion =1def__init__(self, inplanes, planes, stride=1, downsample=None, groups=1,
  3. base_width=64, dilation=1, norm_layer=None):super(BasicBlock, self).__init__()if norm_layer isNone:
  4. norm_layer = nn.BatchNorm2d
  5. if groups !=1or 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
  6. self.conv1 = conv3x3(inplanes, planes, stride)
  7. self.bn1 = norm_layer(planes)
  8. self.relu = nn.ReLU(inplace=True)
  9. self.conv2 = conv3x3(planes, planes)
  10. self.bn2 = norm_layer(planes)
  11. self.downsample = downsample
  12. self.stride = stride
  13. defforward(self, x):
  14. identity = x
  15. out = self.conv1(x)
  16. out = self.bn1(out)
  17. out = self.relu(out)
  18. out = self.conv2(out)
  19. out = self.bn2(out)if self.downsample isnotNone:
  20. identity = self.downsample(x)
  21. out += identity
  22. out = self.relu(out)return out

BottleNeck
BottleNeck输出的channel输出是对应BasicBlock的4倍,所以expension=4

  1. classBottleneck(nn.Module):# Bottleneck in torchvision places the stride for downsampling at 3x3 convolution(self.conv2)# while original implementation places the stride at the first 1x1 convolution(self.conv1)# according to "Deep residual learning for image recognition"https://arxiv.org/abs/1512.03385.# This variant is also known as ResNet V1.5 and improves accuracy according to# https://ngc.nvidia.com/catalog/model-scripts/nvidia:resnet_50_v1_5_for_pytorch.
  2. expansion =4def__init__(self, inplanes, planes, stride=1, downsample=None, groups=1,
  3. base_width=64, dilation=1, norm_layer=None):super(Bottleneck, self).__init__()if norm_layer isNone:
  4. norm_layer = nn.BatchNorm2d
  5. width =int(planes *(base_width /64.))* groups
  6. # Both self.conv2 and self.downsample layers downsample the input when stride != 1
  7. self.conv1 = conv1x1(inplanes, width)
  8. self.bn1 = norm_layer(width)
  9. self.conv2 = conv3x3(width, width, stride, groups, dilation)
  10. self.bn2 = norm_layer(width)
  11. self.conv3 = conv1x1(width, planes * self.expansion)
  12. self.bn3 = norm_layer(planes * self.expansion)
  13. self.relu = nn.ReLU(inplace=True)
  14. self.downsample = downsample
  15. self.stride = stride
  16. defforward(self, x):
  17. identity = x
  18. out = self.conv1(x)
  19. out = self.bn1(out)
  20. out = self.relu(out)
  21. out = self.conv2(out)
  22. out = self.bn2(out)
  23. out = self.relu(out)
  24. out = self.conv3(out)
  25. out = self.bn3(out)if self.downsample isnotNone:
  26. identity = self.downsample(x)
  27. out += identity
  28. out = self.relu(out)return out

ResNet
在构造每个层的时候,要注意在输出通道和输入通道数量不一致的时候,要添加一个下采样层对恒等映射进行下采样

  1. classResNet(nn.Module):def__init__(self, block, layers, num_classes=1000, zero_init_residual=False,
  2. groups=1, width_per_group=64, replace_stride_with_dilation=None,
  3. norm_layer=None):super(ResNet, self).__init__()if norm_layer isNone:
  4. norm_layer = nn.BatchNorm2d
  5. self._norm_layer = norm_layer
  6. self.inplanes =64
  7. self.dilation =1if replace_stride_with_dilation isNone:# each element in the tuple indicates if we should replace# the 2x2 stride with a dilated convolution instead
  8. replace_stride_with_dilation =[False,False,False]iflen(replace_stride_with_dilation)!=3:raise ValueError("replace_stride_with_dilation should be None ""or a 3-element tuple, got {}".format(replace_stride_with_dilation))
  9. self.groups = groups
  10. self.base_width = width_per_group
  11. self.conv1 = nn.Conv2d(3, self.inplanes, kernel_size=7, stride=2, padding=3,
  12. bias=False)
  13. self.bn1 = norm_layer(self.inplanes)
  14. self.relu = nn.ReLU(inplace=True)
  15. self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
  16. self.layer1 = self._make_layer(block,64, layers[0])
  17. self.layer2 = self._make_layer(block,128, layers[1], stride=2,
  18. dilate=replace_stride_with_dilation[0])
  19. self.layer3 = self._make_layer(block,256, layers[2], stride=2,
  20. dilate=replace_stride_with_dilation[1])
  21. self.layer4 = self._make_layer(block,512, layers[3], stride=2,
  22. dilate=replace_stride_with_dilation[2])
  23. self.avgpool = nn.AdaptiveAvgPool2d((1,1))
  24. self.fc = nn.Linear(512* block.expansion, num_classes)for m in self.modules():ifisinstance(m, nn.Conv2d):
  25. nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')elifisinstance(m,(nn.BatchNorm2d, nn.GroupNorm)):
  26. nn.init.constant_(m.weight,1)
  27. nn.init.constant_(m.bias,0)# Zero-initialize the last BN in each residual branch,# so that the residual branch starts with zeros, and each residual block behaves like an identity.# This improves the model by 0.2~0.3% according to https://arxiv.org/abs/1706.02677if zero_init_residual:for m in self.modules():ifisinstance(m, Bottleneck):
  28. nn.init.constant_(m.bn3.weight,0)elifisinstance(m, BasicBlock):
  29. nn.init.constant_(m.bn2.weight,0)def_make_layer(self, block, planes, blocks, stride=1, dilate=False):
  30. norm_layer = self._norm_layer
  31. downsample =None
  32. previous_dilation = self.dilation
  33. if dilate:
  34. self.dilation *= stride
  35. stride =1if stride !=1or self.inplanes != planes * block.expansion:
  36. downsample = nn.Sequential(
  37. conv1x1(self.inplanes, planes * block.expansion, stride),
  38. norm_layer(planes * block.expansion),)
  39. layers =[]
  40. layers.append(block(self.inplanes, planes, stride, downsample, self.groups,
  41. self.base_width, previous_dilation, norm_layer))
  42. self.inplanes = planes * block.expansion
  43. for _ inrange(1, blocks):
  44. layers.append(block(self.inplanes, planes, groups=self.groups,
  45. base_width=self.base_width, dilation=self.dilation,
  46. norm_layer=norm_layer))return nn.Sequential(*layers)def_forward_impl(self, x):# See note [TorchScript super()]
  47. x = self.conv1(x)
  48. x = self.bn1(x)
  49. x = self.relu(x)
  50. x = self.maxpool(x)
  51. x = self.layer1(x)
  52. x = self.layer2(x)
  53. x = self.layer3(x)
  54. x = self.layer4(x)
  55. x = self.avgpool(x)
  56. x = torch.flatten(x,1)
  57. x = self.fc(x)return x
  58. defforward(self, x):return self._forward_impl(x)

5. 恒等映射

对残差做一个简单的改进:

  1. x
  2. l
  3. +
  4. 1
  5. =
  6. λ
  7. l
  8. x
  9. l
  10. +
  11. F
  12. (
  13. x
  14. l
  15. ,
  16. W
  17. l
  18. )
  19. \mathbf{x}_{l+1}=\lambda_{l}\mathbf{x}_{l}+\mathcal{F}(\mathbf{x}_{l},\mathcal{W}_{l})
  20. xl+1​=λlxl​+F(xl​,Wl​)

则有:

  1. x
  2. L
  3. =
  4. (
  5. i
  6. =
  7. l
  8. L
  9. 1
  10. λ
  11. i
  12. )
  13. x
  14. l
  15. +
  16. i
  17. =
  18. l
  19. L
  20. 1
  21. F
  22. (
  23. x
  24. l
  25. ,
  26. W
  27. l
  28. )
  29. \mathbf{x}_{L}=(\prod_{i=l}^{L-1}\lambda_{i})\mathbf{x}_{l}+\sum_{i=l}^{L-1}{\mathcal{F}(\mathbf{x}_{l},\mathcal{W}_{l})}
  30. xL​=(i=lL1​λi​)xl​+i=lL1F(xl​,Wl​)

求梯度,有:

  1. L
  2. o
  3. s
  4. s
  5. x
  6. l
  7. =
  8. L
  9. o
  10. s
  11. s
  12. x
  13. L
  14. x
  15. L
  16. x
  17. l
  18. =
  19. L
  20. o
  21. s
  22. s
  23. x
  24. L
  25. (
  26. i
  27. =
  28. l
  29. L
  30. 1
  31. λ
  32. i
  33. +
  34. i
  35. =
  36. l
  37. L
  38. 1
  39. F
  40. (
  41. x
  42. l
  43. ,
  44. W
  45. l
  46. )
  47. x
  48. l
  49. )
  50. \frac{\partial{Loss}}{\partial{\mathbf{x}_{l}}}=\frac{\partial{Loss}}{\partial{\mathbf{x}_{L}}}\frac{\partial{\mathbf{x}_{L}}}{\partial{\mathbf{x}_{l}}}=\frac{\partial{Loss}}{\partial{\mathbf{x}_{L}}}(\prod_{i=l}^{L-1}{\lambda_{i}}+\frac{\partial{\sum_{i=l}^{L-1}{\mathcal{F}(\mathbf{x}_{l},\mathcal{W}_{l})}}}{\partial{\mathbf{x}_{l}}})
  51. xl​∂Loss​=∂xL​∂Loss​∂xl​∂xL​​=∂xL​∂Loss​(i=lL1​λi​+∂xl​∂∑i=lL1F(xl​,Wl​)​)

可以看到,

  1. λ
  2. \lambda
  3. λ大于1的时候,累乘会造成梯度爆炸;在小于1的时候,累乘会造成梯度消失。

6. 分析残差连接

作者给出了一下几种残差连接的变体:
Original

  1. x
  2. l
  3. +
  4. 1
  5. =
  6. x
  7. l
  8. +
  9. F
  10. (
  11. x
  12. l
  13. ,
  14. W
  15. l
  16. )
  17. \mathbf{x}_{l+1}=\mathbf{x}_{l}+\mathcal{F}(\mathbf{x}_{l},\mathcal{W}_{l})
  18. xl+1​=xl​+F(xl​,Wl​)**constant**
  19. x
  20. l
  21. +
  22. 1
  23. =
  24. λ
  25. 1
  26. x
  27. l
  28. +
  29. λ
  30. 2
  31. F
  32. (
  33. x
  34. l
  35. ,
  36. W
  37. l
  38. )
  39. \mathbf{x}_{l+1}=\lambda_{1}\mathbf{x}_{l}+\lambda_{2}\mathcal{F}(\mathbf{x}_{l},\mathcal{W}_{l})
  40. xl+1​=λ1xl​+λ2F(xl​,Wl​)**exclusive gating**
  41. x
  42. l
  43. +
  44. 1
  45. =
  46. (
  47. 1
  48. g
  49. (
  50. x
  51. l
  52. )
  53. )
  54. x
  55. l
  56. +
  57. g
  58. (
  59. x
  60. l
  61. )
  62. F
  63. (
  64. x
  65. l
  66. ,
  67. W
  68. l
  69. )
  70. \mathbf{x}_{l+1}=(1-g(\mathbf{x}_{l}))\mathbf{x}_{l}+g(\mathbf{x}_{l})\mathcal{F}(\mathbf{x}_{l},\mathcal{W}_{l})
  71. xl+1​=(1g(xl​))xl​+g(xl​)F(xl​,Wl​)**shortcut-only gating**
  72. x
  73. l
  74. +
  75. 1
  76. =
  77. (
  78. 1
  79. g
  80. (
  81. x
  82. l
  83. )
  84. )
  85. x
  86. l
  87. +
  88. F
  89. (
  90. x
  91. l
  92. ,
  93. W
  94. l
  95. )
  96. \mathbf{x}_{l+1}=(1-g(\mathbf{x}_{l}))\mathbf{x}_{l}+\mathcal{F}(\mathbf{x}_{l},\mathcal{W}_{l})
  97. xl+1​=(1g(xl​))xl​+F(xl​,Wl​)

其余还包括1×1 conv shortcutdropout shortcut

这几种残差连接的示意图如下所示:
在这里插入图片描述
作者给出的实验结果如下:
在这里插入图片描述
可以看出,原版的残差连接时效果最好的。使用exclusive gate的效果则强烈依赖于偏差的设定。

7. 不同结构的残差模块

作者接下来分析了不同残差模块的设计带来的影响。
在这里插入图片描述

  • 在(b)中,由于BN层的存在,使得 x l + 1 = f ( y l ) \mathbf{x}{l+1}=f(\mathbf{y}{l}) xl+1​=f(yl​)不再是一个线性映射,这会影响残差网络的性能
  • 在(c)中,残差目标最后的ReLU激活层使得残差的输出范围是非负的。然而,无论是数学定义上还是经验上,残差的范围应该是 ( − ∞ , + ∞ ) (-\infty,+\infty) (−∞,+∞),非负的残差影响模型性能
  • zai(d)和(e)中,作者采用了一种pre-activation的想法。 在原版设计中, f f f会影响到残差模块的两个部分: y l + 1 = f ( y l ) + F ( f ( y l ) , W l + 1 ) \mathbf{y}{l+1}=f(\mathbf{y}{l})+\mathcal{F}(f(\mathbf{y}{l}),\mathcal{W}{l+1}) yl+1​=f(yl​)+F(f(yl​),Wl+1​) pre-activation使得 f f f只影响残差部分,不影响恒等映射 y l + 1 = y l + F ^ ( f ( y l ) , W l + 1 ) \mathbf{y}{l+1}=\mathbf{y}{l}+\hat{\mathcal{F}}(f(\mathbf{y}{l}),\mathcal{W}{l+1}) yl+1​=yl​+F^(f(yl​),Wl+1​) 在网络设计中,结构如下:在这里插入图片描述 实际上,当我们将BN层一同放在卷积层的前面的时候,网络性能会进一步提升,这可以看做BN层起到了正则化的作用。

本文转载自: https://blog.csdn.net/d33332/article/details/128725870
版权归原作者 Ace2NoU 所有, 如有侵权,请联系我们删除。

“【论文笔记】一文读懂残差网络ResNet(附代码)”的评论:

还没有评论