[moco-tf] Import as TFMaxPool with a knob (#6043)
author박세희/On-Device Lab(SR)/Principal Engineer/삼성전자 <saehie.park@samsung.com>
Thu, 1 Aug 2019 03:34:50 +0000 (12:34 +0900)
committerGitHub Enterprise <noreply-CODE@samsung.com>
Thu, 1 Aug 2019 03:34:50 +0000 (12:34 +0900)
* [moco-tf] Import as TFMaxPool with a knob

This will revise import to load MaxPool node as TFMaxPool with a knob

Signed-off-by: SaeHie Park <saehie.park@samsung.com>
* use plier and rename variable

compiler/moco-tf/src/Knob.lst
compiler/moco-tf/src/Op/MaxPool.cpp
compiler/moco-tf/src/Op/MaxPool.h [new file with mode: 0644]
compiler/moco-tf/src/Op/MaxPool.test.cpp

index b241d52..cf68017 100644 (file)
@@ -10,6 +10,7 @@ KNOB_BOOL(ImportAsTFBiasAdd, true, Import BiasAdd node as TFBiasAdd node)
 KNOB_BOOL(ImportAsTFConst, true, Import Const node as TFConst node)
 KNOB_BOOL(ImportAsTFConv2D, true, Import Conv2D node as TFConv2D node)
 KNOB_BOOL(ImportAsTFIdentity, false, Import Identity node as TFIdentity node)
+KNOB_BOOL(ImportAsTFMaxPool, false, Import MaxPool node as TFMaxPool node)
 
 // TensorFlow dialect transforms
 KNOB_BOOL(FuseBinaryIntoPreceding, true, Fuse Binary node to preceding node)
index 0407492..a2c4cb1 100644 (file)
  * limitations under the License.
  */
 
+#include "MaxPool.h"
+
+#include "Convert.h"
 #include "GraphBuilder.h"
 #include "GraphBuilderContext.h"
+#include "Knob.h"
+
+#include "IR/TFMaxPool.h"
 
 #include "Annotations/PaddingData.h"
 
@@ -38,10 +44,9 @@ namespace tf
 /**
  * @brief GraphBuilder for MaxPool node
  */
-class MaxPoolGraphBuilder final : public GraphBuilder
+class MaxPoolGraphBuilder final : public MaxPoolGraphBuilderBase
 {
 public:
-  bool validate(const tensorflow::NodeDef &) const override;
   void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
 };
 
@@ -60,7 +65,22 @@ private:
   const TensorName _input_name;
 };
 
