sparse_block.py 6.41 KB
Newer Older
dingchang's avatar
dingchang committed
1
# Copyright (c) OpenMMLab. All rights reserved.
wuyuefeng's avatar
wuyuefeng committed
2
from mmcv.cnn import build_conv_layer, build_norm_layer
wuyuefeng's avatar
wuyuefeng committed
3
4
from torch import nn

wuyuefeng's avatar
wuyuefeng committed
5
from mmdet.models.backbones.resnet import BasicBlock, Bottleneck
VVsssssk's avatar
VVsssssk committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from .spconv import IS_SPCONV2_AVAILABLE

if IS_SPCONV2_AVAILABLE:
    from spconv.pytorch import SparseModule, SparseSequential
else:
    from mmcv.ops import SparseModule, SparseSequential


def replace_feature(out, new_features):
    if 'replace_feature' in out.__dir__():
        # spconv 2.x behaviour
        return out.replace_feature(new_features)
    else:
        out.features = new_features
        return out
wuyuefeng's avatar
wuyuefeng committed
21
22


23
class SparseBottleneck(Bottleneck, SparseModule):
liyinhao's avatar
liyinhao committed
24
25
26
27
28
29
30
    """Sparse bottleneck block for PartA^2.

    Bottleneck block implemented with submanifold sparse convolution.

    Args:
        inplanes (int): inplanes of block.
        planes (int): planes of block.
31
32
33
34
35
36
        stride (int, optional): stride of the first block. Default: 1.
        downsample (Module, optional): down sample module for block.
        conv_cfg (dict, optional): dictionary to construct and config conv
            layer. Default: None.
        norm_cfg (dict, optional): dictionary to construct and config norm
            layer. Default: dict(type='BN').
liyinhao's avatar
liyinhao committed
37
38
    """

wuyuefeng's avatar
wuyuefeng committed
39
40
41
42
43
44
45
46
47
48
    expansion = 4

    def __init__(self,
                 inplanes,
                 planes,
                 stride=1,
                 downsample=None,
                 conv_cfg=None,
                 norm_cfg=None):

49
        SparseModule.__init__(self)
wuyuefeng's avatar
wuyuefeng committed
50
51
52
53
54
55
56
57
58
59
60
61
62
        Bottleneck.__init__(
            self,
            inplanes,
            planes,
            stride=stride,
            downsample=downsample,
            conv_cfg=conv_cfg,
            norm_cfg=norm_cfg)

    def forward(self, x):
        identity = x.features

        out = self.conv1(x)
VVsssssk's avatar
VVsssssk committed
63
64
        out = replace_feature(out, self.bn1(out.features))
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
65
66

        out = self.conv2(out)
VVsssssk's avatar
VVsssssk committed
67
68
        out = replace_feature(out, self.bn2(out.features))
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
69
70

        out = self.conv3(out)
VVsssssk's avatar
VVsssssk committed
71
        out = replace_feature(out, self.bn3(out.features))
wuyuefeng's avatar
wuyuefeng committed
72
73
74
75

        if self.downsample is not None:
            identity = self.downsample(x)

VVsssssk's avatar
VVsssssk committed
76
77
        out = replace_feature(out, out.features + identity)
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
78
79
80
81

        return out


82
class SparseBasicBlock(BasicBlock, SparseModule):
liyinhao's avatar
liyinhao committed
83
84
85
86
87
88
89
    """Sparse basic block for PartA^2.

    Sparse basic block implemented with submanifold sparse convolution.

    Args:
        inplanes (int): inplanes of block.
        planes (int): planes of block.
90
91
92
93
94
95
        stride (int, optional): stride of the first block. Default: 1.
        downsample (Module, optional): down sample module for block.
        conv_cfg (dict, optional): dictionary to construct and config conv
            layer. Default: None.
        norm_cfg (dict, optional): dictionary to construct and config norm
            layer. Default: dict(type='BN').
liyinhao's avatar
liyinhao committed
96
97
    """

wuyuefeng's avatar
wuyuefeng committed
98
99
100
101
102
103
104
105
106
    expansion = 1

    def __init__(self,
                 inplanes,
                 planes,
                 stride=1,
                 downsample=None,
                 conv_cfg=None,
                 norm_cfg=None):
107
        SparseModule.__init__(self)
wuyuefeng's avatar
wuyuefeng committed
108
109
110
111
112
113
114
115
116
117
118
119
        BasicBlock.__init__(
            self,
            inplanes,
            planes,
            stride=stride,
            downsample=downsample,
            conv_cfg=conv_cfg,
            norm_cfg=norm_cfg)

    def forward(self, x):
        identity = x.features

120
        assert x.features.dim() == 2, f'x.features.dim()={x.features.dim()}'
wuyuefeng's avatar
wuyuefeng committed
121
        out = self.conv1(x)
VVsssssk's avatar
VVsssssk committed
122
123
        out = replace_feature(out, self.norm1(out.features))
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
124
125

        out = self.conv2(out)
VVsssssk's avatar
VVsssssk committed
126
        out = replace_feature(out, self.norm2(out.features))
wuyuefeng's avatar
wuyuefeng committed
127
128
129
130

        if self.downsample is not None:
            identity = self.downsample(x)

VVsssssk's avatar
VVsssssk committed
131
132
        out = replace_feature(out, out.features + identity)
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
133
134

        return out
wuyuefeng's avatar
wuyuefeng committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164


def make_sparse_convmodule(in_channels,
                           out_channels,
                           kernel_size,
                           indice_key,
                           stride=1,
                           padding=0,
                           conv_type='SubMConv3d',
                           norm_cfg=None,
                           order=('conv', 'norm', 'act')):
    """Make sparse convolution module.

    Args:
        in_channels (int): the number of input channels
        out_channels (int): the number of out channels
        kernel_size (int|tuple(int)): kernel size of convolution
        indice_key (str): the indice key used for sparse tensor
        stride (int|tuple(int)): the stride of convolution
        padding (int or list[int]): the padding number of input
        conv_type (str): sparse conv type in spconv
        norm_cfg (dict[str]): config of normalization layer
        order (tuple[str]): The order of conv/norm/activation layers. It is a
            sequence of "conv", "norm" and "act". Common examples are
            ("conv", "norm", "act") and ("act", "conv", "norm").

    Returns:
        spconv.SparseSequential: sparse convolution module.
    """
    assert isinstance(order, tuple) and len(order) <= 3
liyinhao's avatar
liyinhao committed
165
    assert set(order) | {'conv', 'norm', 'act'} == {'conv', 'norm', 'act'}
wuyuefeng's avatar
wuyuefeng committed
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197

    conv_cfg = dict(type=conv_type, indice_key=indice_key)

    layers = list()
    for layer in order:
        if layer == 'conv':
            if conv_type not in [
                    'SparseInverseConv3d', 'SparseInverseConv2d',
                    'SparseInverseConv1d'
            ]:
                layers.append(
                    build_conv_layer(
                        conv_cfg,
                        in_channels,
                        out_channels,
                        kernel_size,
                        stride=stride,
                        padding=padding,
                        bias=False))
            else:
                layers.append(
                    build_conv_layer(
                        conv_cfg,
                        in_channels,
                        out_channels,
                        kernel_size,
                        bias=False))
        elif layer == 'norm':
            layers.append(build_norm_layer(norm_cfg, out_channels)[1])
        elif layer == 'act':
            layers.append(nn.ReLU(inplace=True))

198
    layers = SparseSequential(*layers)
wuyuefeng's avatar
wuyuefeng committed
199
    return layers