@@ -62,6 +62,7 @@ __all__ = [ | |||
"softplus", | |||
"svd", | |||
"warp_perspective", | |||
"conv1d", | |||
] | |||
@@ -121,7 +122,7 @@ def conv2d( | |||
and the shape of weight should be `(groups, out_channel // groups, | |||
in_channels // groups, height, width)`. | |||
:type conv_mode: string or :class:`P.Convolution.Mode` | |||
:param conv_mode: supports "CROSS_CORRELATION" or "CONVOLUTION". Default: | |||
:param conv_mode: supports "CROSS_CORRELATION". Default: | |||
"CROSS_CORRELATION" | |||
:type compute_mode: string or | |||
:class:`P.Convolution.ComputeMode` | |||
@@ -187,7 +188,7 @@ def conv_transpose2d( | |||
and the shape of weight should be `(groups, out_channel // groups, | |||
in_channels // groups, height, width)`. Default: 1 | |||
:type conv_mode: string or :class:`P.Convolution.Mode` | |||
:param conv_mode: supports "CROSS_CORRELATION" or "CONVOLUTION". Default: | |||
:param conv_mode: supports "CROSS_CORRELATION". Default: | |||
"CROSS_CORRELATION" | |||
:type compute_mode: string or | |||
:class:`P.Convolution.ComputeMode` | |||
@@ -232,9 +233,7 @@ def local_conv2d( | |||
dilation: Union[int, Tuple[int, int]] = 1, | |||
conv_mode="CROSS_CORRELATION", | |||
): | |||
""" | |||
Applies spatial 2D convolution over an groupped channeled image with untied kernels. | |||
""" | |||
"""Applies spatial 2D convolution over an groupped channeled image with untied kernels.""" | |||
assert conv_mode == "CROSS_CORRELATION" or conv_mode.name == "CROSS_CORRELATION" | |||
stride_h, stride_w = expand_hw(stride) | |||
@@ -1585,6 +1584,82 @@ def indexing_one_hot( | |||
return result | |||
def conv1d( | |||
inp: Tensor, | |||
weight: Tensor, | |||
bias: Optional[Tensor] = None, | |||
stride: int = 1, | |||
padding: int = 0, | |||
dilation: int = 1, | |||
groups: int = 1, | |||
conv_mode="CROSS_CORRELATION", | |||
compute_mode="DEFAULT", | |||
) -> Tensor: | |||
"""1D convolution operation. | |||
Refer to :class:`~.Conv1d` for more information. | |||
:param inp: The feature map of the convolution operation | |||
:param weight: The convolution kernel | |||
:param bias: The bias added to the result of convolution (if given) | |||
:param stride: Stride of the 1D convolution operation. Default: 1 | |||
:param padding: Size of the paddings added to the input on both sides of its | |||
spatial dimensions. Only zero-padding is supported. Default: 0 | |||
:param dilation: Dilation of the 1D convolution operation. Default: 1 | |||
:param groups: number of groups to divide input and output channels into, | |||
so as to perform a "grouped convolution". When ``groups`` is not 1, | |||
``in_channels`` and ``out_channels`` must be divisible by ``groups``, | |||
and the shape of weight should be ``(groups, out_channel // groups, | |||
in_channels // groups, height, width)``. | |||
:type conv_mode: string or :class:`mgb.opr_param_defs.Convolution.Mode` | |||
:param conv_mode: Supports 'CROSS_CORRELATION'. Default: | |||
'CROSS_CORRELATION'. | |||
:type compute_mode: string or | |||
:class:`mgb.opr_param_defs.Convolution.ComputeMode` | |||
:param compute_mode: When set to 'DEFAULT', no special requirements will be | |||
placed on the precision of intermediate results. When set to 'FLOAT32', | |||
Float32 would be used for accumulator and intermediate result, but only | |||
effective when input and output are of Float16 dtype. | |||
""" | |||
assert conv_mode == "CROSS_CORRELATION" or conv_mode.name == "CROSS_CORRELATION" | |||
assert compute_mode == "DEFAULT" or compute_mode.name == "DEFAULT" | |||
assert inp.ndim == 3, "the input dimension of conv1d should be 3" | |||
assert weight.ndim == 3, "the weight dimension of conv1d should be 3" | |||
inp = expand_dims(inp, 3) | |||
weight = expand_dims(weight, 3) | |||
if bias is not None: | |||
assert bias.ndim == 3, "the bias dimension of conv1d should be 3" | |||
bias = expand_dims(bias, 3) | |||
stride_h = stride | |||
pad_h = padding | |||
dilate_h = dilation | |||
Sparse = P.Convolution.Sparse | |||
sparse_type = Sparse.DENSE if groups == 1 else Sparse.GROUP | |||
op = builtin.Convolution( | |||
stride_h=stride_h, | |||
stride_w=1, | |||
pad_h=pad_h, | |||
pad_w=0, | |||
dilate_h=dilate_h, | |||
dilate_w=1, | |||
strategy=get_conv_execution_strategy(), | |||
mode=conv_mode, | |||
compute_mode=compute_mode, | |||
sparse=sparse_type, | |||
) | |||
inp, weight = utils.convert_inputs(inp, weight) | |||
(output,) = apply(op, inp, weight) | |||
if bias is not None: | |||
output += bias | |||
output = squeeze(output, 3) | |||
return output | |||
def nms( | |||
boxes: Tensor, scores: Tensor, iou_thresh: float, max_output: Optional[int] = None | |||
) -> Tensor: | |||
@@ -11,7 +11,7 @@ from .activation import LeakyReLU, PReLU, ReLU, Sigmoid, Softmax | |||
from .adaptive_pooling import AdaptiveAvgPool2d, AdaptiveMaxPool2d | |||
from .batchnorm import BatchNorm1d, BatchNorm2d, SyncBatchNorm | |||
from .concat import Concat | |||
from .conv import Conv2d, ConvRelu2d, ConvTranspose2d, LocalConv2d | |||
from .conv import Conv1d, Conv2d, ConvRelu2d, ConvTranspose2d, LocalConv2d | |||
from .conv_bn import ConvBn2d, ConvBnRelu2d | |||
from .dropout import Dropout | |||
from .elemwise import Elemwise | |||
@@ -11,7 +11,7 @@ from typing import Tuple, Union | |||
import numpy as np | |||
from ..core.ops._internal import param_defs as P | |||
from ..functional import conv2d, conv_transpose2d, local_conv2d, relu | |||
from ..functional import conv1d, conv2d, conv_transpose2d, local_conv2d, relu | |||
from ..functional.types import _pair, _pair_nonzero | |||
from ..tensor import Parameter | |||
from . import init | |||
@@ -86,6 +86,152 @@ class _ConvNd(Module): | |||
return s.format(**self.__dict__) | |||
class Conv1d(_ConvNd): | |||
r""" | |||
Applies a 1D convolution over an input tensor. | |||
For instance, given an input of the size :math:`(N, C_{\text{in}}, H)`, | |||
this layer generates an output of the size | |||
:math:`(N, C_{\text{out}}, H_{\text{out}}})` through the | |||
process described as below: | |||
.. math:: | |||
\text{out}(N_i, C_{\text{out}_j}) = \text{bias}(C_{\text{out}_j}) + | |||
\sum_{k = 0}^{C_{\text{in}} - 1} \text{weight}(C_{\text{out}_j}, k) \star \text{input}(N_i, k) | |||
where :math:`\star` is the valid 1D cross-correlation operator, | |||
:math:`N` is batch size, :math:`C` denotes number of channels, and | |||
:math:`H` is length of 1D data element. | |||
When `groups == in_channels` and `out_channels == K * in_channels`, | |||
where K is a positive integer, this operation is also known as depthwise | |||
convolution. | |||
In other words, for an input of size :math:`(N, C_{in}, H_{in})`, | |||
a depthwise convolution with a depthwise multiplier `K`, can be constructed | |||
by arguments :math:`(in\_channels=C_{in}, out\_channels=C_{in} \times K, ..., groups=C_{in})`. | |||
:param in_channels: number of input channels. | |||
:param out_channels: number of output channels. | |||
:param kernel_size: size of weight on spatial dimensions. If kernel_size is | |||
an :class:`int`, the actual kernel size would be | |||
`(kernel_size, kernel_size)`. Default: 1 | |||
:param stride: stride of the 1D convolution operation. Default: 1 | |||
:param padding: size of the paddings added to the input on both sides of its | |||
spatial dimensions. Only zero-padding is supported. Default: 0 | |||
:param dilation: dilation of the 1D convolution operation. Default: 1 | |||
:param groups: number of groups into which the input and output channels are divided, so as to perform a "grouped convolution". When ``groups`` is not 1, | |||
``in_channels`` and ``out_channels`` must be divisible by ``groups``, | |||
and there would be an extra dimension at the beginning of the weight's | |||
shape. Specifically, the shape of weight would be `(groups, | |||
out_channel // groups, in_channels // groups, *kernel_size)`. | |||
:param bias: whether to add a bias onto the result of convolution. Default: | |||
True | |||
:param conv_mode: Supports `CROSS_CORRELATION`. Default: | |||
`CROSS_CORRELATION` | |||
:param compute_mode: When set to "DEFAULT", no special requirements will be | |||
placed on the precision of intermediate results. When set to "FLOAT32", | |||
"Float32" would be used for accumulator and intermediate result, but only | |||
effective when input and output are of float16 dtype. | |||
Examples: | |||
.. testcode:: | |||
import numpy as np | |||
import megengine as mge | |||
import megengine.module as M | |||
m = M.Conv1d(in_channels=3, out_channels=1, kernel_size=3) | |||
inp = mge.tensor(np.arange(0, 24).astype("float32").reshape(2, 3, 4)) | |||
oup = m(inp) | |||
print(oup.numpy().shape) | |||
Outputs: | |||
.. testoutput:: | |||
(2, 1, 2) | |||
""" | |||
_conv_mode_type = P.Convolution.Mode | |||
_compute_mode_type = P.Convolution.ComputeMode | |||
def __init__( | |||
self, | |||
in_channels: int, | |||
out_channels: int, | |||
kernel_size: int, | |||
stride: int = 1, | |||
padding: int = 0, | |||
dilation: int = 1, | |||
groups: int = 1, | |||
bias: bool = True, | |||
conv_mode: str = "CROSS_CORRELATION", | |||
compute_mode: str = "DEFAULT", | |||
): | |||
kernel_size = kernel_size | |||
stride = stride | |||
padding = padding | |||
dilation = dilation | |||
self.conv_mode = self._conv_mode_type.convert(conv_mode) | |||
self.compute_mode = self._compute_mode_type.convert(compute_mode) | |||
super().__init__( | |||
in_channels, | |||
out_channels, | |||
kernel_size, | |||
stride, | |||
padding, | |||
dilation, | |||
groups, | |||
bias, | |||
) | |||
def _get_fanin(self): | |||
kh = self.kernel_size | |||
ic = self.in_channels | |||
return kh * ic | |||
def _infer_weight_shape(self): | |||
group = self.groups | |||
ichl = self.in_channels | |||
ochl = self.out_channels | |||
kh = self.kernel_size | |||
if group == 1: | |||
# Assume format is NCH(W=1) | |||
return (ochl, ichl, kh) | |||
assert ( | |||
ichl % group == 0 and ochl % group == 0 | |||
), "invalid config: input_channels={} output_channels={} group={}".format( | |||
ichl, ochl, group | |||
) | |||
# Assume format is NCH(W=1) | |||
return (group, ochl // group, ichl // group, kh) | |||
def _infer_bias_shape(self): | |||
# Assume format is NCH(W=1) | |||
return (1, self.out_channels, 1) | |||
def calc_conv(self, inp, weight, bias): | |||
return conv1d( | |||
inp, | |||
weight, | |||
bias, | |||
self.stride, | |||
self.padding, | |||
self.dilation, | |||
self.groups, | |||
self.conv_mode, | |||
self.compute_mode, | |||
) | |||
def forward(self, inp): | |||
return self.calc_conv(inp, self.weight, self.bias) | |||
class Conv2d(_ConvNd): | |||
r""" | |||
Applies a 2D convolution over an input tensor. | |||
@@ -128,7 +274,7 @@ class Conv2d(_ConvNd): | |||
out_channel // groups, in_channels // groups, *kernel_size)`. | |||
:param bias: whether to add a bias onto the result of convolution. Default: | |||
True | |||
:param conv_mode: Supports `CROSS_CORRELATION` or `CONVOLUTION`. Default: | |||
:param conv_mode: Supports `CROSS_CORRELATION`. Default: | |||
`CROSS_CORRELATION` | |||
:param compute_mode: When set to "DEFAULT", no special requirements will be | |||
placed on the precision of intermediate results. When set to "FLOAT32", | |||
@@ -260,7 +406,7 @@ class ConvTranspose2d(_ConvNd): | |||
out_channels // groups, in_channels // groups, *kernel_size)``. Default: 1 | |||
:param bias: wether to add a bias onto the result of convolution. Default: | |||
True | |||
:param conv_mode: Supports `CROSS_CORRELATION` or `CONVOLUTION`. Default: | |||
:param conv_mode: Supports `CROSS_CORRELATION`. Default: | |||
`CROSS_CORRELATION` | |||
:param compute_mode: When set to "DEFAULT", no special requirements will be | |||
placed on the precision of intermediate results. When set to "FLOAT32", | |||
@@ -531,6 +531,18 @@ def test_zero_stride_numpy_array(): | |||
out = F.conv2d(inp, weight, None, (2, 2), (3, 3), (1, 1), 1) | |||
def test_conv1d(): | |||
inp = tensor(np.ones((16,), dtype=np.float32).reshape(2, 2, 4)) | |||
weight = tensor(np.ones((12,), dtype=np.float32).reshape(3, 2, 2)) | |||
out = F.conv1d(inp, weight, None, 2, 0, 1, 1) | |||
np.testing.assert_equal( | |||
out.numpy(), | |||
np.array( | |||
[[[4, 4], [4, 4], [4, 4]], [[4, 4], [4, 4], [4, 4]]], dtype=np.float32 | |||
), | |||
) | |||
def test_condtake(): | |||
x = np.array([[1, 2, 3], [4, 5, 6]]) | |||
y = np.array([[True, False, True], [False, True, True]]) | |||
@@ -20,6 +20,7 @@ from megengine import Parameter, Tensor, tensor | |||
from megengine.module import ( | |||
BatchNorm1d, | |||
BatchNorm2d, | |||
Conv1d, | |||
Conv2d, | |||
Dropout, | |||
Linear, | |||
@@ -541,6 +542,43 @@ def test_shared_param(): | |||
np.testing.assert_allclose(conv0(data).numpy(), conv1(data).numpy()) | |||
class Simple2(Module): | |||
def __init__(self): | |||
super().__init__() | |||
self.conv1 = Conv1d(1, 1, kernel_size=3, bias=False) | |||
self.conv0 = Conv1d(1, 1, kernel_size=3, bias=False) | |||
self.conv1.weight = self.conv0.weight | |||
def forward(self, inputs): | |||
pass | |||
def test_shared_param_1d(): | |||
net = Simple2() | |||
assert net.conv0.weight is net.conv1.weight | |||
data = tensor(np.random.random((1, 1, 8)).astype(np.float32)) | |||
np.testing.assert_allclose(net.conv0(data).numpy(), net.conv1(data).numpy()) | |||
with BytesIO() as f: | |||
mge.save(net, f) | |||
f.seek(0) | |||
net1 = mge.load(f) | |||
assert net1.conv0.weight is net1.conv1.weight | |||
np.testing.assert_allclose(net1.conv0(data).numpy(), net1.conv1(data).numpy()) | |||
with BytesIO() as f: | |||
mge.save(net.conv0, f) | |||
f.seek(0) | |||
conv0 = mge.load(f) | |||
with BytesIO() as f: | |||
mge.save(net.conv1, f) | |||
f.seek(0) | |||
conv1 = mge.load(f) | |||
assert conv0.weight is not conv1.weight | |||
np.testing.assert_allclose(conv0(data).numpy(), conv1(data).numpy()) | |||
def test_pickle_module(): | |||
data_shape = (2, 28) | |||
data = tensor(np.random.random(data_shape)) | |||