【mmdetection代码解读 3.x版本】FPN层的解读

文章目录

    • 1. forward函数
      • 1.1 self.fpn_convs的构建

1. forward函数

def forward(self, inputs: Tuple[Tensor]) -> tuple:

【mmdetection代码解读 3.x版本】FPN层的解读_第1张图片

assert len(inputs) == len(self.in_channels)

# build laterals
laterals = [
    lateral_conv(inputs[i + self.start_level])
    for i, lateral_conv in enumerate(self.lateral_convs)
]

查输入特征 inputs 的数量是否等于定义的 self.in_channels 中的通道数
构建特征金字塔网络中的侧边连接

在这里插入图片描述
【mmdetection代码解读 3.x版本】FPN层的解读_第2张图片

used_backbone_levels = len(laterals)
for i in range(used_backbone_levels - 1, 0, -1):
    # In some cases, fixing `scale factor` (e.g. 2) is preferred, but
    #  it cannot co-exist with `size` in `F.interpolate`.
    if 'scale_factor' in self.upsample_cfg:
        # fix runtime error of "+=" inplace operation in PyTorch 1.10
        laterals[i - 1] = laterals[i - 1] + F.interpolate(
            laterals[i], **self.upsample_cfg)
    else:
        prev_shape = laterals[i - 1].shape[2:]
        laterals[i - 1] = laterals[i - 1] + F.interpolate(
            laterals[i], size=prev_shape, **self.upsample_cfg)

取了实际用于 top-down 路径的侧边连接的数量
循环,从最顶层的侧边连接开始向下构建 top-down 路径:
	条件判断,检查是否设置了 'scale_factor'
	根据上一层的尺寸,使用 F.interpolate 对下一层的侧边连接进行上采样,然后将两层的特征相加

在这里插入图片描述

outs = [
         self.fpn_convs[i](laterals[i]) for i in range(used_backbone_levels)
        ]

通过对输入的侧边连接 laterals 进行卷积操作,生成用于网络输出的特征图 outs

在这里插入图片描述

 if self.num_outs > len(outs):
     # use max pool to get more levels on top of outputs
     # (e.g., Faster R-CNN, Mask R-CNN)
     if not self.add_extra_convs:
         for i in range(self.num_outs - used_backbone_levels):
             outs.append(F.max_pool2d(outs[-1], 1, stride=2))
     # add conv layers on top of original feature maps (RetinaNet)
     else:
         if self.add_extra_convs == 'on_input':
             extra_source = inputs[self.backbone_end_level - 1]
         elif self.add_extra_convs == 'on_lateral':
             extra_source = laterals[-1]
         elif self.add_extra_convs == 'on_output':
             extra_source = outs[-1]
         else:
             raise NotImplementedError
         outs.append(self.fpn_convs[used_backbone_levels](extra_source))
         for i in range(used_backbone_levels + 1, self.num_outs):
             if self.relu_before_extra_convs:
                 outs.append(self.fpn_convs[i](F.relu(outs[-1])))
             else:
                 outs.append(self.fpn_convs[i](outs[-1]))
 return tuple(outs)

检查是否需要在 outs 中添加额外的层级,以确保输出的特征图数量达到 self.num_outs
如果 self.add_extra_convs 参数设置为 False:
	使用最大池化来生成额外的输出层级,F.max_pool2d 用于对 outs 中的最后一层进行最大池化,将分辨率减半
如果 self.add_extra_convs 参数设置为其他值:
	根据 self.add_extra_convs 的设置,选择额外层级的输入来源
	使用 self.fpn_convs 中的卷积层对额外层级的输入 extra_source 进行处理,生成一个额外的输出层级
	如果仍然需要更多的输出层级,进行循环操作:
		检查是否在额外卷积前应用了 ReLU 激活函数,如果是:
			将 ReLU 激活函数应用于前一个输出层级 的特征图,并使用 self.fpn_convs[i] 中的卷积层进行处理
		如果没有应用 ReLU:
			将前一个输出层级 outs[-1] 直接送入 self.fpn_convs[i] 中的卷积层进行处理

【mmdetection代码解读 3.x版本】FPN层的解读_第3张图片
【mmdetection代码解读 3.x版本】FPN层的解读_第4张图片
在这里插入图片描述


1.1 self.fpn_convs的构建

self.lateral_convs = nn.ModuleList()
self.fpn_convs = nn.ModuleList()

for i in range(self.start_level, self.backbone_end_level):
    l_conv = ConvModule(
        in_channels[i],
        out_channels,
        1,
        conv_cfg=conv_cfg,
        norm_cfg=norm_cfg if not self.no_norm_on_lateral else None,
        act_cfg=act_cfg,
        inplace=False)
    fpn_conv = ConvModule(
        out_channels,
        out_channels,
        3,
        padding=1,
        conv_cfg=conv_cfg,
        norm_cfg=norm_cfg,
        act_cfg=act_cfg,
        inplace=False)

    self.lateral_convs.append(l_conv)
    self.fpn_convs.append(fpn_conv)

创建了一个空的 PyTorch 模块列表,用于存储侧边连接的卷积层
创建了另一个空的 PyTorch 模块列表,用于存储上采样卷积层
通过循环,对从 self.start_level 到 self.backbone_end_level - 1:
	l_conv 是一个 1x1 卷积层,用于对输入特征进行降维,将通道数从输入级别的 in_channels[i] 减少到 out_channels
	fpn_conv 是一个 3x3 卷积层,用于对输入特征进行上采样
	将侧边连接卷积层 l_conv 添加到 lateral_convs 模块列表中
	将上采样卷积层 fpn_conv 添加到 fpn_convs 模块列表中
extra_levels = num_outs - self.backbone_end_level + self.start_level
if self.add_extra_convs and extra_levels >= 1:
    for i in range(extra_levels):
        if i == 0 and self.add_extra_convs == 'on_input':
            in_channels = self.in_channels[self.backbone_end_level - 1]
        else:
            in_channels = out_channels
        extra_fpn_conv = ConvModule(
            in_channels,
            out_channels,
            3,
            stride=2,
            padding=1,
            conv_cfg=conv_cfg,
            norm_cfg=norm_cfg,
            act_cfg=act_cfg,
            inplace=False)
        self.fpn_convs.append(extra_fpn_conv)

计算需要添加的额外级别数量
检查是否需要添加额外级别并且是否有至少一个额外级别要添加:
	循环遍历要添加的每个额外级别:
		在第一个额外级别时,检查配置是否要将额外级别卷积添加到输入上:
			将额外级别卷积添加到输入级别上
		否则:
			设置输入通道数等于输出通道数。这是因为额外级别的输入是上一个额外级别的输出
			extra_fpn_conv 是一个额外级别的卷积层。它是一个 3x3 大小的卷积核,带有 2 的步长,用于将上一个级别的特征图上采样
		将额外级别的卷积层添加到 fpn_convs 模块列表中

你可能感兴趣的:(人工智能,目标检测,深度学习)