sparse_block.py 7.98 KB
Newer Older
dingchang's avatar
dingchang committed
1
# Copyright (c) OpenMMLab. All rights reserved.
Sun Jiahao's avatar
Sun Jiahao committed
2
from typing import Optional, Tuple, Union
3

xizaoqu's avatar
xizaoqu committed
4
from mmcv.cnn import build_conv_layer, build_norm_layer
5
from mmdet.models.backbones.resnet import BasicBlock, Bottleneck
wuyuefeng's avatar
wuyuefeng committed
6
7
from torch import nn

xizaoqu's avatar
xizaoqu committed
8
from mmdet3d.utils import OptConfigType
VVsssssk's avatar
VVsssssk committed
9
10
11
from .spconv import IS_SPCONV2_AVAILABLE

if IS_SPCONV2_AVAILABLE:
12
    from spconv.pytorch import SparseConvTensor, SparseModule, SparseSequential
VVsssssk's avatar
VVsssssk committed
13
else:
xizaoqu's avatar
xizaoqu committed
14
    from mmcv.ops import SparseConvTensor, SparseModule, SparseSequential
VVsssssk's avatar
VVsssssk committed
15
16


17
18
def replace_feature(out: SparseConvTensor,
                    new_features: SparseConvTensor) -> SparseConvTensor:
VVsssssk's avatar
VVsssssk committed
19
20
21
22
23
24
    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
25
26


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

    Bottleneck block implemented with submanifold sparse convolution.

    Args:
33
34
35
36
37
        inplanes (int): Inplanes of block.
        planes (int): Planes of block.
        stride (int or Tuple[int]): Stride of the first block. Defaults to 1.
        downsample (Module, optional): Down sample module for block.
            Defaults to None.
Sun Jiahao's avatar
Sun Jiahao committed
38
        indice_key (str): Indice key for spconv. Default to None.
39
40
41
42
        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for
            convolution layer. Defaults to None.
        norm_cfg (:obj:`ConfigDict` or dict, optional): Config dict for
            normalization layer. Defaults to None.
liyinhao's avatar
liyinhao committed
43
44
    """

wuyuefeng's avatar
wuyuefeng committed
45
46
47
    expansion = 4

    def __init__(self,
48
49
50
51
                 inplanes: int,
                 planes: int,
                 stride: Union[int, Tuple[int]] = 1,
                 downsample: nn.Module = None,
Sun Jiahao's avatar
Sun Jiahao committed
52
                 indice_key=None,
53
54
                 conv_cfg: OptConfigType = None,
                 norm_cfg: OptConfigType = None) -> None:
wuyuefeng's avatar
wuyuefeng committed
55

56
        SparseModule.__init__(self)
Sun Jiahao's avatar
Sun Jiahao committed
57
58
59
60
61
        if conv_cfg is None:
            conv_cfg = dict(type='SubMConv3d')
        conv_cfg.setdefault('indice_key', indice_key)
        if norm_cfg is None:
            norm_cfg = dict(type='BN1d')
wuyuefeng's avatar
wuyuefeng committed
62
63
64
65
66
67
68
69
70
        Bottleneck.__init__(
            self,
            inplanes,
            planes,
            stride=stride,
            downsample=downsample,
            conv_cfg=conv_cfg,
            norm_cfg=norm_cfg)

71
    def forward(self, x: SparseConvTensor) -> SparseConvTensor:
wuyuefeng's avatar
wuyuefeng committed
72
73
74
        identity = x.features

        out = self.conv1(x)
VVsssssk's avatar
VVsssssk committed
75
76
        out = replace_feature(out, self.bn1(out.features))
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
77
78

        out = self.conv2(out)
VVsssssk's avatar
VVsssssk committed
79
80
        out = replace_feature(out, self.bn2(out.features))
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
81
82

        out = self.conv3(out)
VVsssssk's avatar
VVsssssk committed
83
        out = replace_feature(out, self.bn3(out.features))
wuyuefeng's avatar
wuyuefeng committed
84
85

        if self.downsample is not None:
Sun Jiahao's avatar
Sun Jiahao committed
86
            identity = self.downsample(x).features
wuyuefeng's avatar
wuyuefeng committed
87

VVsssssk's avatar
VVsssssk committed
88
89
        out = replace_feature(out, out.features + identity)
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
90
91
92
93

        return out


94
class SparseBasicBlock(BasicBlock, SparseModule):
liyinhao's avatar
liyinhao committed
95
96
97
98
99
    """Sparse basic block for PartA^2.

    Sparse basic block implemented with submanifold sparse convolution.

    Args:
100
101
102
103
104
        inplanes (int): Inplanes of block.
        planes (int): Planes of block.
        stride (int or Tuple[int]): Stride of the first block. Defaults to 1.
        downsample (Module, optional): Down sample module for block.
            Defaults to None.
