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

wuyuefeng's avatar
wuyuefeng 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

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:
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
38
39
40
41
        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.
        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
42
43
    """

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

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

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

64
    def forward(self, x: SparseConvTensor) -> SparseConvTensor:
wuyuefeng's avatar
wuyuefeng committed
65
66
67
        identity = x.features

        out = self.conv1(x)
VVsssssk's avatar
VVsssssk committed
68
69
        out = replace_feature(out, self.bn1(out.features))
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
70
71

        out = self.conv2(out)
VVsssssk's avatar
VVsssssk committed
72
73
        out = replace_feature(out, self.bn2(out.features))
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
74
75

        out = self.conv3(out)
VVsssssk's avatar
VVsssssk committed
76
        out = replace_feature(out, self.bn3(out.features))
wuyuefeng's avatar
wuyuefeng committed
77
78
79
80

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

VVsssssk's avatar
VVsssssk committed
81
82
        out = replace_feature(out, out.features + identity)
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
83
84
85
86

        return out


87
class SparseBasicBlock(BasicBlock, SparseModule):
liyinhao's avatar
liyinhao committed
88
89
90
91
92
    """Sparse basic block for PartA^2.

    Sparse basic block implemented with submanifold sparse convolution.

    Args:
93
94
95
96
97
98
99
100
101
        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.
        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
102
103
    """

wuyuefeng's avatar
wuyuefeng committed
104
105
106
    expansion = 1

    def __init__(self,
107
108
109
110
111
112
                 inplanes: int,
                 planes: int,
                 stride: Union[int, Tuple[int]] = 1,
                 downsample: nn.Module = None,
                 conv_cfg: OptConfigType = None,
                 norm_cfg: OptConfigType = None) -> None:
113
        SparseModule.__init__(self)
wuyuefeng's avatar
wuyuefeng committed
114
115
116
117
118
119
120
121
122
        BasicBlock.__init__(
            self,
            inplanes,
            planes,
            stride=stride,
            downsample=downsample,
            conv_cfg=conv_cfg,
            norm_cfg=norm_cfg)

123
    def forward(self, x: SparseConvTensor) -> SparseConvTensor:
wuyuefeng's avatar
wuyuefeng committed
124
125
        identity = x.features

126
        assert x.features.dim() == 2, f'x.features.dim()={x.features.dim()}'
wuyuefeng's avatar
wuyuefeng committed
127
        out = self.conv1(x)
VVsssssk's avatar
VVsssssk committed
128
129
        out = replace_feature(out, self.norm1(out.features))
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
130
131

        out = self.conv2(out)
VVsssssk's avatar
VVsssssk committed
132
        out = replace_feature(out, self.norm2(out.features))
wuyuefeng's avatar
wuyuefeng committed
133
134
135
136

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

VVsssssk's avatar
VVsssssk committed
137
138
        out = replace_feature(out, out.features + identity)
        out = replace_feature(out, self.relu(out.features))
wuyuefeng's avatar
wuyuefeng committed
139
140

        return out
wuyuefeng's avatar
wuyuefeng committed
141
142


143
144
145
146
147
148
149
150
151
152
153
def make_sparse_convmodule(
    in_channels: int,
    out_channels: int,
    kernel_size: Union[int, Tuple[int]],
    indice_key: str,
    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')
) -> SparseSequential:
wuyuefeng's avatar
wuyuefeng committed
154
155
156
    """Make sparse convolution module.

    Args:
157
158
159
160
161
162
163
164
165
166
        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
167
168
            sequence of "conv", "norm" and "act". Common examples are
            ("conv", "norm", "act") and ("act", "conv", "norm").
169
            Defaults to ('conv', 'norm', 'act').
wuyuefeng's avatar
wuyuefeng committed
170
171
172
173
174

    Returns:
        spconv.SparseSequential: sparse convolution module.
    """
    assert isinstance(order, tuple) and len(order) <= 3
liyinhao's avatar
liyinhao committed
175
    assert set(order) | {'conv', 'norm', 'act'} == {'conv', 'norm', 'act'}
wuyuefeng's avatar
wuyuefeng committed
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207

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

208
    layers = SparseSequential(*layers)
wuyuefeng's avatar
wuyuefeng committed
209
    return layers