Skip to content

Commit d5c6fe8

Browse files
committed
channelwise conv
1 parent 032bb51 commit d5c6fe8

6 files changed

Lines changed: 182 additions & 113 deletions

File tree

Lines changed: 83 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# Copyright (c) Chris Choy (chrischoy@ai.stanford.edu).
1+
# Copyright (c) 2020 NVIDIA CORPORATION.
2+
# Copyright (c) 2018-2020 Chris Choy (chrischoy@ai.stanford.edu).
23
#
34
# Permission is hereby granted, free of charge, to any person obtaining a copy of
45
# this software and associated documentation files (the "Software"), to deal in
@@ -27,13 +28,24 @@
2728
import torch
2829
from torch.nn import Parameter
2930

30-
from SparseTensor import SparseTensor
31-
from Common import RegionType, MinkowskiModuleBase, KernelGenerator, \
32-
prep_args, convert_to_int_list, convert_to_int_tensor
33-
from MinkowskiCoords import CoordsKey
31+
from MinkowskiSparseTensor import SparseTensor
32+
from MinkowskiEngineBackend._C import CoordinateMapKey, RegionType
33+
from MinkowskiCommon import MinkowskiModuleBase
34+
from MinkowskiKernelGenerator import KernelGenerator
3435

3536

3637
class MinkowskiChannelwiseConvolution(MinkowskiModuleBase):
38+
39+
__slots__ = (
40+
"in_channels",
41+
"out_channels",
42+
"kernel_generator",
43+
"dimension",
44+
"kernel",
45+
"bias",
46+
"conv",
47+
)
48+
3749
r"""Channelwise (Depthwise) Convolution layer for a sparse tensor.
3850
3951
@@ -57,14 +69,16 @@ class MinkowskiChannelwiseConvolution(MinkowskiModuleBase):
5769
5870
"""
5971

