[exo/tflite] Export T/F Lite operation in exeuction order (#4222)
author박종현/On-Device Lab(SR)/Staff Engineer/삼성전자 <jh1302.park@samsung.com>
Fri, 12 Jul 2019 05:32:04 +0000 (14:32 +0900)
committerGitHub Enterprise <noreply-CODE@samsung.com>
Fri, 12 Jul 2019 05:32:04 +0000 (14:32 +0900)
* [exo/tflite] Export T/F Lite operation in exeuction order

The current implementations generates T/F Lite operators following node
generation order which may differ from actual execution order.

Signed-off-by: Jonghyun Park <jh1302.park@samsung.com>
* Apply patch

contrib/exo-tflite/src/OperationExporter.cpp
contrib/exo-tflite/src/OperationExporter.h
contrib/exo-tflite/src/TFLExporterImpl.cpp
contrib/exo-tflite/src/TFLExporterImpl.test.cpp [new file with mode: 0644]

index 4cbe51c..301e4f1 100644 (file)
@@ -339,11 +339,10 @@ void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder,
 
 } // namespace
 
-void exportNodes(loco::Graph::NodeContext *nodes, FlatBufferBuilder &builder,
-                 SerializedModelData &gd)
+void exportNodes(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &gd)
 {
-  for (uint32_t node_id = 0; node_id < nodes->size(); node_id++)
+  for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
   {
-    exportNode(nodes->at(node_id), builder, gd);
+    exportNode(node, builder, gd);
   }
 }
index ddadfa5..ac40445 100644 (file)
@@ -26,7 +26,6 @@
  * @param nodes container with nodes
  * @param gd information about serializer parts of model
  */
-void exportNodes(loco::Graph::NodeContext *nodes, flatbuffers::FlatBufferBuilder &builder,
-                 SerializedModelData &gd);
+void exportNodes(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &gd);
 
 #endif // __OPERATION_EXPORTER_H__
index eb780f5..63fd151 100644 (file)
@@ -105,7 +105,7 @@ void TFLExporter::Impl::exportGraph(loco::Graph *graph)
   registerGraphInputTensors(graph, gd);
   registerGraphOutputTensors(graph, gd);
 
-  exportNodes(graph->nodes(), _builder, gd);
+  exportNodes(graph, _builder, gd);
 
   // excode operator codes
   auto operator_codes = encodeOperatorCodes(_builder, gd._operator_codes);
diff --git a/contrib/exo-tflite/src/TFLExporterImpl.test.cpp b/contrib/exo-tflite/src/TFLExporterImpl.test.cpp
new file mode 100644 (file)
index 0000000..b1403ed
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+#include "TFLExporterImpl.h"
+
+#include "schema_generated.h"
+
+#include <loco/IR/PermutingCodec.h>
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+class TFLExporterImplTests : public ::testing::Test
+{
+public:
+  TFLExporterImplTests() { _graph = loco::make_graph(); }
+
+public:
+  virtual ~TFLExporterImplTests() = default;
+
+protected:
+  loco::Graph *graph(void) { return _graph.get(); }
+
+  template <typename NodeT> NodeT *make_node(void);
+
+private:
+  std::unique_ptr<loco::Graph> _graph;
+};
+
+template <typename NodeT> NodeT *TFLExporterImplTests::make_node(void)
+{
+  return graph()->nodes()->create<NodeT>();
+}
+
+template <> loco::FeatureEncode *TFLExporterImplTests::make_node(void)
+{
+  loco::FeatureEncode *encode_layer = graph()->nodes()->create<loco::FeatureEncode>();
+
+  auto encoder = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
+  (*encoder->perm())[loco::FeatureAxis::Count] = 0;
+  (*encoder->perm())[loco::FeatureAxis::Depth] = 1;
+  (*encoder->perm())[loco::FeatureAxis::Height] = 2;
+  (*encoder->perm())[loco::FeatureAxis::Width] = 3;
+  encode_layer->encoder(std::move(encoder));
+
+  return encode_layer;
+}
+
+template <> loco::FeatureDecode *TFLExporterImplTests::make_node(void)
+{
+  loco::FeatureDecode *decode_layer = graph()->nodes()->create<loco::FeatureDecode>();
+
+  auto decoder = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
+  (*decoder->perm())[loco::FeatureAxis::Count] = 0;
+  (*decoder->perm())[loco::FeatureAxis::Depth] = 1;
+  (*decoder->perm())[loco::FeatureAxis::Height] = 2;
+  (*decoder->perm())[loco::FeatureAxis::Width] = 3;
+  decode_layer->decoder(std::move(decoder));
+
+  return decode_layer;
+}
+
+} // namespace
+
+/**
+ * What happens when there is a mismatch between generation and execution order!?
+ */
+TEST_F(TFLExporterImplTests, Regression_0000)
+{
+  // Execution Order: MaxPool2D -> ReLU
+  // Generation Order: ReLU -> MaxPool2D
+  auto pull = make_node<loco::Pull>();
+  {
+    pull->dtype(loco::DataType::FLOAT32);
+    pull->shape({1, 8, 8, 3});
+  }
+  auto relu = make_node<loco::ReLU>();
+  auto encode = make_node<loco::FeatureEncode>();
+  auto maxpool = make_node<loco::MaxPool2D>();
+  auto decode = make_node<loco::FeatureDecode>();
+  auto push = make_node<loco::Push>();
+
+  ASSERT_EQ(maxpool->window()->vertical(), 1);
+  ASSERT_EQ(maxpool->window()->horizontal(), 1);
+
+  encode->input(pull);
+  maxpool->ifm(encode);
+  relu->input(maxpool);
+  decode->input(relu);
+  push->from(decode);
+
+  auto input = graph()->inputs()->create();
+  {
+    input->name("input");
+    input->node(pull);
+  }
+  auto output = graph()->outputs()->create();
+  {
+    output->name("output");
+    output->node(push);
+  }
+
+  exo::TFLExporter::Impl exporter{graph()};
+  {
+    int64_t maxpool_execution_index = -1;
+    int64_t relu_exeuction_index = -1;
+
+    auto model = tflite::GetModel(exporter.getBufferPointer());
+    auto operators = model->subgraphs()->Get(0)->operators();
+
+    for (uint32_t n = 0; n < operators->Length(); ++n)
+    {
+      auto opcode_index = operators->Get(n)->opcode_index();
+
+      switch (model->operator_codes()->Get(opcode_index)->builtin_code())
+      {
+      case tflite::BuiltinOperator_RELU:
+        ASSERT_EQ(relu_exeuction_index, -1);
+        relu_exeuction_index = static_cast<int64_t>(n);
+        break;
+      case tflite::BuiltinOperator_MAX_POOL_2D:
+        ASSERT_EQ(maxpool_execution_index, -1);
+        maxpool_execution_index = static_cast<int64_t>(n);
+        break;
+      default:
+        break;
+      }
+    }
+
+    ASSERT_NE(maxpool_execution_index, -1);
+    ASSERT_NE(relu_exeuction_index, -1);
+    // maxpool SHOULD precede ReLU
+    ASSERT_LT(maxpool_execution_index, relu_exeuction_index);
+  }
+}