Skip to content

Commit

Permalink
Add upsample_cfg support in FPN (#2787)
Browse files Browse the repository at this point in the history
* Add upsample_cfg support in FPN

* small fix

* Add multiple extra conv sources

* small logical fix

* Add neck tests for fpn

* Add neck tests for fpn

* fixed several typos

* resolved issues

* Removed extra_convs_source option

* added necks to apis.rst

* change according to comments

* reconfigured configs
  • Loading branch information
Johnson-Wang authored May 24, 2020
1 parent e903b5c commit 50ffa24
Show file tree
Hide file tree
Showing 12 changed files with 263 additions and 27 deletions.
2 changes: 1 addition & 1 deletion configs/_base_/models/retinanet_r50_fpn.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
in_channels=[256, 512, 1024, 2048],
out_channels=256,
start_level=1,
add_extra_convs=True,
add_extra_convs='on_input',
num_outs=5),
bbox_head=dict(
type='RetinaHead',
Expand Down
3 changes: 1 addition & 2 deletions configs/atss/atss_r50_fpn_1x_coco.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
in_channels=[256, 512, 1024, 2048],
out_channels=256,
start_level=1,
add_extra_convs=True,
extra_convs_on_inputs=False,
add_extra_convs='on_output',
num_outs=5),
bbox_head=dict(
type='ATSSHead',
Expand Down
3 changes: 1 addition & 2 deletions configs/fcos/fcos_r50_caffe_fpn_4x4_1x_coco.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
in_channels=[256, 512, 1024, 2048],
out_channels=256,
start_level=1,
add_extra_convs=True,
extra_convs_on_inputs=False, # use P5
add_extra_convs='on_output', # use P5
num_outs=5,
relu_before_extra_convs=True),
bbox_head=dict(
Expand Down
3 changes: 1 addition & 2 deletions configs/fcos/fcos_r50_caffe_fpn_gn-head_4x4_1x_coco.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
in_channels=[256, 512, 1024, 2048],
out_channels=256,
start_level=1,
add_extra_convs=True,
extra_convs_on_inputs=False, # use P5
add_extra_convs='on_output', # use P5
num_outs=5,
relu_before_extra_convs=True),
bbox_head=dict(
Expand Down
2 changes: 1 addition & 1 deletion configs/foveabox/fovea_r50_fpn_4x4_1x_coco.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
out_channels=256,
start_level=1,
num_outs=5,
add_extra_convs=True),
add_extra_convs='on_input'),
bbox_head=dict(
type='FoveaHead',
num_classes=80,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
in_channels=[256, 512, 1024, 2048],
out_channels=256,
start_level=1,
add_extra_convs=True,
add_extra_convs='on_input',
num_outs=5),
bbox_head=dict(
type='GARetinaHead',
Expand Down
2 changes: 1 addition & 1 deletion configs/libra_rcnn/libra_retinanet_r50_fpn_1x_coco.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
in_channels=[256, 512, 1024, 2048],
out_channels=256,
start_level=1,
add_extra_convs=True,
add_extra_convs='on_input',
num_outs=5),
dict(
type='BFP',
Expand Down
2 changes: 1 addition & 1 deletion configs/reppoints/reppoints_moment_r50_fpn_1x_coco.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
in_channels=[256, 512, 1024, 2048],
out_channels=256,
start_level=1,
add_extra_convs=True,
add_extra_convs='on_input',
num_outs=5),
bbox_head=dict(
type='RepPointsHead',
Expand Down
2 changes: 1 addition & 1 deletion demo/mmdet_inference_colab.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -534,4 +534,4 @@
]
}
]
}
}
5 changes: 5 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ backbones
.. automodule:: mmdet.models.backbones
:members:

necks
^^^^^^^^^^^^
.. automodule:: mmdet.models.necks
:members:

