diff --git a/docs/api/api_python/mindspore.nn.rst b/docs/api/api_python/mindspore.nn.rst index bae5ebb77d8..4a7f20ea10b 100644 --- a/docs/api/api_python/mindspore.nn.rst +++ b/docs/api/api_python/mindspore.nn.rst @@ -170,6 +170,7 @@ Dropout层 :nosignatures: :template: classtemplate.rst + mindspore.nn.AdaptiveAvgPool1d mindspore.nn.AvgPool1d mindspore.nn.AvgPool2d mindspore.nn.MaxPool1d diff --git a/docs/api/api_python/nn/mindspore.nn.AdaptiveAvgPool1d.rst b/docs/api/api_python/nn/mindspore.nn.AdaptiveAvgPool1d.rst new file mode 100644 index 00000000000..b6b55cea732 --- /dev/null +++ b/docs/api/api_python/nn/mindspore.nn.AdaptiveAvgPool1d.rst @@ -0,0 +1,34 @@ +mindspore.nn.AdaptiveAvgPool1d +======================= + +.. py:class:: mindspore.nn.AdaptiveAvgPool1d(output_size) + + 对输入的多维数据进行一维平面上的自适应平均池化运算。 + + 通常,输入的shape为 :math:`(N_{in}, C_{in}, L_{in})` ,AdaptiveAvgPool1d在 :math:`(L_{in})` 维度上输出区域平均值。 + 输出的shape为 :math:`(N_{in}, C_{in}, L_{out})` ,其中, :math:`L_{out}` 为 `output_size`。 + + .. note:: + :math:`L_{in}` 必须能被 `output_size` 整除。 + + **参数:** + + - **output_size** (int) - 目标输出大小 :math:`L_{out}`。 + + **输入:** + + - **x** (Tensor) - shape为 :math:`(N, C_{in}, L_{in})` 的Tensor,数据类型为float16,float32或float64。 + + **输出:** + + Tensor,其shape为 :math:`(N, C_{in}, L_{out})`,数据类型与 `x` 相同。 + + **异常:** + + - **TypeError** - `output_size` 不是int。 + - **TypeError** - `x` 不是float16或float32或float64。 + - **ValueError** - `output_size` 小于1。 + - **ValueError** - `x` 的shape长度不等于3。 + - **ValueError** - `x` 的最后一个维度小于 `output_size`。 + - **ValueError** - `x` 的最后一个维度不能被 `output_size` 整除。 + diff --git a/docs/api/api_python_en/mindspore.nn.rst b/docs/api/api_python_en/mindspore.nn.rst index f57f093673b..da631f0ca8a 100644 --- a/docs/api/api_python_en/mindspore.nn.rst +++ b/docs/api/api_python_en/mindspore.nn.rst @@ -170,6 +170,7 @@ Pooling Layer :nosignatures: :template: classtemplate.rst + mindspore.nn.AdaptiveAvgPool1d mindspore.nn.AvgPool1d mindspore.nn.AvgPool2d mindspore.nn.MaxPool1d diff --git a/mindspore/python/mindspore/nn/layer/pooling.py b/mindspore/python/mindspore/nn/layer/pooling.py index 5cefe743e73..8b175e4e282 100644 --- a/mindspore/python/mindspore/nn/layer/pooling.py +++ b/mindspore/python/mindspore/nn/layer/pooling.py @@ -1,4 +1,4 @@ -# Copyright 2020-2021 Huawei Technologies Co., Ltd +# Copyright 2020-2022 Huawei Technologies Co., Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,9 +18,10 @@ from mindspore.ops import functional as F from mindspore._checkparam import Rel, Validator as validator from mindspore.ops.primitive import constexpr import mindspore.context as context +from mindspore.common import dtype as mstype from ..cell import Cell -__all__ = ['AvgPool2d', 'MaxPool2d', 'AvgPool1d', 'MaxPool1d'] +__all__ = ['AvgPool2d', 'MaxPool2d', 'AvgPool1d', 'MaxPool1d', 'AdaptiveAvgPool1d'] class _PoolNd(Cell): @@ -399,3 +400,100 @@ class AvgPool1d(_PoolNd): x = self.avg_pool(x) x = self.squeeze(x) return x + + +@constexpr +def _adaptive_shape_check(in_shape, output_size, prim_name): + """Check shape.""" + msg_prefix = "For {}, the".format(prim_name) + if len(in_shape) != 3: + raise ValueError("{} input must has 3 dim, but got {}.".format(msg_prefix, len(in_shape))) + if in_shape[2] < output_size: + raise ValueError("{} input's last dimension must be greater or equal to " + "output size {}, but got {}.".format(msg_prefix, output_size, in_shape[2])) + if in_shape[2] % output_size != 0: + raise ValueError("{} input's last dimension must be divisible by " + "output size {}, but got {}.".format(msg_prefix, output_size, in_shape[2])) + + +@constexpr +def _adaptive_dtype_check(x_dtype, prim_name): + """Check dtype.""" + if x_dtype not in [mstype.float16, mstype.float32, mstype.float64]: + raise TypeError("For {}, the x_dtype must be float16, float32 or float64, " + "but got {}.".format(prim_name, x_dtype)) + + +class AdaptiveAvgPool1d(Cell): + r""" + 1D adaptive average pooling for temporal data. + + Applies a 1D adaptive average pooling over an input Tensor which can be regarded as + a composition of 1D input planes. + + Typically, the input is of shape :math:`(N_{in}, C_{in}, L_{in})`, + AdaptiveAvgPool1d outputs regional average in the :math:`(L_{in})`-dimension. + The output is of shape :math:`(N_{in}, C_{in}, L_{out})`, + where :math:`L_{out})` is defined by `output_size` + + Note: + :math:`(L_{in})` must be divisible by `output_size`. + + Args: + output_size (int): the target output size :math:`L_{out}`. + + Inputs: + - **x** (Tensor) - Tensor of shape :math:`(N, C_{in}, L_{in})`, with float16, float32 or float64 data type. + + Outputs: + Tensor of shape :math:`(N, C_{in}, L_{out})`, has the same type as `x`. + + Raises: + TypeError: If `output_size` is not an int. + TypeError: If `x` is neither float16 nor float32 nor float64. + ValueError: If `output_size` is less than 1. + ValueError: If length of shape of `x` is not equal to 3. + ValueError: If the last dimension of `x` is smaller than `output_size`. + ValueError: If the last dimension of `x` is not divisible by `output_size`. + + + Supported Platforms: + ``Ascend`` ``GPU`` ``CPU`` + + Examples: + >>> pool = nn.AdaptiveAvgPool1d(output_size=2) + >>> x = Tensor(np.random.randint(0, 10, [1, 3, 6]), mindspore.float32) + >>> output = pool(x) + >>> result = output.shape + >>> print(result) + (1, 3, 2) + """ + + def __init__(self, output_size): + """Initialize AdaptiveAvgPool1d.""" + super(AdaptiveAvgPool1d, self).__init__() + validator.check_value_type('output_size', output_size, [int], self.cls_name) + validator.check_int(output_size, 1, Rel.GE, "output_size", self.cls_name) + self.shape = F.shape + self.expand = P.ExpandDims() + self.squeeze = P.Squeeze(2) + self.output_size = output_size + self.dtype = P.DType() + + def construct(self, x): + _adaptive_shape_check(self.shape(x), self.output_size, self.cls_name) + _adaptive_dtype_check(self.dtype(x), self.cls_name) + + _, _, width = self.shape(x) + stride = width // self.output_size + kernel_size = width - (self.output_size - 1) * stride + + stride = (1, width // self.output_size) + kernel_size = (1, kernel_size) + + x = self.expand(x, 2) + avg_pool = P.AvgPool(kernel_size=kernel_size, strides=stride) + x = avg_pool(x) + x = self.squeeze(x) + + return x diff --git a/tests/ut/python/nn/test_pooling.py b/tests/ut/python/nn/test_pooling.py index 61acf1aef9e..f4a676d3875 100644 --- a/tests/ut/python/nn/test_pooling.py +++ b/tests/ut/python/nn/test_pooling.py @@ -1,4 +1,4 @@ -# Copyright 2020 Huawei Technologies Co., Ltd +# Copyright 2020-2022 Huawei Technologies Co., Ltd # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -76,3 +76,25 @@ def test_avg1d(): net = Avg1dNet(6, 1) input_ = Tensor(np.random.randint(0, 255, [1, 3, 6]).astype(np.float32)) _cell_graph_executor.compile(net, input_) + + +class AdaptiveAvgPool1dNet(nn.Cell): + """AdaptiveAvgPool1d.""" + + def __init__(self, output_size): + super(AdaptiveAvgPool1dNet, self).__init__() + self.adaptive_avg_pool_1d = nn.AdaptiveAvgPool1d(output_size) + + def construct(self, x): + return self.adaptive_avg_pool_1d(x) + + +def test_adaptive_avg_pool_1d(): + """ + Feature: Test AdaptiveAvgPool1d. + Description: Test AdaptiveAvgPool1d functional. + Expectation: Success. + """ + net = AdaptiveAvgPool1dNet(2) + input_ = Tensor(np.random.randint(0, 255, [1, 3, 6]).astype(np.float32)) + _cell_graph_executor.compile(net, input_)