Sun Jiahao's avatar
Sun Jiahao committed
105
        indice_key (str): Indice key for spconv. Default to None.
106
107
108
109
        conv_cfg (:obj:`ConfigDict` or dict, optional): Config dict for
            convolution layer. Defaults to None.
        norm_cfg (:obj:`ConfigDict` or dict, optional): Config dict for
            normalization layer. Defaults to None.
liyinhao's avatar
liyinhao committed
110
111
    """

wuyuefeng's avatar
wuyuefeng committed
112
113
114
    expansion = 1

    def __init__(self,
115
116
117
118
                 inplanes: int,
                 planes: int,
                 stride: Union[int, Tuple[int]] = 1,
                 downsample: nn.Module = None,
Sun Jiahao's avatar
Sun Jiahao committed
119
                 indice_key: Optional[str] = None,
120
121
                 conv_cfg: OptConfigType = None,
                 norm_cfg: OptConfigType = None) -> None:
122
        SparseModule.__init__(self)
Sun Jiahao's avatar
Sun Jiahao committed
123
124
125
126
127
        if conv_cfg is None:
            conv_cfg = dict(type='SubMConv3d')
        conv_cfg.setdefault('indice_key', indice_key)
        if norm_cfg is None:
            norm_cfg = dict(type='BN1d')
wuyuefeng's avatar
wuyuefeng committed
128
129
130
131
132
133
134
135
136
        BasicBlock.__init__(
            self,
            inplanes,
            planes,
            stride=stride,
            downsample=downsample,
            conv_cfg=conv_cfg,
            norm_cfg=norm_cfg)

137
    def forward(self, x: SparseConvTensor) -> SparseConvTensor:
wuyuefeng's avatar
wuyuefeng committed
138
139
        identity = x.features

140
        assert x.features.dim() == 2, f'x.features.dim()={x.features.dim()}'
wuyuefeng's avatar
wuyuefeng committed
141
        out = self.conv1(x)
VVsssssk's avatar
VVsssssk committed
142
143
        out = replace_feature(out, self.norm1(out.features))
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
144
145

        out = self.conv2(out)
VVsssssk's avatar
VVsssssk committed
146
        out = replace_feature(out, self.norm2(out.features))
wuyuefeng's avatar
wuyuefeng committed
147
148

        if self.downsample is not None:
Sun Jiahao's avatar
Sun Jiahao committed
149
            identity = self.downsample(x).features
wuyuefeng's avatar
wuyuefeng committed
150

VVsssssk's avatar
VVsssssk committed
151
152
        out = replace_feature(out, out.features + identity)
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
153
154

        return out
wuyuefeng's avatar
wuyuefeng committed
155
156


Sun Jiahao's avatar
Sun Jiahao committed
157
158
159
160
161
162
163
164
165
166
def make_sparse_convmodule(in_channels: int,
                           out_channels: int,
                           kernel_size: Union[int, Tuple[int]],
                           indice_key: Optional[str] = None,
                           stride: Union[int, Tuple[int]] = 1,
                           padding: Union[int, Tuple[int]] = 0,
                           conv_type: str = 'SubMConv3d',
                           norm_cfg: OptConfigType = None,
                           order: Tuple[str] = ('conv', 'norm', 'act'),
                           **kwargs) -> SparseSequential:
wuyuefeng's avatar
wuyuefeng committed
167
168
169
    """Make sparse convolution module.

    Args:
170
171
172
173
174
175
176
177
178
179
        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 or tuple[int]): The stride of convolution.
        padding (int or tuple[int]): The padding number of input.
        conv_type (str): Sparse conv type in spconv. Defaults to 'SubMConv3d'.
        norm_cfg (:obj:`ConfigDict` or dict, optional): Config dict for
            normalization layer. Defaults to None.
        order (Tuple[str]): The order of conv/norm/activation layers. It is a
wuyuefeng's avatar
wuyuefeng committed
180
181
            sequence of "conv", "norm" and "act". Common examples are
            ("conv", "norm", "act") and ("act", "conv", "norm").
182
            Defaults to ('conv', 'norm', 'act').
wuyuefeng's avatar
wuyuefeng committed
183
184
185
186
187

    Returns:
        spconv.SparseSequential: sparse convolution module.
    """
    assert isinstance(order, tuple) and len(order) <= 3
liyinhao's avatar
liyinhao committed
188
    assert set(order) | {'conv', 'norm', 'act'} == {'conv', 'norm', 'act'}
wuyuefeng's avatar
wuyuefeng committed
189
190

    conv_cfg = dict(type=conv_type, indice_key=indice_key)
Sun Jiahao's avatar
Sun Jiahao committed
191
192
    if norm_cfg is None:
        norm_cfg = dict(type='BN1d')
wuyuefeng's avatar
wuyuefeng committed
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

    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))

223
    layers = SparseSequential(*layers)
wuyuefeng's avatar
wuyuefeng committed
224
    return layers