1. forward函数
def forward(self, inputs: Tuple[Tensor]) -> tuple:
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 中的通道数
构建特征金字塔网络中的侧边连接
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] 中的卷积层进行处理
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 模块列表中