From 0544aa20f34997813bff2ae4c7d441c6b9596ae9 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EB=B0=95=EC=84=B8=ED=9D=AC/On-Device=20Lab=28SR=29/Princip?= =?utf8?q?al=20Engineer/=EC=82=BC=EC=84=B1=EC=A0=84=EC=9E=90?= Date: Thu, 1 Aug 2019 12:34:50 +0900 Subject: [PATCH] [moco-tf] Import as TFMaxPool with a knob (#6043) * [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 * use plier and rename variable --- compiler/moco-tf/src/Knob.lst | 1 + compiler/moco-tf/src/Op/MaxPool.cpp | 98 +++++++++++++++++++++++++++++++- compiler/moco-tf/src/Op/MaxPool.h | 52 +++++++++++++++++ compiler/moco-tf/src/Op/MaxPool.test.cpp | 72 ++++++++++++++++++++++- 4 files changed, 218 insertions(+), 5 deletions(-) create mode 100644 compiler/moco-tf/src/Op/MaxPool.h diff --git a/compiler/moco-tf/src/Knob.lst b/compiler/moco-tf/src/Knob.lst index b241d52..cf68017 100644 --- a/compiler/moco-tf/src/Knob.lst +++ b/compiler/moco-tf/src/Knob.lst @@ -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) diff --git a/compiler/moco-tf/src/Op/MaxPool.cpp b/compiler/moco-tf/src/Op/MaxPool.cpp index 0407492..a2c4cb1 100644 --- a/compiler/moco-tf/src/Op/MaxPool.cpp +++ b/compiler/moco-tf/src/Op/MaxPool.cpp @@ -14,8 +14,14 @@ * 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()) + { + MaxPoolGraphBuilderImpl builder; + return builder.build(node, context); + } + else + { + MaxPoolGraphBuilderImpl builder; + return builder.build(node, context); + } +} + +void MaxPoolGraphBuilderImpl::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::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(); + 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(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 index 0000000..e95f19e --- /dev/null +++ b/compiler/moco-tf/src/Op/MaxPool.h @@ -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 class MaxPoolGraphBuilderImpl; + +template <> +struct MaxPoolGraphBuilderImpl final : public MaxPoolGraphBuilderBase +{ + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final; +}; + +template <> +struct MaxPoolGraphBuilderImpl final : public MaxPoolGraphBuilderBase +{ + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const final; +}; + +} // namespace tf +} // namespace moco + +#endif // __OP_MAX_POOL_H__ diff --git a/compiler/moco-tf/src/Op/MaxPool.test.cpp b/compiler/moco-tf/src/Op/MaxPool.test.cpp index 7f42496..fb29c2c 100644 --- a/compiler/moco-tf/src/Op/MaxPool.test.cpp +++ b/compiler/moco-tf/src/Op/MaxPool.test.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +#include "MaxPool.h" + +#include "IR/TFMaxPool.h" + #include "TestHelper.h" #include "Importer.h" @@ -27,6 +31,7 @@ #include +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 graph = importer.import(signature, graph_def); + + // Test "MaxPoolGraphBuilderImpl" + { + // 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; + + moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()}; + r.add("MaxPool", stdex::make_unique()); + moco::tf::Importer importer{&r}; + + std::unique_ptr graph = importer.import(signature, graph_def); + loco::MaxPool2D *maxpool2d_node = moco::tf::test::find_first_node_bytype(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" + { + // what to test: + // - there should exist TFMaxPool + // - attributes value should match + + using MaxPoolGraphBuilder = MaxPoolGraphBuilderImpl; + + moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()}; + r.add("MaxPool", stdex::make_unique()); + moco::tf::Importer importer{&r}; + + std::unique_ptr graph = importer.import(signature, graph_def); + + moco::tf::TFMaxPool *maxpool_node = + moco::tf::test::find_first_node_bytype(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({1, 2, 3, 1})); + ASSERT_EQ(maxpool_node->strides(), std::vector({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 graph = importer.import(signature, graph_def); + + // Test "MaxPoolGraphBuilderImpl" + { + // 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; + + moco::tf::GraphBuilderRegistry r{&moco::tf::GraphBuilderRegistry::get()}; + r.add("MaxPool", stdex::make_unique()); + moco::tf::Importer importer{&r}; + + std::unique_ptr graph = importer.import(signature, graph_def); + loco::MaxPool2D *maxpool2d_node = moco::tf::test::find_first_node_bytype(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" + // There is no FeatureEncode nor FeatureDecode to test } -- 2.7.4