-bool MaxPoolGraphBuilder::validate(const tensorflow::NodeDef &node) const
+class TFMaxPoolGraphUpdate final : public GraphUpdate
+{
+public:
+  TFMaxPoolGraphUpdate(moco::tf::TFMaxPool *node, const TensorName &name)
+      : _maxpool_node(node), _value_name(name)
+  {
+  }
+
+  void input(const SymbolTable *) const override;
+
+private:
+  moco::tf::TFMaxPool *_maxpool_node;
+  const TensorName _value_name;
+};
+
+bool MaxPoolGraphBuilderBase::validate(const tensorflow::NodeDef &node) const
 {
   // note: even though "data_format" is not entered when a model is written,
   //       TF seems to generate "data_format" field into a pb file
@@ -69,6 +89,23 @@ bool MaxPoolGraphBuilder::validate(const tensorflow::NodeDef &node) const
 
 void MaxPoolGraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const
 {
+  assert(context != nullptr);
+
+  if (moco::tf::get<moco::tf::Knob::ImportAsTFMaxPool>())
+  {
+    MaxPoolGraphBuilderImpl<ImportTarget::TensorFlow> builder;
+    return builder.build(node, context);
+  }
+  else
+  {
+    MaxPoolGraphBuilderImpl<ImportTarget::Canonical> builder;
+    return builder.build(node, context);
+  }
+}
+
+void MaxPoolGraphBuilderImpl<ImportTarget::Canonical>::build(const tensorflow::NodeDef &node,
+                                                             GraphBuilderContext *context) const
+{
   using plier::tf::DataLayout;
 
   assert(context != nullptr);
@@ -185,6 +222,61 @@ void MaxPoolGraphUpdate::input(const SymbolTable *tensor_names) const
   _encode_node->input(input_node);
 }
 
+void MaxPoolGraphBuilderImpl<ImportTarget::TensorFlow>::build(const tensorflow::NodeDef &node,
+                                                              GraphBuilderContext *context) const
+{
+  loco::Graph *graph = context->graph();
+  SymbolTable *tensor_names = context->tensor_names();
+  UpdateQueue *updates = context->updates();
+
+  // name of loco nodes
+  ::std::string node_name = node.name();
+
+  // tensorflow data_format: one of NHWC or NCHW.
+  auto data_layout = plier::tf::get_string_attr(node, "data_format");
+  auto maxPool_node = graph->nodes()->create<moco::tf::TFMaxPool>();
+  maxPool_node->data_layout(data_layout);
+
+  // padding
+  auto padding = moco::str_toupper(get_string_attr(node, "padding"));
+  maxPool_node->padding(padding);
+
+  // ksize
+  auto tf_ksize = plier::tf::get_list_attr(node, "ksize");
+  auto ksize = plier::tf::as_int64_list(tf_ksize);
+  if (ksize.size() != 4)
+  {
+    // TODO support ksize length for 1 and 2
+    throw std::runtime_error("MaxPool only supports ksize length 4");
+  }
+  maxPool_node->ksize(ksize);
+
+  // strides
+  auto tf_strides = plier::tf::get_list_attr(node, "strides");
+  auto strides = plier::tf::as_int64_list(tf_strides);
+  if (strides.size() != 4)
+  {
+    // TODO support strides length for 1 and 2
+    throw std::runtime_error("MaxPool only supports strides length 4");
+  }
+  maxPool_node->strides(strides);
+
+  // To set the input node of encode_node with node_name
+  TensorName output_name(node_name, 0);
+  tensor_names->enroll(output_name, maxPool_node);
+
+  // Record ifm inputs to featureEncode_node
+  auto update = stdex::make_unique<TFMaxPoolGraphUpdate>(maxPool_node, TensorName(node.input(0)));
+
+  updates->enroll(std::move(update));
+}
+
+void TFMaxPoolGraphUpdate::input(const SymbolTable *node_table) const
+{
+  loco::Node *value_node = node_table->node(_value_name);
+  _maxpool_node->value(value_node);
+}
+
 } // namespace tf
 } // namespace moco
 
