从ResNet到DenseNet

image.png
ResNet和DenseNet的关键区别在于,DenseNet输出是连接(用图中的[,]表示)而不是如ResNet的简单相加。 因此,在应用越来越复杂的函数序列后,我们执行从x到其展开式的映射:
DenseNet - 图2

最后,将这些展开式结合到多层感知机中,再次减少特征的数量。 实现起来非常简单:我们不需要添加术语,而是将它们连接起来。 DenseNet这个名字由变量之间的“稠密连接”而得来,最后一层与之前的所有层紧密相连。
image.png
稠密网络主要由2部分构成:稠密块(dense block)和过渡层(transition layer)。 前者定义如何连接输入和输出,而后者则控制通道数量,使其不会太复杂

  1. import torch
  2. from torch import nn
  3. def conv_block(input_channels, num_channels):
  4. return nn.Sequential(
  5. nn.BatchNorm2d(input_channels),
  6. nn.ReLU(),
  7. nn.Conv2d(input_channels, num_channels, kernel_size=3, padding=1))

一个稠密块由多个卷积块组成,每个卷积块使用相同数量的输出通道。
然而,在前向传播中,我们将每个卷积块的输入和输出在通道维度上连结

  1. class DenseBlock(nn.Module):
  2. def __init__(self, num_convs, input_channels, num_channels):
  3. super(DenseBlock, self).__init__()
  4. layer = []
  5. for i in range(num_convs):
  6. layer.append(conv_block(num_channels * i + input_channels, num_channels))
  7. self.net = nn.Sequential(*layer)
  8. def forward(self,X):
  9. for blk in self.net:
  10. Y = blk(X)
  11. X = torch.cat((X,Y), dim=1)
  12. return X

过渡层

由于每个稠密块都会带来通道数的增加,使用过多则会过于复杂化模型,而过渡层可以用来控制模型复杂度,它通过1x1卷积来减小通道数,并使用步幅为2的池化来进一步降低模型复杂度

  1. def transition_block(input_channels, num_channels):
  2. return nn.Sequential(
  3. nn.BatchNorm2d(input_channels),
  4. nn.ReLU(),
  5. nn.Conv2d(input_channels, num_channels, kernel_size=1),
  6. nn.AvgPool2d(kernel_size=2, stride=2))