前言
我们拿到一个网络,关注的问题总是,给定尺寸的输入后,网络的输出通道数、尺寸是多少?
假设 ResNet101 的代码是像下面这样写的(删除了一些内参和类方法):
class ResNet(nn.Module):
def __init__(self, ...):
super(ResNet, self).__init__()
"""
ResNet101
假设 input 的 shape 为 (64, 5, 512, 512)
"""
self.conv1 = nn.Conv2d(5, 64, kernel_size=7, stride=2, padding=3, bias=False) # (64, 64, 256, 256)
self.bn1 = BatchNorm(64)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) # (64, 64, 128, 128)
self.layer1 = self._make_layer(Bottleneck, 64, 3, stride=1) # (64, 256, 128, 128)
self.layer2 = self._make_layer(Bottleneck, 128, 4, stride=2) # (64, 512, 64, 64)
self.layer3 = self._make_layer(Bottleneck, 256, 23, stride=2) # (64, 1024, 32, 32)
self.layer4 = self._make_layer(Bottleneck, 512, 3, stride=1) # (64, 2048, 32, 32)
首先要知道,数据集经过 DataLoader 打包后,通常包含四个维度,(Batchsize, Channels, Height, Width),所以我假设 input 的 shape 为 (64, 5, 512, 512)。
当经过卷积层或池化层时,只需观察 out_channels (池化层不改变 channels 所以不用看)和 stride,输出数据的通道数=out_channels,输出数据的尺寸=input_size / stride。
padding 和 kernel_size,通常会互相配合设置,不会影响输出尺寸,这从公式也可以看出分母 s 是最影响输出尺寸的:
o u t p u t _ s i z e = n − d × ( k − 1 ) + 2 p − 1 s + 1 {output\_size}=\frac{n-d\times (k-1)+2p-1}{s}+1 output_size=sn−d×(k−1)+2p−1+1
一、conv1_x
根据上面介绍,那么数据经过 conv1 后通道和尺寸的变化是这样的:
二、conv2_x,conv3_x,conv4_x,conv5_x
self.__make_layer
用于构造 conv2_x 到 conv5_x 块:
每个 conv_x 块由多个 Bottleneck 结构构成,如下图所示:
其中,用户输入的 stride 将作用于每个 conv_x 块的,第一个 Bottleneck 的第 2 个卷积层,stride 直接影响着图像尺寸的变化,输出图像的尺寸变为 input_size / stride:
self.__make_layer
的 planes
参数则控制着 Bottleneck 中卷积层的输出通道数,每经过一个 conv_x,输出的通道数会变成原来的 4 倍(这是由 Bottleneck.expansion 参数设置的):
在每个self.layer = __make_layer()
中,self.inplanes 记录着上一个 conv_x 的 out_channels
self.inplanes = 64 # 初始值
self.inplanes = planes * block.expansion # 更新inplanes,用于下一个块的in_channels