diff --git a/mindspore/ops/_grad/__init__.py b/mindspore/ops/_grad/__init__.py index c2c837b89c..de9e3ae8d0 100644 --- a/mindspore/ops/_grad/__init__.py +++ b/mindspore/ops/_grad/__init__.py @@ -15,7 +15,7 @@ """grad impl.""" from . import grad_array_ops, grad_comm_ops, grad_debug_ops, grad_implementations, \ - grad_math_ops, grad_nn_ops, grad_other_ops, grad_quant_ops + grad_inner_ops, grad_math_ops, grad_nn_ops, grad_other_ops, grad_quant_ops from .grad_base import get_bprop_fn __all__ = ['get_bprop_fn'] diff --git a/mindspore/ops/_grad/grad_inner_ops.py b/mindspore/ops/_grad/grad_inner_ops.py new file mode 100644 index 0000000000..e577b1b0ab --- /dev/null +++ b/mindspore/ops/_grad/grad_inner_ops.py @@ -0,0 +1,37 @@ +# Copyright 2020 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +"""array_ops""" + +from ..operations import _grad_ops as G +from ..operations import _inner_ops as inner +from ..composite.multitype_ops.zeros_like_impl import zeros_like +from .grad_base import bprop_getters + + +@bprop_getters.register(inner.StridedSliceAICPU) +def get_bprop_strided_slice_aicpu(self): + """Generate bprop for StridedSlice""" + input_grad = G.StridedSliceGradAICPU(self.begin_mask, + self.end_mask, + self.ellipsis_mask, + self.new_axis_mask, + self.shrink_axis_mask) + + def bprop(x, begin, end, strides, out, dout): + dx = input_grad(dout, shape_op(x), begin, end, strides) + return dx, zeros_like(begin), zeros_like(end), zeros_like(strides) + + return bprop diff --git a/mindspore/ops/_op_impl/aicpu/__init__.py b/mindspore/ops/_op_impl/aicpu/__init__.py index adff8bebfc..bbf1013e0a 100644 --- a/mindspore/ops/_op_impl/aicpu/__init__.py +++ b/mindspore/ops/_op_impl/aicpu/__init__.py @@ -40,3 +40,5 @@ from .poisson import _poisson_aicpu from .uniform_int import _uniform_int_aicpu from .uniform_real import _uniform_real_aicpu from .laplace import _laplace_aicpu +from .strided_slice import _strided_slice_aicpu +from .strided_slice_grad import _strided_slice_grad_aicpu diff --git a/mindspore/ops/_op_impl/aicpu/strided_slice.py b/mindspore/ops/_op_impl/aicpu/strided_slice.py new file mode 100644 index 0000000000..b62a86f3f3 --- /dev/null +++ b/mindspore/ops/_op_impl/aicpu/strided_slice.py @@ -0,0 +1,41 @@ +# Copyright 2020 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +"""StridedSlice op""" +from mindspore.ops.op_info_register import op_info_register, AiCPURegOp, DataType + +strided_slice_op_info = AiCPURegOp("StridedSliceAICPU") \ + .fusion_type("OPAQUE") \ + .input(0, "input", "required") \ + .input(1, "begin", "required") \ + .input(2, "end", "required") \ + .input(3, "stride", "required") \ + .output(0, "output", "required") \ + .attr("begin_mask", "int") \ + .attr("end_mask", "int") \ + .attr("ellipsis_mask", "int") \ + .attr("new_axis_mask", "int") \ + .attr("shrink_axis_mask", "int") \ + .dtype_format(DataType.F32_NCHW, + DataType.I32_NCHW, + DataType.I32_NCHW, + DataType.I32_NCHW, + DataType.F32_NCHW) \ + .get_op_info() + +@op_info_register(strided_slice_op_info) +def _strided_slice_aicpu(): + """StridedSlice AiCPU register""" + return diff --git a/mindspore/ops/_op_impl/aicpu/strided_slice_grad.py b/mindspore/ops/_op_impl/aicpu/strided_slice_grad.py new file mode 100644 index 0000000000..f1ce9319c4 --- /dev/null +++ b/mindspore/ops/_op_impl/aicpu/strided_slice_grad.py @@ -0,0 +1,43 @@ +# Copyright 2020 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ + +"""StridedSliceGrad op""" +from mindspore.ops.op_info_register import op_info_register, AiCPURegOp, DataType + +strided_slice_grad_op_info = AiCPURegOp("StridedSliceGradAICPU") \ + .fusion_type("OPAQUE") \ + .input(0, "dy", "required") \ + .input(1, "shape", "required") \ + .input(2, "begin", "required") \ + .input(3, "end", "required") \ + .input(4, "stride", "required") \ + .output(0, "output", "required") \ + .attr("begin_mask", "int") \ + .attr("end_mask", "int") \ + .attr("ellipsis_mask", "int") \ + .attr("new_axis_mask", "int") \ + .attr("shrink_axis_mask", "int") \ + .dtype_format(DataType.F32_NCHW, + DataType.I32_NCHW, + DataType.I32_NCHW, + DataType.I32_NCHW, + DataType.I32_NCHW, + DataType.F32_NCHW) \ + .get_op_info() + +@op_info_register(strided_slice_grad_op_info) +def _strided_slice_grad_aicpu(): + """StridedSliceGrad AiCPU register""" + return diff --git a/mindspore/ops/operations/_grad_ops.py b/mindspore/ops/operations/_grad_ops.py index 590f277942..247e6c186e 100644 --- a/mindspore/ops/operations/_grad_ops.py +++ b/mindspore/ops/operations/_grad_ops.py @@ -1110,6 +1110,54 @@ class StridedSliceGrad(PrimitiveWithInfer): 'value': None} +class StridedSliceGradAICPU(PrimitiveWithInfer): + """ + Performs grad of StridedSlice operation. + + Args: + begin_mask (int): Start indexing the slice. Default: 0. + end_mask (int): End indexing the slice. Default: 0. + ellipsis_mask (int): An int32 mask. Default: 0. + new_axis_mask (int): An int32 mask. Default: 0. + shrink_axis_mask (int): An int32 mask. Default: 0. + + Returns: + Tensor, has the same shape of input. + """ + + @prim_attr_register + def __init__(self, + begin_mask=0, + end_mask=0, + ellipsis_mask=0, + new_axis_mask=0, + shrink_axis_mask=0): + """init StrideSliceGrad""" + validator.check_value_type('begin_mask', begin_mask, [int], self.name) + validator.check_value_type('end_mask', end_mask, [int], self.name) + validator.check_value_type('ellipsis_mask', ellipsis_mask, [int], self.name) + validator.check_value_type('new_axis_mask', new_axis_mask, [int], self.name) + validator.check_value_type('shrink_axis_mask', shrink_axis_mask, [int], self.name) + self.init_prim_io_names(inputs=['dy', 'shapex', 'begin', 'end', 'strides'], outputs=['output']) + + def __infer__(self, dy, shapex, begin, end, strides): + args = {"dy": dy['dtype']} + validator.check_tensor_type_same(args, mstype.number_type, self.name) + + for idx, item in enumerate(shapex['value']): + validator.check_value_type("shapex[%d]" % idx, item, [int], self.name) + for idx, item in enumerate(begin['value']): + validator.check_value_type("begin[%d]" % idx, item, [int], self.name) + for idx, item in enumerate(end['value']): + validator.check_value_type("end[%d]" % idx, item, [int], self.name) + for idx, item in enumerate(strides['value']): + validator.check_value_type("strides[%d]" % idx, item, [int], self.name) + + return {'shape': shapex['value'], + 'dtype': dy['dtype'], + 'value': None} + + class SoftplusGrad(PrimitiveWithInfer): """Computes gradient for the Log Softmax activation.""" diff --git a/mindspore/ops/operations/_inner_ops.py b/mindspore/ops/operations/_inner_ops.py index 49834fc168..2ace10a6b4 100644 --- a/mindspore/ops/operations/_inner_ops.py +++ b/mindspore/ops/operations/_inner_ops.py @@ -21,6 +21,137 @@ from ...common import dtype as mstype from ..primitive import PrimitiveWithInfer, prim_attr_register +class StridedSliceAICPU(PrimitiveWithInfer): + r""" + + Extracts a strided slice of a tensor. + + Given an input tensor, this operation inserts a dimension of length 1 at the dimension. + This operation extracts a fragment of size (end-begin)/stride from the given + 'input_tensor'. Starting from the position specified by the begin, the fragment + continues adding stride to the index until all dimensions are not less than end. + + Note: + The stride may be negative value, which causes reverse slicing. + The shape of `begin`, `end` and `strides` should be the same. + + Args: + begin_mask (int): Starting index of the slice. Default: 0. + end_mask (int): Ending index of the slice. Default: 0. + ellipsis_mask (int): An int mask. Default: 0. + new_axis_mask (int): An int mask. Default: 0. + shrink_axis_mask (int): An int mask. Default: 0. + Currently all the masks are not in used. Use default 0 only. + + Inputs: + - **input_x** (Tensor) - The input Tensor. + - **begin** (tuple[int]) - A tuple which represents the location where to start. Only + constant value is allowed. + - **end** (tuple[int]) - A tuple or which represents the maximum location where to stop. + Only constant value is allowed. + - **strides** (tuple[int]) - A tuple which represents the stride continuously added + before reach the maximum location. Only constant value is allowed. + + Outputs: + Tensor. + Explain with the following example. + - In the 0th dim, begin is 1, end is 2, and strides is 1, + because :math:`1+1=2\geq2`, the interval is :math:`[1,2)`. + Thus, return the element with :math:`index = 1` in 0th dim, i.e., [[3, 3, 3], [4, 4, 4]]. + - In the 1st dim, similarly, the interval is :math:`[0,1)`. + Based on the return value of the 0th dim, return the element with :math:`index = 0`, + i.e., [3, 3, 3]. + - In the 2nd dim, similarly, the interval is :math:`[0,3)`. + Based on the return value of the 1st dim, return the element with :math:`index = 0,1,2`, + i.e., [3, 3, 3]. + - Finally, the output is [3, 3, 3]. + + Examples + >>> input_x = Tensor([[[1, 1, 1], [2, 2, 2]], [[3, 3, 3], [4, 4, 4]], + >>> [[5, 5, 5], [6, 6, 6]]], mindspore.float32) + >>> slice = P.StridedSliceAICPU() + >>> output = slice(input_x, (1, 0, 0), (2, 1, 3), (1, 1, 2)) + >>> output.shape + (1, 1, 2) + >>> output + [[[3, 3]]] + """ + + @prim_attr_register + def __init__(self, + begin_mask=0, + end_mask=0, + ellipsis_mask=0, + new_axis_mask=0, + shrink_axis_mask=0): + """init StrideSlice""" + self.init_prim_io_names(inputs=['x', 'begin', 'end', 'strides'], outputs=['output']) + validator.check_value_type('begin_mask', begin_mask, [int], self.name) + validator.check_value_type('end_mask', end_mask, [int], self.name) + validator.check_value_type('ellipsis_mask', ellipsis_mask, [int], self.name) + validator.check_value_type('new_axis_mask', new_axis_mask, [int], self.name) + validator.check_value_type('shrink_axis_mask', shrink_axis_mask, [int], self.name) + + def __infer__(self, x, begin, end, strides): + begin_v, end_v, strides_v = begin['value'], end['value'], strides['value'] + validator.check_value_type("begin", begin_v, [tuple], self.name) + validator.check_value_type("end", end_v, [tuple], self.name) + validator.check_value_type("strides", strides_v, [tuple], self.name) + + x_shape = x['shape'] + x_shp_len = len(x_shape) + if len(begin_v) != x_shp_len or len(end_v) != x_shp_len or len(strides_v) != x_shp_len: + raise ValueError(f"For \'{self.name}\' the length of begin index{begin_v}, end index{end_v} and " + f"strides{strides_v} must be equal to the dims({x_shp_len}) of input.") + + ret_shape = [] + append_dimensions = [] + shrink_pos = bin(self.shrink_axis_mask)[::-1] + new_pos = bin(self.new_axis_mask)[::-1] + for i in range(x_shp_len): + # After the integer is converted to binary, it is a str and the first two chars are the flag char '0b' + if i < (len(new_pos) - 2) and new_pos[i] == '1': + ret_shape.append(1) + append_dimensions.append(x_shape[x_shp_len - 1 - len(append_dimensions)]) + continue + if i < (len(shrink_pos) - 2) and shrink_pos[i] == '1': + validator.check_integer(f'begin[{i}]', begin_v[i], -x_shape[i], Rel.GE, self.name) + validator.check_integer(f'begin[{i}]', begin_v[i], x_shape[i], Rel.LT, self.name) + continue + + begin_idx = begin_v[i] + end_idx = end_v[i] + strides_idx = strides_v[i] + if self.begin_mask: + begin_idx = 0 + if self.end_mask: + end_idx = x_shape[i] + validator.check_integer(f'begin[{i}]', begin_idx, x_shape[i], Rel.LE, self.name) + validator.check_integer(f'end[{i}]', end_idx, x_shape[i], Rel.LE, self.name) + validator.check_integer(f'strides[{i}]', strides_idx, 0, Rel.NE, self.name) + if strides_idx > 0: + # If sliced forward , end_idx >= begin_idx + validator.check(f'begin[{i}]', begin_idx, f'end[{i}]', end_idx, Rel.LE) + if begin_idx < 0 < end_idx: + # Turn negative begin_idx into positive values + begin_idx = x_shape[i] + begin_idx + num_elems = (end_idx - begin_idx + strides_idx - 1) // strides_idx + else: + # If sliced backwards, end_idx <= begin_idx + validator.check(f'begin[{i}]', begin_idx, f'end[{i}]', end_idx, Rel.GE) + if end_idx < 0 < begin_idx: + # Turn negative end_idx into positive values + end_idx = x_shape[i] + end_idx + num_elems = (end_idx - begin_idx + strides_idx + 1) // strides_idx + + ret_shape.append(num_elems) + if append_dimensions: + ret_shape += append_dimensions[::-1] + return {'shape': ret_shape, + 'dtype': x['dtype'], + 'value': None} + + class ExtractImagePatches(PrimitiveWithInfer): """ Extract patches from images. diff --git a/tests/st/ops/ascend/test_aicpu_ops/test_strided_slice.py b/tests/st/ops/ascend/test_aicpu_ops/test_strided_slice.py new file mode 100644 index 0000000000..422572857e --- /dev/null +++ b/tests/st/ops/ascend/test_aicpu_ops/test_strided_slice.py @@ -0,0 +1,51 @@ +# Copyright 2020 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +import numpy as np + +import mindspore.context as context +import mindspore.nn as nn +from mindspore import Tensor +from mindspore.ops.operations import _inner_ops as inner + +context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") + + +class Net(nn.Cell): + def __init__(self, begin, end, strides): + super(Net, self).__init__() + self.strided_slice = inner.StridedSliceAICPU() + self.begin = begin + self.end = end + self.strides = strides + + def construct(self, input): + return self.strided_slice(input, self.begin, self.end, self.strides) + + +input_x = np.array([[[0, 1, 2], [3, 4, 5]], + [[6, 7, 8], [9, 10, 11]], + [[12, 13, 14], [15, 16, 17]] + ]).astype(np.float32) +begin = (1, 0, 0) +end = (2, 2, 3) +strides = (1, 1, 2) + + +def test_net(): + net = Net(begin, end, strides) + tinput = Tensor(input_x) + output = net(tinput) + print(output.asnumpy()) + assert np.all([[[6, 8], [9, 11]]] == output.asnumpy()) \ No newline at end of file diff --git a/tests/st/ops/ascend/test_aicpu_ops/test_strided_slice_grad.py b/tests/st/ops/ascend/test_aicpu_ops/test_strided_slice_grad.py new file mode 100644 index 0000000000..116ceb7451 --- /dev/null +++ b/tests/st/ops/ascend/test_aicpu_ops/test_strided_slice_grad.py @@ -0,0 +1,53 @@ +# Copyright 2020 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. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================ +import numpy as np + +import mindspore.context as context +import mindspore.nn as nn +from mindspore import Tensor +from mindspore.ops.operations import _grad_ops as G + +context.set_context(mode=context.GRAPH_MODE, device_target="Ascend") + + +class Net(nn.Cell): + def __init__(self, shape_x, begin, end, strides): + super(Net, self).__init__() + self.strided_slice_grad = G.StridedSliceGradAICPU() + self.shape_x = shape_x + self.begin = begin + self.end = end + self.strides = strides + + def construct(self, dy): + return self.strided_slice_grad(dy, self.shape_x, self.begin, self.end, self.strides) + + +dy = np.array([[[6, 8], [9, 11]]]).astype(np.float32) +shape_x = (3, 2, 3) +begin = (1, 0, 0) +end = (2, 2, 3) +strides = (1, 1, 2) + + +def test_net(): + net = Net(shape_x, begin, end, strides) + tdy = Tensor(dy) + output = net(tdy) + print(output.asnumpy()) + assert np.all([[[0, 0, 0], [0, 0, 0]], + [[6, 0, 8], [9, 0, 11]], + [[0, 0, 0], [0, 0, 0]] + ] == output.asnumpy()) \ No newline at end of file