60-
def __init__(self,
61-
in_channels,
62-
kernel_size=-1,
63-
stride=1,
64-
dilation=1,
65-
has_bias=False,
66-
kernel_generator=None,
67-
dimension=-1):
72+
def __init__(
73+
self,
74+
in_channels,
75+
kernel_size=-1,
76+
stride=1,
77+
dilation=1,
78+
bias=False,
79+
kernel_generator=None,
80+
dimension=-1,
81+
):
6882
r"""convolution on a sparse tensor
6983
7084
Args:
@@ -87,7 +101,7 @@ def __init__(self,
87101
convolution kernel. When a list is given, the length must be D and
88102
each element is an axis specific dilation. All elements must be > 0.
89103
90-
:attr:`has_bias` (bool, optional): if True, the convolution layer
104+
:attr:`bias` (bool, optional): if True, the convolution layer
91105
has a bias.
92106
93107
:attr:`kernel_generator` (:attr:`MinkowskiEngine.KernelGenerator`,
@@ -107,97 +121,93 @@ def __init__(self,
107121
kernel_size=kernel_size,
108122
stride=stride,
109123
dilation=dilation,
110-
dimension=dimension)
111-
else:
112-
kernel_size = kernel_generator.kernel_size
113-
114-
stride = convert_to_int_tensor(stride, dimension)
115-
kernel_size = convert_to_int_tensor(kernel_size, dimension)
116-
dilation = convert_to_int_tensor(dilation, dimension)
124+
dimension=dimension,
125+
)
117126

118-
kernel_volume = kernel_generator.kernel_volume
127+
self.kernel_generator = kernel_generator
119128

120129
self.in_channels = in_channels
121-
self.kernel_size = kernel_size
122-
self.kernel_volume = kernel_volume
123-
self.stride = stride
124-
self.dilation = dilation
125-
self.kernel_generator = kernel_generator
126130
self.dimension = dimension
127-
self.use_mm = False # use matrix multiplication when kernel is 1
128131

129-
Tensor = torch.FloatTensor
130-
self.kernel_shape = (self.kernel_volume, self.in_channels)
132+
self.kernel_shape = (kernel_generator.kernel_volume, self.in_channels)
131133

134+
Tensor = torch.FloatTensor
132135
self.kernel = Parameter(Tensor(*self.kernel_shape))
133-
self.bias = Parameter(Tensor(1, in_channels)) if has_bias else None
134-
self.has_bias = has_bias
136+
self.bias = Parameter(Tensor(1, in_channels)) if bias else None
137+
135138
self.reset_parameters()
136139

137-
def forward(self,
138-
input: SparseTensor,
139-
coords: Union[torch.IntTensor, CoordsKey, SparseTensor] = None):
140+
def forward(
141+
self,
142+
input: SparseTensor,
143+
coords: Union[torch.IntTensor, CoordinateMapKey, SparseTensor] = None,
144+
):
140145
r"""
141146
:attr:`input` (`MinkowskiEngine.SparseTensor`): Input sparse tensor to apply a
142147
convolution on.
143148
144-
:attr:`coords` ((`torch.IntTensor`, `MinkowskiEngine.CoordsKey`,
149+
:attr:`coords` ((`torch.IntTensor`, `MinkowskiEngine.CoordinateMapKey`,
145150
`MinkowskiEngine.SparseTensor`), optional): If provided, generate
146151
results on the provided coordinates. None by default.
147152
148153
"""
149154
assert isinstance(input, SparseTensor)
150155
assert input.D == self.dimension
156+
assert (
157+
self.in_channels == input.shape[1]
158+
), f"Channel size mismatch {self.in_channels} != {input.shape[1]}"
151159

152160
# Create a region_offset
153-
self.region_type_, self.region_offset_, _ = \
154-
self.kernel_generator.get_kernel(input.tensor_stride, False)
161+
region_type_, region_offset_, _ = self.kernel_generator.get_kernel(
162+
input.tensor_stride, False
163+
)
155164

156-
cm = input.coords_man
157-
in_key = input.coords_key
158-
on_gpu = input.device.type != 'cpu'
165+
cm = input.coordinate_manager
166+
in_key = input.coordinate_map_key
159167

160-
out_key = cm.stride(in_key, self.stride)
161-
N_out = cm.get_coords_size_by_coords_key(out_key)
168+
out_key = cm.stride(in_key, self.kernel_generator.kernel_stride)
169+
N_out = cm.size(out_key)
162170
out_F = input._F.new(N_out, self.in_channels).zero_()
163171

164-
in_maps, out_maps = cm.get_kernel_map(
172+
kernel_map = cm.get_kernel_map(
165173
in_key,
166174
out_key,
167-
self.stride,
168-
self.kernel_size,
169-
self.dilation,
170-
self.region_type_,
171-
self.region_offset_,
172-
is_transpose=False,
173-
is_pool=False,
174-
on_gpu=on_gpu)
175-
176-
for k in range(self.kernel_volume):
177-
out_F[out_maps[k]] += input.F[in_maps[k]] * self.kernel[k]
178-
179-
if self.has_bias:
175+
self.kernel_generator.kernel_stride,
176+
self.kernel_generator.kernel_size,
177+
self.kernel_generator.kernel_dilation,
178+
region_type=region_type_,
179+
region_offset=region_offset_,
180+
)
181+
182+
for k, in_out in kernel_map.items():
183+
in_out = in_out.long().to(input.device)
184+
out_F[in_out[1]] += input.F[in_out[0]] * self.kernel[k]
185+
186+
if self.bias is not None:
180187
out_F += self.bias
181188

182-
return SparseTensor(out_F, coords_key=out_key, coords_manager=cm)
189+
return SparseTensor(out_F, coordinate_map_key=out_key, coordinate_manager=cm)
183190

184191
def reset_parameters(self, is_transpose=False):
185-
n = (self.out_channels
186-
if is_transpose else self.in_channels) * self.kernel_volume
187-
stdv = 1. / math.sqrt(n)
188-
self.kernel.data.uniform_(-stdv, stdv)
189-
if self.bias is not None:
190-
self.bias.data.uniform_(-stdv, stdv)
192+
with torch.no_grad():
193+
n = (
194+
self.out_channels if is_transpose else self.in_channels
195+
) * self.kernel_generator.kernel_volume
196+
stdv = 1.0 / math.sqrt(n)
197+
self.kernel.data.uniform_(-stdv, stdv)
198+
if self.bias is not None:
199+
self.bias.data.uniform_(-stdv, stdv)
191200

192201
def __repr__(self):
193-
s = '(in={}, region_type={}, '.format(self.in_channels,
194-
self.kernel_generator.region_type)
195-
if self.kernel_generator.region_type in [
196-
RegionType.HYBRID, RegionType.CUSTOM
197-
]:
198-
s += 'kernel_volume={}, '.format(self.kernel_volume)
202+
s = "(in={}, region_type={}, ".format(
203+
self.in_channels, self.kernel_generator.region_type
204+
)
205+
if self.kernel_generator.region_type in [RegionType.CUSTOM]:
206+
s += "kernel_volume={}, ".format(self.kernel_generator.kernel_volume)
199207
else:
200-
s += 'kernel_size={}, '.format(self.kernel_size.tolist())
201-
s += 'stride={}, dilation={})'.format(self.stride.tolist(),
202-
self.dilation.tolist())
208+
s += "kernel_size={}, ".format(self.kernel_generator.kernel_size)
209+
s += "stride={}, dilation={})".format(
210+
self.kernel_generator.kernel_stride,
211+
self.kernel_generator.kernel_dilation,
212+
)
203213
return self.__class__.__name__ + s

MinkowskiEngine/MinkowskiConvolution.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
get_minkowski_function,
3737
)
3838
from MinkowskiCoordinateManager import CoordinateManager
39-
from MinkowskiKernelGenerator import KernelGenerator, save_ctx
39+
from MinkowskiKernelGenerator import KernelGenerator
4040

4141

4242
class MinkowskiConvolutionFunction(Function):
@@ -413,7 +413,7 @@ def __init__(
413413
convolution kernel. When a list is given, the length must be D and
414414
each element is an axis specific dilation. All elements must be > 0.
415415
416-
:attr:`has_bias` (bool, optional): if True, the convolution layer
416+
:attr:`bias` (bool, optional): if True, the convolution layer
417417
has a bias.
418418
419419
:attr:`kernel_generator` (:attr:`MinkowskiEngine.KernelGenerator`,
@@ -487,7 +487,7 @@ def __init__(
487487
convolution kernel. When a list is given, the length must be D and
488488
each element is an axis specific dilation. All elements must be > 0.
489489
490-
:attr:`has_bias` (bool, optional): if True, the convolution layer
490+
:attr:`bias` (bool, optional): if True, the convolution layer
491491
has a bias.
492492
493493
:attr:`kernel_generator` (:attr:`MinkowskiEngine.KernelGenerator`,
@@ -582,7 +582,7 @@ def __init__(
582582
convolution kernel. When a list is given, the length must be D and
583583
each element is an axis specific dilation. All elements must be > 0.
584584
585-
:attr:`has_bias` (bool, optional): if True, the convolution layer
585+
:attr:`bias` (bool, optional): if True, the convolution layer
586586
has a bias.
587587
588588
:attr:`kernel_generator` (:attr:`MinkowskiEngine.KernelGenerator`,

MinkowskiEngine/MinkowskiCoordinateManager.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,10 @@ def stride(
210210
stride = convert_to_int_list(stride, self.D)
211211
return self._manager.stride(coordinate_map_key, stride, string_id)
212212

213-
def origin(self):
213+
def origin(self) -> CoordinateMapKey:
214214
return self._manager.origin()
215215

216-
def size(self, coordinate_map_key: CoordinateMapKey):
216+
def size(self, coordinate_map_key: CoordinateMapKey) -> int:
217217
return self._manager.size(coordinate_map_key)
218218

219219
# def transposed_stride(
@@ -249,7 +249,7 @@ def size(self, coordinate_map_key: CoordinateMapKey):
249249
# )
250250
# return strided_key
251251

252-
def _get_coordinate_map_key(self, key_or_tensor_strides):
252+
def _get_coordinate_map_key(self, key_or_tensor_strides) -> CoordinateMapKey:
253253
r"""Helper function that retrieves the first coordinate map key for the given tensor stride."""
254254
assert isinstance(key_or_tensor_strides, CoordinateMapKey) or isinstance(
255255
key_or_tensor_strides, (Sequence, np.ndarray, torch.IntTensor, int)
@@ -263,18 +263,20 @@ def _get_coordinate_map_key(self, key_or_tensor_strides):
263263
assert len(keys) > 0
264264
return keys[0]
265265

266-
def get_coordinates(self, coords_key_or_tensor_strides):
266+
def get_coordinates(self, coords_key_or_tensor_strides) -> torch.Tensor:
267267
key = self._get_coordinate_map_key(coords_key_or_tensor_strides)
268268
return self._manager.get_coordinates(key)
269269

270-
def get_coordinate_field(self, coords_key_or_tensor_strides):
270+
def get_coordinate_field(self, coords_key_or_tensor_strides) -> torch.Tensor:
271271
key = self._get_coordinate_map_key(coords_key_or_tensor_strides)
272272
return self._manager.get_coordinate_field(key)
273273

274-
def number_of_unique_batch_indices(self):
274+
def number_of_unique_batch_indices(self) -> int:
275275
return self._manager.origin_map_size()
276276

277-
def get_unique_coordinate_map_key(self, tensor_stride: Union[int, list]):
277+
def get_unique_coordinate_map_key(
278+
self, tensor_stride: Union[int, list]
279+
) -> CoordinateMapKey:
278280
"""
279281
Returns a unique coordinate_map_key for a given tensor stride.
280282
@@ -292,7 +294,7 @@ def get_kernel_map(
292294
region_offset=None,
293295
is_transpose=False,
294296
is_pool=False,
295-
):
297+
) -> dict:
296298
r"""Get kernel in-out maps for the specified coords keys or tensor strides.
297299
298300
returns dict{kernel_index: in_out_tensor} where in_out_tensor[0] is the input row indices that correspond to in_out_tensor[1], which is the row indices for output.

0 commit comments

Comments
 (0)