You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mxnet.apache.org by zh...@apache.org on 2019/02/14 21:20:17 UTC

[incubator-mxnet] branch master updated: [MXNET-1315] Add checks for dynamic-shaped operators in CachedOp (#14018)

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

zhengda 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 df5310b  [MXNET-1315] Add checks for dynamic-shaped operators in CachedOp (#14018)
df5310b is described below

commit df5310b059417fb6d855f99cf7bb0cf7c0093551
Author: Junru Shao <ju...@gmail.com>
AuthorDate: Thu Feb 14 13:19:53 2019 -0800

    [MXNET-1315] Add checks for dynamic-shaped operators in CachedOp (#14018)
    
    * Initial commit
    
    * Try this
    
    * Boy next door!
    
    * Add comments per discussion with Da
    
    * Try this
    
    * boy try this
    
    * change the boss of the gym
---
 src/imperative/cached_op.cc          | 10 +++++++++-
 src/imperative/cached_op.h           |  4 ++++
 src/imperative/imperative_utils.h    | 15 +++++++++++----
 src/operator/contrib/boolean_mask.cc |  2 ++
 src/operator/subgraph_op_common.cc   |  8 +++++++-
 src/operator/subgraph_op_common.h    |  3 ++-
 6 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/src/imperative/cached_op.cc b/src/imperative/cached_op.cc
index 58ec4e6..8dd0a4d 100644
--- a/src/imperative/cached_op.cc
+++ b/src/imperative/cached_op.cc
@@ -285,12 +285,20 @@ bool CachedOp::SetForwardGraph(
   }
 
   bool match = true;
-  match &= CheckAndInferShape(&g, std::move(shape_inputs), true);
+  bool contain_dynamic_shape = false;
+  match &= CheckAndInferShape(&g, std::move(shape_inputs), true,
+                              {0, 0}, {0, 0}, &contain_dynamic_shape);
   match &= CheckAndInferType(&g, std::move(dtype_inputs), true);
   exec::DevMaskVector dev_mask(g.indexed_graph().num_nodes(), inputs[0]->ctx().dev_mask());
   match &= CheckAndInferStorageType(&g, std::move(dev_mask),
                                     std::move(storage_type_inputs), true);
 
+  // When dynmaic shape exists, it is not feasible to plan memory ahead of time
+  if (contain_dynamic_shape) {
+    g.attrs.erase("forward_mem_plan");
+    g.attrs.erase("full_mem_plan");
+    return false;
+  }
   if (!match) {
     g.attrs.erase("forward_mem_plan");
     g.attrs.erase("full_mem_plan");
diff --git a/src/imperative/cached_op.h b/src/imperative/cached_op.h
index 59a793e..3b173c8 100644
--- a/src/imperative/cached_op.h
+++ b/src/imperative/cached_op.h
@@ -35,6 +35,7 @@ struct CachedOpConfig : public dmlc::Parameter<CachedOpConfig> {
   uint32_t backward_bulk_size;
   bool static_alloc;
   bool static_shape;
+  bool is_dynamic;
   nnvm::Tuple<uint32_t> data_indices;
   nnvm::Tuple<uint32_t> param_indices;
   std::string subgraph;
@@ -66,6 +67,9 @@ struct CachedOpConfig : public dmlc::Parameter<CachedOpConfig> {
     DMLC_DECLARE_FIELD(subgraph)
     .set_default(std::string(""))
     .describe("JSON string of a subgraph.");
+    DMLC_DECLARE_FIELD(is_dynamic)
+    .set_default(false)
+    .describe("Whether the graph contains dynamic shape operators.");
   }
 };
 
diff --git a/src/imperative/imperative_utils.h b/src/imperative/imperative_utils.h
index 6446c37..7113cb2 100644
--- a/src/imperative/imperative_utils.h
+++ b/src/imperative/imperative_utils.h
@@ -179,7 +179,7 @@ inline void SetShapeType(const Context& ctx,
 
   for (size_t i = 0; i < outputs.size(); ++i) {
     NDArrayStorageType storage_type = static_cast<NDArrayStorageType>(out_storage_types[i]);
-    if (outputs[i]->is_none()) {
+    if (outputs[i]->is_none() || outputs[i]->shape().ndim() == 0) {
       if (is_dynamic_shape_existing) {
         // once there is dynamic shape somewhere, we could not pre-determine the shape.
         *outputs[i] = NDArray(ctx, out_types[i]);
@@ -566,8 +566,12 @@ inline void PushOperator(const OpStatePtr& state,
 inline bool CheckAndInferShape(nnvm::Graph* p_g, nnvm::ShapeVector&& shapes,
                                bool use_inputs,
                                std::pair<uint32_t, uint32_t> node_range = {0, 0},
-                               std::pair<uint32_t, uint32_t> entry_range = {0, 0}) {
+                               std::pair<uint32_t, uint32_t> entry_range = {0, 0},
+                               bool *contain_unknown = nullptr) {
   using namespace nnvm;
+  if (contain_unknown != nullptr) {
+    *contain_unknown = false;
+  }
   nnvm::Graph& g = *p_g;
   if (use_inputs) {
     if (g.attrs.count("shape_inputs") &&
@@ -601,8 +605,11 @@ inline bool CheckAndInferShape(nnvm::Graph* p_g, nnvm::ShapeVector&& shapes,
     g.attrs["shape"] = std::make_shared<dmlc::any>(std::move(shapes));
     g = exec::InferShape(std::move(g));
   }
-  CHECK_EQ(g.GetAttr<size_t>("shape_num_unknown_nodes"), 0U);
-
+  if (contain_unknown == nullptr) {
+    CHECK_EQ(g.GetAttr<size_t>("shape_num_unknown_nodes"), 0U);
+  } else {
+    *contain_unknown = g.GetAttr<size_t>("shape_num_unknown_nodes") != 0U;
+  }
   return false;
 }
 
diff --git a/src/operator/contrib/boolean_mask.cc b/src/operator/contrib/boolean_mask.cc
index 2dcafb6..7fd66bc 100644
--- a/src/operator/contrib/boolean_mask.cc
+++ b/src/operator/contrib/boolean_mask.cc
@@ -98,6 +98,8 @@ which stands for the rows in x where the corresonding element in index is non-ze
 .set_attr<FComputeEx>("FComputeEx<cpu>", BooleanMaskForward<cpu>)
 .set_attr<FInferStorageType>("FInferStorageType", BooleanMaskStorageType)
 .set_attr<nnvm::FGradient>("FGradient", ElemwiseGradUseIn{"_backward_contrib_boolean_mask"})
+.set_attr<nnvm::FListInputNames>("FListInputNames", [](const NodeAttrs& attrs) {
+  return std::vector<std::string>{"data", "index"};})
 .add_argument("data", "NDArray-or-Symbol", "Data")
 .add_argument("index", "NDArray-or-Symbol", "Mask")
 .add_arguments(BooleanMaskParam::__FIELDS__());
diff --git a/src/operator/subgraph_op_common.cc b/src/operator/subgraph_op_common.cc
index 7a99aed..4b8f63a 100644
--- a/src/operator/subgraph_op_common.cc
+++ b/src/operator/subgraph_op_common.cc
@@ -222,8 +222,14 @@ void LoopState::Forward(int iter_no,
   // If an input and an output share the array, the output array will be changed
   // by CachedOp. We need to copy data to the real output.
   for (size_t i = 0; i < out_bufs.size(); i++)
-    if (!out_bufs[i].IsSame(coutputs[i]))
+    if (!out_bufs[i].IsSame(coutputs[i])) {
+      // The line below checks whether dynamic shape exists.
+      // If so, re-initialize the shape.
+      if (coutputs[i].shape().ndim() == 0) {
+        const_cast<NDArray &>(coutputs[i]).Init(out_bufs[i].shape());
+      }
       CopyFromTo(out_bufs[i], coutputs[i]);
+    }
   if (is_recording) {
     all_inputs.push_back(cinputs);
     all_outputs.push_back(coutputs);
diff --git a/src/operator/subgraph_op_common.h b/src/operator/subgraph_op_common.h
index ebf727f..c316fca 100644
--- a/src/operator/subgraph_op_common.h
+++ b/src/operator/subgraph_op_common.h
@@ -161,7 +161,8 @@ class LoopState {
     // only static_alloc supports nested call of CachedOp.
     std::vector<std::pair<std::string, std::string> > kwargs = {
       {"inline_limit", "0"},
-      {"static_alloc", "1"}
+      {"static_alloc", "1"},
+      {"is_dynamic", "1"}
     };
     return std::make_shared<CachedOp>(sym, kwargs);
   }