之前写了mmdetection的模型创建部分,这次以cascade rcnn为例具体看下网络是怎么构建的。
讲网络之前,要先看看配置文件,这里我主要结合官方提供的cascade_mask_rcnn_r50_fpn_1x.py
来看具体实现,关于这些配置项具体的含义可以看mmdetection的configs中的各项参数具体解释
创建cascade rcnn网络
先找到cascade rcnn的定义文件mmdet/models/detectors/cascade_rcnn.py
这里我将cascade rcnn网络的创建过程主要分为5个部分。
- backbone
- neck
- rpn_head
- bbox_head
- mask_head
backbone
cascade rcnn的backb选择的是res50
,创建backbone的方式和之前一样,也是将支持的模型注册到registry
中,只后再通过builder
进行实例化。
resnet
的定义文件在mmdet/models/backbones/resnet.py
def forward(self, x):
x = self.conv1(x)
x = self.norm1(x)
x = self.relu(x)
x = self.maxpool(x)
outs = []
for i, layer_name in enumerate(self.res_layers):
res_layer = getattr(self, layer_name)
x = res_layer(x)
if i in self.out_indices:
outs.append(x)
if len(outs) == 1:
return outs[0]
else:
return tuple(outs)
在forward
中outs取的是多stage的输出,先拼成一个list在转成tuple,取哪些stage是根据config中的out_indices
。
model = dict(
type='CascadeRCNN',
num_stages=3,
pretrained='modelzoo://resnet50',
backbone=dict(
type='ResNet',
depth=50,
num_stages=4,
out_indices=(0, 1, 2, 3),
frozen_stages=1,
style='pytorch'),
backbone是4stage,取了所有的stage。
backbone的主要作用就是提取图像特征。
neck
这部分主要是实现FPN
,FPN讲解
先看下config文件中与FPN相关的部分
neck=dict(
type='FPN',
in_channels=[256, 512, 1024, 2048],
out_channels=256,
num_outs=5),
in_channels
与之前backbone
的输出相匹配,out_channels
为输出纬度。
FPN
定义在mmdet/models/necks/fpn.py
,其中__init__.py
中
for i in range(self.start_level, self.backbone_end_level):
l_conv = ConvModule(
in_channels[i],
out_channels,
1,
normalize=normalize,
bias=self.with_bias,
activation=self.activation,
inplace=False)
fpn_conv = ConvModule(
out_channels,
out_channels,
3,
padding=1,
normalize=normalize,
bias=self.with_bias,
activation=self.activation,
inplace=False)
self.lateral_convs.append(l_conv)
self.fpn_convs.append(fpn_conv)
这里的self.start_level
为0 self.backbone_end_level
为len(in_channels)
,也就是说这里定义的lateral_convs
和fpn_convs
的长度和输入的长度是相等的。
这里可以这样理解,之前backbone的输出是多层的特征图,这里对每层的输出用不同的ConvModule
来处理,再统一channel
数,就完成了高低层特征的融合。可能比较绕,结合代码就比较好理解了。
下面是forward
函数部分代码。
# build laterals
laterals = [
lateral_conv(inputs[i + self.start_level])
for i, lateral_conv in enumerate(self.lateral_convs)
]
# part 1: from original levels
outs = [
self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels)
]
其实这部分也可以看成是