dense_heads
^^^^^^^^^^^^
.. automodule:: mmdet.models.dense_heads
Expand Down
63 changes: 48 additions & 15 deletions mmdet/models/necks/fpn.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,19 @@ class FPN(nn.Module):
build the feature pyramid. Default: 0.
end_level (int): Index of the end input backbone level (exclusive) to
build the feature pyramid. Default: -1, which means the last level.
add_extra_convs (bool): Whether to add conv layers on top of the
original feature maps. Default: False.
extra_convs_on_inputs (bool): Whether to apply extra conv on
the original feature from the backbone. Default: False.
add_extra_convs (bool | str): If bool, it decides whether to add conv
layers on top of the original feature maps. Default to False.
If True, its actual mode is specified by `extra_convs_on_inputs`.
If str, it specifies the source feature map of the extra convs.
Only the following options are allowed
- 'on_input': Last feat map of neck inputs (i.e. backbone feature).
- 'on_lateral': Last feature map after lateral convs.
- 'on_output': The last output feature map after fpn convs.
extra_convs_on_inputs (bool, deprecated): Whether to apply extra convs
on the original feature from the backbone. If True,
it is equivalent to `add_extra_convs='on_input'`. If False, it is
equivalent to set `add_extra_convs='on_output'`. Default to True.
relu_before_extra_convs (bool): Whether to apply relu before the extra
conv. Default: False.
no_norm_on_lateral (bool): Whether to apply norm on lateral.
Expand All @@ -34,6 +43,8 @@ class FPN(nn.Module):
norm_cfg (dict): Config dict for normalization layer. Default: None.
act_cfg (str): Config dict for activation layer in ConvModule.
Default: None.
upsample_cfg (dict): Config dict for interpolate layer.
Default: `dict(mode='nearest')`
Example:
>>> import torch
Expand Down Expand Up @@ -63,7 +74,8 @@ def __init__(self,
no_norm_on_lateral=False,
conv_cfg=None,
norm_cfg=None,
act_cfg=None):
act_cfg=None,
upsample_cfg=dict(mode='nearest')):
super(FPN, self).__init__()
assert isinstance(in_channels, list)
self.in_channels = in_channels
Expand All @@ -73,6 +85,7 @@ def __init__(self,
self.relu_before_extra_convs = relu_before_extra_convs
self.no_norm_on_lateral = no_norm_on_lateral
self.fp16_enabled = False
self.upsample_cfg = upsample_cfg.copy()

if end_level == -1:
self.backbone_end_level = self.num_ins
Expand All @@ -85,7 +98,17 @@ def __init__(self,
self.start_level = start_level
self.end_level = end_level
self.add_extra_convs = add_extra_convs
self.extra_convs_on_inputs = extra_convs_on_inputs
assert isinstance(add_extra_convs, (str, bool))
if isinstance(add_extra_convs, str):
# Extra_convs_source choices: 'on_input', 'on_lateral', 'on_output'
assert add_extra_convs in ('on_input', 'on_lateral', 'on_output')
elif add_extra_convs: # True
if extra_convs_on_inputs:
# For compatibility with previous release
# TODO: deprecate `extra_convs_on_inputs`
self.add_extra_convs = 'on_input'
else:
self.add_extra_convs = 'on_output'

self.lateral_convs = nn.ModuleList()
self.fpn_convs = nn.ModuleList()
Expand Down Expand Up @@ -114,9 +137,9 @@ def __init__(self,

# add extra conv layers (e.g., RetinaNet)
extra_levels = num_outs - self.backbone_end_level + self.start_level
if add_extra_convs and extra_levels >= 1:
if self.add_extra_convs and extra_levels >= 1:
for i in range(extra_levels):
if i == 0 and self.extra_convs_on_inputs:
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
Expand Down Expand Up @@ -151,9 +174,15 @@ def forward(self, inputs):
# build top-down path
used_backbone_levels = len(laterals)
for i in range(used_backbone_levels - 1, 0, -1):
prev_shape = laterals[i - 1].shape[2:]
laterals[i - 1] += F.interpolate(
laterals[i], size=prev_shape, mode='nearest')
# 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:
laterals[i - 1] += F.interpolate(laterals[i],
**self.upsample_cfg)
else:
prev_shape = laterals[i - 1].shape[2:]
laterals[i - 1] += F.interpolate(
laterals[i], size=prev_shape, **self.upsample_cfg)

# build outputs
# part 1: from original levels
Expand All @@ -169,11 +198,15 @@ def forward(self, inputs):
outs.append(F.max_pool2d(outs[-1], 1, stride=2))
# add conv layers on top of original feature maps (RetinaNet)
else:
if self.extra_convs_on_inputs:
orig = inputs[self.backbone_end_level - 1]
outs.append(self.fpn_convs[used_backbone_levels](orig))
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:
outs.append(self.fpn_convs[used_backbone_levels](outs[-1]))
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])))
Expand Down
Loading

0 comments on commit 50ffa24

Please sign in to comment.