diff --git a/compiler/moco-tf/src/Op/MaxPool.h b/compiler/moco-tf/src/Op/MaxPool.h
new file mode 100644 (file)
index 0000000..e95f19e
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifndef __OP_MAX_POOL_H__
+#define __OP_MAX_POOL_H__
+
+#include "GraphBuilder.h"
+#include "ImportTarget.h"
+
+namespace moco
+{
+namespace tf
+{
+
+struct MaxPoolGraphBuilderBase : public GraphBuilder
+{
+  virtual ~MaxPoolGraphBuilderBase() = default;
+
+  bool validate(const tensorflow::NodeDef &) const final;
+};
+
+template <ImportTarget T> class MaxPoolGraphBuilderImpl;
+
+template <>
+struct MaxPoolGraphBuilderImpl<ImportTarget::Canonical> final : public MaxPoolGraphBuilderBase
+{
+  void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
+};
+
+template <>
+struct MaxPoolGraphBuilderImpl<ImportTarget::TensorFlow> final : public MaxPoolGraphBuilderBase
+{
+  void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __OP_MAX_POOL_H__
index 7f42496..fb29c2c 100644 (file)
  * limitations under the License.
  */
 
+#include "MaxPool.h"
+
+#include "IR/TFMaxPool.h"
+
 #include "TestHelper.h"
 
 #include "Importer.h"
@@ -27,6 +31,7 @@
 
 #include <memory>
 
+using namespace moco::tf;
 using namespace moco::tf::test;
 
 namespace
@@ -125,7 +130,11 @@ TEST(TensorFlowImport, MaxPool_01)
 
   tensorflow::GraphDef graph_def;
   EXPECT_TRUE(plier::tf::parse_graphdef(maxpool_01_pbtxtdata, graph_def));
-  std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
+
+  // Test "MaxPoolGraphBuilderImpl<ImportTarget::Canonical>"
+  {
+    // TODO: fix indentation
+    // clang-format off
 
   // what to test:
   // - there should exist MaxPool2D
@@ -134,6 +143,14 @@ TEST(TensorFlowImport, MaxPool_01)
   // - stride values should match
   // - window values should match
 
+  using MaxPoolGraphBuilder = MaxPoolGraphBuilderImpl<ImportTarget::Canonical>;
+
+  moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
+  r.add("MaxPool", stdex::make_unique<MaxPoolGraphBuilder>());
+  moco::tf::Importer importer{&r};
+
+  std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
+
   loco::MaxPool2D *maxpool2d_node =
       moco::tf::test::find_first_node_bytype<loco::MaxPool2D>(graph.get());
   ASSERT_NE(maxpool2d_node, nullptr);
@@ -160,6 +177,39 @@ TEST(TensorFlowImport, MaxPool_01)
   // window
   ASSERT_EQ(maxpool2d->window()->vertical(), 2);
   ASSERT_EQ(maxpool2d->window()->horizontal(), 3);
+    // clang-format on
+  }
+
+  // Test "MaxPoolGraphBuilderImpl<ImportTarget::TensorFlow>"
+  {
+    // what to test:
+    // - there should exist TFMaxPool
+    // - attributes value should match
+
+    using MaxPoolGraphBuilder = MaxPoolGraphBuilderImpl<ImportTarget::TensorFlow>;
+
+    moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
+    r.add("MaxPool", stdex::make_unique<MaxPoolGraphBuilder>());
+    moco::tf::Importer importer{&r};
+
+    std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
+
+    moco::tf::TFMaxPool *maxpool_node =
+        moco::tf::test::find_first_node_bytype<moco::tf::TFMaxPool>(graph.get());
+    ASSERT_NE(maxpool_node, nullptr);
+
+    loco::Node *previous_node = maxpool_node->value();
+    auto following_nodes = loco::succs(maxpool_node);
+    ASSERT_EQ(following_nodes.size(), 1);
+    loco::Node *following_node = *following_nodes.begin();
+    ASSERT_NE(following_node, nullptr);
+
+    // attrs inside TFMaxPool
+    ASSERT_EQ(maxpool_node->data_layout(), "NHWC");
+    ASSERT_EQ(maxpool_node->padding(), "VALID");
+    ASSERT_EQ(maxpool_node->ksize(), std::vector<int64_t>({1, 2, 3, 1}));
+    ASSERT_EQ(maxpool_node->strides(), std::vector<int64_t>({1, 3, 2, 1}));
+  }
 }
 
 TEST(TensorFlowImport, MaxPool_02)
@@ -171,7 +221,11 @@ TEST(TensorFlowImport, MaxPool_02)
 
   tensorflow::GraphDef graph_def;
   EXPECT_TRUE(plier::tf::parse_graphdef(maxpool_01_pbtxtdata, graph_def));
-  std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
+
+  // Test "MaxPoolGraphBuilderImpl<ImportTarget::Canonical>"
+  {
+    // TODO: fix indentation
+    // clang-format off
 
   // what to test: Encoder and Decoder dimension order
   // - there should exist MaxPool2D
@@ -180,6 +234,14 @@ TEST(TensorFlowImport, MaxPool_02)
   // - FeatureEncode encoder should encode Count-Height-Width-Depth order
   // - FeatureDecode decoder should decode Count-Height-Width-Depth order
 
+  using MaxPoolGraphBuilder = MaxPoolGraphBuilderImpl<ImportTarget::Canonical>;
+
+  moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()};
+  r.add("MaxPool", stdex::make_unique<MaxPoolGraphBuilder>());
+  moco::tf::Importer importer{&r};
+
+  std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
+
   loco::MaxPool2D *maxpool2d_node =
       moco::tf::test::find_first_node_bytype<loco::MaxPool2D>(graph.get());
   ASSERT_NE(maxpool2d_node, nullptr);
@@ -232,4 +294,10 @@ TEST(TensorFlowImport, MaxPool_02)
   ASSERT_EQ(tensor_shape.dim(1).value(), 720);  // HEIGHT
   ASSERT_EQ(tensor_shape.dim(2).value(), 1280); // WIDTH
   ASSERT_EQ(tensor_shape.dim(3).value(), 3);    // DEPTH
+
+    // clang-format on
+  }
+
+  // Skip Test "AvgPoolGraphBuilderImpl<ImportTarget::TensorFlow>"
+  // There is no FeatureEncode nor FeatureDecode to test
 }