Repository: open-mmlab/mmdetection

Issue:
Empirical attention with ResNext backbone
In mmdetectiom v1 I used ResNext backbone with empirical attention 0010_dcn but in v2 I got an error.
Any ideas how to fix this?

My updated config file:
```
_base_ = '../faster_rcnn/faster_rcnn_r50_fpn_1x_coco.py'
model = dict(

    pretrained='open-mmlab://resnext50_32x4d',
    backbone=dict(
        type='ResNeXt',
        depth=50,
        groups=32,
        base_width=4,
        num_stages=4,
        out_indices=(0, 1, 2, 3),
        frozen_stages=1,
        norm_cfg=dict(type='BN', requires_grad=True),
        style='pytorch',
        plugins=[
            dict(
                cfg=dict(
                    type='GeneralizedAttention',
                    spatial_range=-1,
                    num_heads=8,
                    attention_type='0010',
                    kv_stride=2),
                stages=(False, False, True, True),
                position='after_conv2')
        ],
        dcn=dict(type='DCN', deform_groups=1, fallback_on_stride=False),
        stage_with_dcn=(False, True, True, True)))
```

Error:
```
RuntimeError: Given groups=1, weight of size [256, 256, 1, 1], expected input[2, 512, 41, 73] to have 256 channels, but got 512 channels instead
```


Discussion:
Hi @zvadaszi ,
Thanks for your bug report. This is because the code forgets to declare the plugin again in the Bottleneck module of ResNext.
Add the following code after line 85 in [resnext.py]() should works.

```python
        if self.with_plugins:
            self.after_conv1_plugin_names = self.make_block_plugins(
                width, self.after_conv1_plugins)
            self.after_conv2_plugin_names = self.make_block_plugins(
                width, self.after_conv2_plugins)
```

Would you like to create a PR to fix that?
Hi @ZwwWayne ,
Thanks for you reply. If I follow your suggestion an ```AssertionError: duplicate plugin gen_attention_block``` would be raised, because the inherited class's constructor already builds this.
As a quick dirty fix, I tried to comment this code block in resnet.Bottleneck and add it to resnext.Bottleneck, to the place you suggested, but breaks with
```RuntimeError: Given groups=1, weight of size [256, 256, 1, 1], expected input[2, 512, 41, 72] to have 256 channels, but got 512 channels instead```

Resolving patch:
diff --git a/mmdet/models/backbones/resnext.py b/mmdet/models/backbones/resnext.py
--- a/mmdet/models/backbones/resnext.py
+++ b/mmdet/models/backbones/resnext.py
@@ -83,6 +83,27 @@ def __init__(self,
             bias=False)
         self.add_module(self.norm3_name, norm3)
 
+        if self.with_plugins:
+            self._del_block_plugins(self.after_conv1_plugin_names +
+                                    self.after_conv2_plugin_names +
+                                    self.after_conv3_plugin_names)
+            self.after_conv1_plugin_names = self.make_block_plugins(
+                width, self.after_conv1_plugins)
+            self.after_conv2_plugin_names = self.make_block_plugins(
+                width, self.after_conv2_plugins)
+            self.after_conv3_plugin_names = self.make_block_plugins(
+                self.planes * self.expansion, self.after_conv3_plugins)
+
+    def _del_block_plugins(self, plugin_names):
+        """delete plugins for block if exist.
+
+        Args:
+            plugin_names (list[str]): List of plugins name to delete.
+        """
+        assert isinstance(plugin_names, list)
+        for plugin_name in plugin_names:
+            del self._modules[plugin_name]
+
 
 @BACKBONES.register_module()
 class ResNeXt(ResNet):


Test patch:
diff --git a/tests/test_models/test_backbones.py b/tests/test_models/test_backbones.py
--- a/tests/test_models/test_backbones.py
+++ b/tests/test_models/test_backbones.py
@@ -858,6 +858,23 @@ def test_renext_bottleneck():
     x_out = block(x)
     assert x_out.shape == torch.Size([1, 64, 56, 56])
 
+    # Test ResNeXt Bottleneck forward with plugins
+    plugins = [
+        dict(
+            cfg=dict(
+                type='GeneralizedAttention',
+                spatial_range=-1,
+                num_heads=8,
+                attention_type='0010',
+                kv_stride=2),
+            stages=(False, False, True, True),
+            position='after_conv2')
+    ]
+    block = BottleneckX(64, 16, groups=32, base_width=4, plugins=plugins)
+    x = torch.randn(1, 64, 56, 56)
+    x_out = block(x)
+    assert x_out.shape == torch.Size([1, 64, 56, 56])
+
 
 def test_resnext_backbone():
     with pytest.raises(KeyError):