ResNet50的源码路径为mmdet/models/backbones/resnet.py。
ResNet系列网络是经典网络之一,后续很多变种(如ResNeXt和ResNeSt等)和其他网络都借鉴了其残差块结构,首先先对一些结构名词做一些解释,便于理解。残差块,即Residual Block,也就是接下来要讲解的BasicBlock和Bottleneck两个基本模块,若干卷积层、归一化层和激活层构成上述两个基本模块,若干基本模块构成一个stage,若干stage构成整体的网络。
ResNet根据网络层数不同,可分为5个模型。分别是ResNet-18、ResNet-34、ResNet-50、ResNet-101和ResNet-152。
除了网络层数不同外,构成模型的基础结构也有所差异,ResNet-18和ResNet-34的基础结构为BasicBlock,其他模型的基础结构为Bottleneck。两者的结构如下图所示(图像截取自B站大佬的讲解视频),左侧为BasicBlock结构,右侧为Bottleneck结构。
该结构较为简单,__init__()中定义了相关的卷积、归一化和激活函数操作,forward()中定义特征图的前向传播路径。
需要注意的是,每个stage之间输出特征矩阵的维度不同,例如第一个stage(即表格中的conv2_x)输出特征矩阵为56*56*64,该特征矩阵作为输入,会输入到第二个stage(即表格中的conv3_x)中,因此第二个stage中的第一个BasicBlock的捷径分支需要对channel维度进行相应的升维处理,由64变为128,;同时主分支对空间维度进行下采样,由56*56变为28*28,对通道维度进行升维处理,由64变为128,最后完成相加操作。
因此,我们需要对每一个stage的第一个残差块做特殊设置。
该结构是构建层数较深模型的基本结构,采用1×1的卷积操作目的是减少参数量,特别是在层数较多的模型中,可以减少相当数量的参数,具体参数量的计算参考讲解视频。
在初始化函数中,style关键字参数有'pytorch'和'caffe'两种选择,区别在于前者将stride=2(即空间维度的下采样操作)整合在3×3卷积中,后者整合在第一个1×1卷积中;
同时,该结构还支持plugin(类似于插件),插入的位置在三个卷积层之后;
首先介绍结构配置:
arch_settings = {
18: (BasicBlock, (2, 2, 2, 2)),
34: (BasicBlock, (3, 4, 6, 3)),
50: (Bottleneck, (3, 4, 6, 3)),
101: (Bottleneck, (3, 4, 23, 3)),
152: (Bottleneck, (3, 8, 36, 3))
}
结构配置中指定了网络深度,残差块类型和每个stage中残差块的个数;
接下来介绍初始化参数的含义:
def __init__(self,
# 网络深度
depth,
# 输入图像的channel数
in_channels=3,
# 主干卷积层的channel数,默认等于base_channels
stem_channels=None,
base_channels=64,
# stage数量
num_stages=4,
# 每个stage第一个残差块的stride参数
strides=(1, 2, 2, 2),
# 膨胀(空洞)卷积参数设置
dilations=(1, 1, 1, 1),
# 输出特征图的索引,每个stage对应一个
out_indices=(0, 1, 2, 3),
# 风格设置
style='pytorch',
# 是否用3个3×3的卷积核代替主干上1个7×7的卷积核
deep_stem=False,
# 是否使用平均池化代替stride为2的卷积操作进行下采样
avg_down=False,
# 冻结层数,-1表示不冻结
frozen_stages=-1,
# 构建卷积层的配置
conv_cfg=None,
# 构建归一化层的配置
norm_cfg=dict(type='BN', requires_grad=True),
norm_eval=True,
# 是否使用dcn(可变形卷积)
dcn=None,
# 指定哪个stage使用dcn
stage_with_dcn=(False, False, False, False),
plugins=None,
with_cp=False,
# 是否对残差块进行0初始化
zero_init_residual=True,
# 预训练模型(已弃用,若指定会自动调用init_cfg)
pretrained=None,
# 指定预训练模型
init_cfg=None):
由于篇幅有限,暂不解析源码中每个函数的实现过程,大家可根据参数的含义推断函数功能并自行阅读源码。