This is an automated email from the ASF dual-hosted git repository.

zhasheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-mxnet.git


The following commit(s) were added to refs/heads/master by this push:
     new 0c0457a  Add a contrib operator for dynamic reshape (#15872)
0c0457a is described below

commit 0c0457a8f7171b24aa2b9a534c0d0e3b15207147
Author: Vandana Kannan <[email protected]>
AuthorDate: Tue Sep 8 00:06:55 2020 -0700

    Add a contrib operator for dynamic reshape (#15872)
    
    * Reshape which accepts shape as a tensor
    
    * Unit tests for dynamic reshape
    
    * Get output shape from shape tensor
    
    * Make changes to backward
    
    * fix lint errors
    
    * add newline
    
    * handle negative values for shape
    
    * edit args_grad shape
    
    * Using out in forward
    
    * Add an example
    
    * Add a comment in forward pass
    
    * address review comments
    
    * add some more doc and ndarray tests
    
    * Re-trigger CI
    
    * fix type params
    
    * Update test_contrib_operator.py
    
    * Update dynamic_shape_ops.cc
    
    * Update dynamic_shape_ops.cc
    
    * add req support
    
    Co-authored-by: Sheng Zha <[email protected]>
---
 src/operator/contrib/dynamic_shape_ops-inl.h   |  92 +++++++++++++++++
 src/operator/contrib/dynamic_shape_ops.cc      | 138 +++++++++++++++++++++++++
 tests/python/unittest/test_contrib_operator.py |  50 +++++++++
 3 files changed, 280 insertions(+)

diff --git a/src/operator/contrib/dynamic_shape_ops-inl.h 
b/src/operator/contrib/dynamic_shape_ops-inl.h
new file mode 100644
index 0000000..1d1aff8
--- /dev/null
+++ b/src/operator/contrib/dynamic_shape_ops-inl.h
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+/*!
+ * Copyright (c) 2018 by Contributors
+ * \file dynamic_shape_ops-inl.h
+*/
+
+#ifndef MXNET_OPERATOR_CONTRIB_DYNAMIC_SHAPE_OPS_INL_H_
+#define MXNET_OPERATOR_CONTRIB_DYNAMIC_SHAPE_OPS_INL_H_
+
+#include <vector>
+#include "../mxnet_op.h"
+#include "../mshadow_op.h"
+#include "../tensor/matrix_op-inl.h"
+
+namespace mxnet {
+namespace op {
+
+template<typename xpu>
+inline void DynamicReshapeForward(const nnvm::NodeAttrs& attrs,
+                            const OpContext &ctx,
+                            const std::vector<NDArray> &inputs,
+                            const std::vector<OpReqType> &req,
+                            const std::vector<NDArray> &outputs) {
+  CHECK_EQ(inputs.size(), 2U);
+  CHECK_EQ(outputs.size(), 1U);
+  const NDArray &out = outputs[0];
+  const NDArray &idx = inputs[1];
+  size_t idx_size = idx.shape()[0];
+  mxnet::TShape shape_value = mxnet::TShape(idx_size, 0);
+  std::vector<index_t> shapev(idx_size, 0);
+
+  // Copy the target shape that is provided in inputs[1]
+  // to the vector shapev
+  MSHADOW_TYPE_SWITCH(idx.dtype(), DType, {
+    DType* idx_dptr = idx.data().dptr<DType>();
+    for (size_t i = 0; i < idx_size; i++) {
+      shapev[i] = static_cast<index_t>(idx_dptr[i]);
+    }
+  });
+  shape_value = InferReshapeShape(mxnet::Tuple<index_t>(shapev), 
inputs[0].shape(), false);
+  const_cast<NDArray &>(out).Init(shape_value);
+  mshadow::Stream<xpu> *s = ctx.get_stream<xpu>();
+
+  MSHADOW_TYPE_SWITCH(out.dtype(), DType, {
+    MXNET_ASSIGN_REQ_SWITCH(req[0], Req, {
+      mxnet_op::Kernel<mxnet_op::op_with_req<mshadow_op::identity, Req>, 
xpu>::Launch(
+          s, inputs[0].data().Size(), out.data().dptr<DType>(),
+          inputs[0].data().dptr<DType>());
+    });
+  });
+}
+
+template<typename xpu>
+inline void DynamicReshapeBackward(const nnvm::NodeAttrs& attrs,
+                            const OpContext &ctx,
+                            const std::vector<NDArray> &inputs,
+                            const std::vector<OpReqType> &req,
+                            const std::vector<NDArray> &outputs) {
+  CHECK_EQ(inputs.size(), 1U);
+  CHECK_EQ(outputs.size(), 2U);
+  mshadow::Stream<xpu> *s = ctx.get_stream<xpu>();
+
+  MSHADOW_TYPE_SWITCH(outputs[0].dtype(), DType, {
+    MXNET_ASSIGN_REQ_SWITCH(req[0], Req, {
+      mxnet_op::Kernel<mxnet_op::op_with_req<mshadow_op::identity, Req>, 
xpu>::Launch(
+          s, inputs[0].data().Size(), outputs[0].data().dptr<DType>(),
+          inputs[0].data().dptr<DType>());
+    });
+  });
+}
+
+}  // namespace op
+}  // namespace mxnet
+
+#endif  // MXNET_OPERATOR_CONTRIB_DYNAMIC_SHAPE_OPS_INL_H_
diff --git a/src/operator/contrib/dynamic_shape_ops.cc 
b/src/operator/contrib/dynamic_shape_ops.cc
new file mode 100644
index 0000000..ff71d4b
--- /dev/null
+++ b/src/operator/contrib/dynamic_shape_ops.cc
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+/*!
+ * Copyright (c) 2018 by Contributors
+ * \file dynamic_shape_ops.cc
+*/
+
+#include "./dynamic_shape_ops-inl.h"
+#include "../tensor/elemwise_binary_op.h"
+#include "../elemwise_op_common.h"
+
+namespace mxnet {
+namespace op {
+
+inline bool DynamicReshapeType(const nnvm::NodeAttrs& attrs,
+                               std::vector<int> *in_attrs,
+                               std::vector<int> *out_attrs) {
+  CHECK_EQ(in_attrs->size(), 2U);
+  CHECK_EQ(out_attrs->size(), 1U);
+  TYPE_ASSIGN_CHECK(*out_attrs, 0, (*in_attrs)[0]);
+  TYPE_ASSIGN_CHECK(*in_attrs, 0, (*out_attrs)[0]);
+  return true;
+}
+
+bool DynamicReshapeStorageType(const nnvm::NodeAttrs& attrs,
+                               const int dev_mask,
+                               DispatchMode* dispatch_mode,
+                               std::vector<int> *in_attrs,
+                               std::vector<int> *out_attrs) {
+  CHECK_EQ(in_attrs->size(), 2);
+  CHECK_EQ(out_attrs->size(), 1);
+  for (size_t i = 0; i < in_attrs->size(); ++i) {
+    STORAGE_TYPE_ASSIGN_CHECK(*in_attrs, i, kDefaultStorage);
+  }
+  for (size_t i = 0; i < out_attrs->size(); ++i) {
+    STORAGE_TYPE_ASSIGN_CHECK(*out_attrs, i, kDefaultStorage);
+  }
+  DISPATCH_MODE_ASSIGN_CHECK(dispatch_mode, 0, DispatchMode::kFComputeEx);
+  return true;
+}
+
+bool DynamicReshapeBackwardStorageType(const nnvm::NodeAttrs& attrs,
+                                       const int dev_mask,
+                                       DispatchMode* dispatch_mode,
+                                       std::vector<int> *in_attrs,
+                                       std::vector<int> *out_attrs) {
+  CHECK_EQ(in_attrs->size(), 1);
+  CHECK_EQ(out_attrs->size(), 2);
+  for (size_t i = 0; i < in_attrs->size(); ++i) {
+    STORAGE_TYPE_ASSIGN_CHECK(*in_attrs, i, kDefaultStorage);
+  }
+  for (size_t i = 0; i < out_attrs->size(); ++i) {
+    STORAGE_TYPE_ASSIGN_CHECK(*out_attrs, i, kDefaultStorage);
+  }
+  DISPATCH_MODE_ASSIGN_CHECK(dispatch_mode, 0, DispatchMode::kFComputeEx);
+  return true;
+}
+
+NNVM_REGISTER_OP(_contrib_dynamic_reshape)
+.describe(R"code(
+Experimental support for reshape operator with dynamic shape.
+
+Accepts 2 inputs - data and shape.
+The output returns data in the new shape.
+
+Some dimensions of the shape can take special values from the set {0, -1, -2, 
-3, -4}. The significance of each is explained below:
+- ``0``  copy this dimension from the input to the output shape.
+  Example::
+  - input shape = (2,3,4), shape = (4,0,2), output shape = (4,3,2)
+  - input shape = (2,3,4), shape = (2,0,0), output shape = (2,3,4)
+- ``-1`` infers the dimension of the output shape by using the remainder of 
the input dimensions
+  keeping the size of the new array same as that of the input array.
+  At most one dimension of shape can be -1.
+  Example::
+  - input shape = (2,3,4), shape = (6,1,-1), output shape = (6,1,4)
+  - input shape = (2,3,4), shape = (3,-1,8), output shape = (3,1,8)
+  - input shape = (2,3,4), shape=(-1,), output shape = (24,)
+- ``-2`` copy all/remainder of the input dimensions to the output shape.
+  Example::
+  - input shape = (2,3,4), shape = (-2,), output shape = (2,3,4)
+  - input shape = (2,3,4), shape = (2,-2), output shape = (2,3,4)
+  - input shape = (2,3,4), shape = (-2,1,1), output shape = (2,3,4,1,1)
+- ``-3`` use the product of two consecutive dimensions of the input shape as 
the output dimension.
+  Example::
+  - input shape = (2,3,4), shape = (-3,4), output shape = (6,4)
+  - input shape = (2,3,4,5), shape = (-3,-3), output shape = (6,20)
+  - input shape = (2,3,4), shape = (0,-3), output shape = (2,12)
+  - input shape = (2,3,4), shape = (-3,-2), output shape = (6,4)
+- ``-4`` split one dimension of the input into two dimensions passed 
subsequent to -4 in shape (can contain -1).
+  Example::
+  - input shape = (2,3,4), shape = (-4,1,2,-2), output shape =(1,2,3,4)
+  - input shape = (2,3,4), shape = (2,-4,-1,3,-2), output shape = (2,1,3,4)
+
+Example::
+   data = mx.nd.array(np.random.normal(0,1,(2,3,5,5)))
+   shape = mx.nd.array((0,-1))
+   out = mx.sym.contrib.dynamic_reshape(data = data, shape = shape)
+   // out will be of shape (2,75)
+)code" ADD_FILELINE)
+.set_num_inputs(2)
+.set_num_outputs(1)
+.set_attr<nnvm::FListInputNames>("FListInputNames",
+  [](const NodeAttrs& attrs) {
+    return std::vector<std::string>{"data", "shape"};
+  })
+.set_attr<nnvm::FInferType>("FInferType", DynamicReshapeType)
+.set_attr<FInferStorageType>("FInferStorageType", DynamicReshapeStorageType)
+.set_attr<FComputeEx>("FComputeEx<cpu>", DynamicReshapeForward<cpu>)
+.set_attr<nnvm::FGradient>("FGradient", 
ElemwiseGradUseNone{"_backward_contrib_dynamic_reshape"})
+.add_argument("data", "NDArray-or-Symbol", "Data")
+.add_argument("shape", "NDArray-or-Symbol", "Shape");
+
+
+NNVM_REGISTER_OP(_backward_contrib_dynamic_reshape)
+.set_num_inputs(1)
+.set_num_outputs(2)
+.set_attr<nnvm::TIsBackward>("TIsBackward", true)
+.set_attr<FInferStorageType>("FInferStorageType", 
DynamicReshapeBackwardStorageType)
+.set_attr<FComputeEx>("FComputeEx<cpu>", DynamicReshapeBackward<cpu>);
+
+}  // namespace op
+}  // namespace mxnet
diff --git a/tests/python/unittest/test_contrib_operator.py 
b/tests/python/unittest/test_contrib_operator.py
index 23cfdf8..e3f59de 100644
--- a/tests/python/unittest/test_contrib_operator.py
+++ b/tests/python/unittest/test_contrib_operator.py
@@ -410,3 +410,53 @@ def test_op_mrcnn_mask_target():
 
     assert_almost_equal(mask_targets.asnumpy(), gt_mask_targets.asnumpy())
     assert_almost_equal(mask_cls.asnumpy(), gt_mask_cls.asnumpy())
+
+@with_seed()
+def test_dynamic_reshape():
+    def dynamic_reshape_testcases(src_shape, shape_arg, dst_shape):
+        data = mx.sym.Variable('data')
+        shape = mx.sym.Variable('shape')
+        net = mx.sym.contrib.dynamic_reshape(data, shape)
+        js = net.tojson()
+        net = mx.sym.load_json(js)
+        dat_npy = np.random.rand(*src_shape)
+        grad_npy = np.random.rand(*dst_shape)
+        args = {
+            'data': mx.nd.array(dat_npy),
+            'shape': mx.nd.array(shape_arg)
+            }
+        args_grad = {
+            'data': mx.nd.empty(src_shape)
+        }
+        exe = net._bind(default_context(), args, args_grad)
+        exe.forward(is_train=True)
+        assert np.square(exe.outputs[0].asnumpy() - 
dat_npy.reshape(dst_shape)).mean() < 1E-7
+        exe.backward(out_grads=mx.nd.array(grad_npy))
+        assert np.square(exe.grad_dict['data'].asnumpy() - 
grad_npy.reshape(src_shape)).mean() < 1E-7
+
+        # test ndarray
+        X = mx.nd.random.uniform(shape=src_shape)
+        Y = mx.contrib.nd.dynamic_reshape(X, mx.nd.array(shape_arg))
+        assert_array_equal(Y.shape, dst_shape)
+
+    test_cases = [
+        [(2, 3, 5, 5),  (0, -1),           (2, 75)],
+        [(2, 3, 5, 5),  (0, 0, -1),        (2, 3, 25)],
+        [(5, 3, 4, 5),  (0, -1, 0),        (5, 15, 4)],
+        [(2, 3, 5, 4),  (-1, 0, 0),        (8, 3, 5)],
+        [(2, 3, 5, 5),  (0, 0, 0, 0),      (2, 3, 5, 5)],
+        [(2, 4, 5, 3),  (-1, 2, 2, 1),     (30, 2, 2, 1)],
+        [(2, 3, 5, 6),  (-2,),             (2, 3, 5, 6)],
+        [(2, 3, 5, 6),  (6, 1, -2),        (6, 1, 5, 6)],
+        [(2, 3, 5, 6),  (-3, -3),          (6, 30)],
+        [(2, 3, 5, 6),  (-3, -1),          (6, 30)],
+        [(64,),         (-4, 16, 4),       (16, 4)],
+        [(64,),         (-4, 16, -1),      (16, 4)],
+        [(64, 1, 2, 3), (-4, 16, -1, -2),  (16, 4, 1, 2, 3)]]
+
+    for test_case in test_cases:
+        dynamic_reshape_testcases(*test_case)
+
+if __name__ == '__main__':
+    import nose
+    nose.runmodule()

Reply via email to