在残差块中,输入可以通过跨层数据线路更快地向前传播
image.png

残差块的实现

通过1x1卷积调整通道数和分辨率
image.png

  1. import torch
  2. from torch import nn
  3. from torch.nn import functional as F
  4. class Residual(nn.Module):
  5. def __init__(self, input_channels, num_channels, use_1x1conv=False, strides=1):
  6. super(Residual, self).__init__()
  7. self.conv1 = nn.Conv2d(input_channels, num_channels
  8. kernel_size=3, padding=1)
  9. self.conv2 = nn.Conv2d(num_channels, num_channels,
  10. kernel_size=3, padding=1)
  11. if use_1x1conv:
  12. self.conv3 = nn.Conv2d(input_channels, num_channels,
  13. kernel_size=1, stride=strides)
  14. else:
  15. self.conv3 = None
  16. self.bn1 = nn.BatchNorm2d(num_channels)
  17. self.bn2 = nn.BatchNorm2d(num_channels)
  18. def forward(self, X):
  19. Y = F.relu(self.bn1(self.conv1(X)))
  20. Y = self.bn2(self.conv2(Y))
  21. if self.conv3:
  22. X = self.conv3(X)
  23. Y += X
  24. return F.relu(Y)

ResNet模型

ResNet的前两层跟之前介绍的GoogLeNet中的一样: 在输出通道数为64、步幅为2的7×7卷积层后,接步幅为2的3×3的最大汇聚层。 不同之处在于ResNet每个卷积层后增加了批量规范化层。
image.png

  1. b1 = nn.Sequential(nn.Conv2d(1, 64, kernel_size=7, padding=3),
  2. nn.BatchNorm2d(64),
  3. nn.ReLU(),
  4. nn.MaxPool2d(kernel_size=3, stride=2, padding=1))

GoogLeNet在后面接了4个由Inception块组成的模块。 ResNet则使用4个由残差块组成的模块,每个模块使用若干个同样输出通道数的残差块。 第一个模块的通道数同输入通道数一致。 由于之前已经使用了步幅为2的最大汇聚层,所以无须减小高和宽。 之后的每个模块在第一个残差块里将上一个模块的通道数翻倍,并将高和宽减半。
下面我们来实现这个模块。注意,我们对第一个模块做了特别处理。

  1. def resnet_block(input_channels, num_channels, num_residuals, first_block=False):
  2. blk = []
  3. for i in range(num_residuals):
  4. if i==0 and not first_block:
  5. blk.append(Residual(input_channels, num_channels,
  6. use_1x1conv=True, strides=2))
  7. else:
  8. blk.append(Residual(num_channels, num_channels))
  9. return blk

image.png