From 0c9798e16361ea37c67a54cc3f8deb953c7eb4e6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?=EC=9C=A4=ED=98=84=EC=8B=9D/On-Device=20Lab=28SR=29/Princip?= =?utf8?q?al=20Engineer/=EC=82=BC=EC=84=B1=EC=A0=84=EC=9E=90?= Date: Fri, 2 Aug 2019 17:21:03 +0900 Subject: [PATCH] [moco-tf] GraphBuilder for custom op (#6139) * [moco-tf] GraphBuilder for custom op This adds GraphBuilder, transforms, and a test for custom op. Signed-off-by: Hyun Sik Yoon * enhancing per comments --- compiler/moco-tf/src/Op/COpCall.cpp | 129 +++++++++++++++++++ compiler/moco-tf/src/Op/COpCall.h | 49 ++++++++ compiler/moco-tf/src/Op/COpCall.test.cpp | 139 +++++++++++++++++++++ .../moco-tf/src/Transforms/FixPaddingTransform.cpp | 14 +++ .../moco-tf/src/Transforms/FixShapeTransform.cpp | 22 ++++ 5 files changed, 353 insertions(+) create mode 100644 compiler/moco-tf/src/Op/COpCall.cpp create mode 100644 compiler/moco-tf/src/Op/COpCall.h create mode 100644 compiler/moco-tf/src/Op/COpCall.test.cpp diff --git a/compiler/moco-tf/src/Op/COpCall.cpp b/compiler/moco-tf/src/Op/COpCall.cpp new file mode 100644 index 0000000..2bd3fcd --- /dev/null +++ b/compiler/moco-tf/src/Op/COpCall.cpp @@ -0,0 +1,129 @@ +/* + * 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 "COpCall.h" + +#include "Convert.h" +#include "GraphBuilder.h" +#include "GraphBuilderContext.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace +{ + +class COpCallGraphUpdate final : public moco::tf::GraphUpdate +{ +public: + COpCallGraphUpdate(locoex::COpCall *node, const std::vector &input_names) + : _node(node), _input_names(input_names) + { + } + + void input(const moco::tf::SymbolTable *) const override; + +private: + locoex::COpCall *_node; + const std::vector _input_names; +}; + +void COpCallGraphUpdate::input(const moco::tf::SymbolTable *tensor_names) const +{ + for (int n = 0; n < _input_names.size(); n++) + { + loco::Node *target = tensor_names->node(_input_names.at(n)); + _node->input(n, target); + } +} + +} // namespace + +namespace moco +{ +namespace tf +{ + +bool COpCallGraphBuilder::validate(const tensorflow::NodeDef &tf_node) const { return true; } + +void COpCallGraphBuilder::build(const tensorflow::NodeDef &tf_node, + GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // Create a "COpCall" node for CustomOp and set attributes + auto call_node = graph->nodes()->create(tf_node.input_size()); + { + call_node->op(tf_node.op()); + call_node->name(tf_node.name()); + call_node->dtype(_signature->dtype(tf_node.name())); + + auto shape = _signature->shape(tf_node.name()); + call_node->rank(shape->rank()); + for (int d = 0; d < shape->rank(); d++) + call_node->dim(d) = shape->dim(d); + + for (auto iter = tf_node.attr().begin(); iter != tf_node.attr().end(); iter++) + { + auto name = iter->first; + auto val = iter->second; + + if (val.value_case() == tensorflow::AttrValue::kF) + { + call_node->attr(name, stdex::make_unique(val.f())); + } + else if (val.value_case() == tensorflow::AttrValue::kI) + { + call_node->attr(name, stdex::make_unique(val.i())); + } + // TODO define more types + else + { + throw std::runtime_error("not supported attribute type"); + } + } + } + + // register this node with its name + TensorName output_name(tf_node.name(), 0); + tensor_names->enroll(output_name, call_node); + + // Queue node input update + std::vector input_names; + for (int i = 0; i < tf_node.input_size(); ++i) + { + input_names.emplace_back(TensorName(tf_node.input(i))); + } + auto update = stdex::make_unique(call_node, input_names); + updates->enroll(std::move(update)); +} + +} // namespace tf +} // namespace moco diff --git a/compiler/moco-tf/src/Op/COpCall.h b/compiler/moco-tf/src/Op/COpCall.h new file mode 100644 index 0000000..ea81d3a --- /dev/null +++ b/compiler/moco-tf/src/Op/COpCall.h @@ -0,0 +1,49 @@ +/* + * 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_COP_CALL_H__ +#define __OP_COP_CALL_H__ + +#include "GraphBuilder.h" +#include "GraphBuilderContext.h" + +#include + +#include + +namespace moco +{ +namespace tf +{ + +/** + * @brief GraphBuilder for COpCall node + */ +class COpCallGraphBuilder final : public GraphBuilder +{ +public: + COpCallGraphBuilder(const ModelSignature *signature) : _signature(signature) { /* empty */} + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; + +private: + const ModelSignature *_signature; +}; + +} // namespace tf +} // namespace moco + +#endif // __OP_COP_CALL_H__ diff --git a/compiler/moco-tf/src/Op/COpCall.test.cpp b/compiler/moco-tf/src/Op/COpCall.test.cpp new file mode 100644 index 0000000..89723b6 --- /dev/null +++ b/compiler/moco-tf/src/Op/COpCall.test.cpp @@ -0,0 +1,139 @@ +/* + * 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 "COpCall.h" + +#include "TestHelper.h" + +#include "Importer.h" +#include "Canonicalizer.h" + +#include +#include + +#include +#include + +#include + +using namespace moco::tf::test; + +namespace +{ +// clang-format off +const char *customop_01_pbtxtdata = STRING_CONTENT( +node { + name: "input1" + op: "Placeholder" + attr { + key: "dtype" value { type: DT_FLOAT } } + attr { + key: "shape" + value { shape { dim { size: 1 } dim { size: 2 } } } + } +} +node { + name: "input2" + op: "Const" + attr { key: "dtype" value { type: DT_FLOAT } } + attr { + key: "value" + value { + tensor { + dtype: DT_FLOAT + tensor_shape { dim { size: 1 } dim { size: 2 } } + float_val: 1.1 float_val: 2.2 + } + } + } +} +node { + name: "my/customOp/000" + op: "new_custom_op" + input: "input1" + input: "input2" + attr { key: "my_float" value { f: 0.001 } } + attr { key: "my_int" value { i: 111 } } +} +node { + name: "output/relu" + op: "Relu" + input: "my/customOp/000" + attr { key: "T" value { type: DT_FLOAT } } +} +); + +// clang-format on +} // namespace + +TEST(Call_Test, Call_01) +{ + moco::tf::ModelSignature signature; + { + signature.add_input(moco::tf::TensorName("input1", 0)); + signature.add_output(moco::tf::TensorName("output/relu", 0)); + signature.add_customop("new_custom_op"); + signature.dtype("my/customOp/000", loco::DataType::FLOAT32); + signature.shape("my/customOp/000", {1, 2}); + } + + tensorflow::GraphDef graph_def; + EXPECT_TRUE(plier::tf::parse_graphdef(customop_01_pbtxtdata, graph_def)); + + // import + moco::tf::GraphBuilderRegistry registry{&moco::tf::GraphBuilderRegistry::get()}; + registry.add("new_custom_op", stdex::make_unique(&signature)); + + moco::tf::Importer importer(®istry); + std::unique_ptr graph = importer.import(signature, graph_def); + + // Convert graph to hold only Canonical dialect + moco::tf::Canonicalizer canonicalizer; + canonicalizer.canonicalize(graph.get()); + + // Cannonicalized graph may look like the following: + // + // loco node : Pull -----customOp - Relu - Push + // | + // ConstGen--+ + // + // 1. Checking other nodes linked to/from custom op + auto *customop = moco::tf::test::find_first_node_bytype(graph.get()); + + // check inputs and next node + ASSERT_EQ(customop->arity(), 2); + + loco::Node *input_0 = customop->arg(0); + loco::Node *input_1 = customop->arg(1); + auto next_nodes = loco::succs(customop); + + ASSERT_EQ(next_nodes.size(), 1); + loco::Node *next_node = *next_nodes.begin(); + ASSERT_NE(next_node, nullptr); + + ASSERT_TRUE(dynamic_cast(input_0) and dynamic_cast(input_1)); + ASSERT_TRUE(dynamic_cast(next_node)); + + // test 2. + // attrs inside COpCall + auto f_attr = customop->attr("my_float"); + ASSERT_FLOAT_EQ(f_attr->val(), 0.001); + ASSERT_TRUE(f_attr->type() == locoex::COpAttrType::Float); + + auto i_attr = customop->attr("my_int"); + ASSERT_FLOAT_EQ(i_attr->val(), 111); + ASSERT_TRUE(i_attr->type() == locoex::COpAttrType::Int); +} diff --git a/compiler/moco-tf/src/Transforms/FixPaddingTransform.cpp b/compiler/moco-tf/src/Transforms/FixPaddingTransform.cpp index 5bcf784..0ac212d 100644 --- a/compiler/moco-tf/src/Transforms/FixPaddingTransform.cpp +++ b/compiler/moco-tf/src/Transforms/FixPaddingTransform.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -765,6 +766,12 @@ bool fix_padding(moco::tf::TFSqueeze *node) return false; } +bool fix_padding(locoex::COpCall *node) +{ + // Nothing to do with padding + return false; +} + } // namespace namespace moco @@ -798,6 +805,13 @@ bool FixPaddingTransform::run(loco::Graph *graph) #include "Dialect/TFNodes.lst" #undef TENSORFLOW_NODE // clang-format on + + if (as(node)) + { + if (fix_padding(as(node))) + changed = true; + } + else { throw std::runtime_error("Not supported loco::Node type in FixPaddingTransform"); } diff --git a/compiler/moco-tf/src/Transforms/FixShapeTransform.cpp b/compiler/moco-tf/src/Transforms/FixShapeTransform.cpp index 9fa72ae..8649981 100644 --- a/compiler/moco-tf/src/Transforms/FixShapeTransform.cpp +++ b/compiler/moco-tf/src/Transforms/FixShapeTransform.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -1240,6 +1241,20 @@ bool fix_shape(moco::tf::TFSqueeze *node) return true; } +bool fix_shape(locoex::COpCall *node) +{ + // if node already has ShapeInferenceData, skip + auto shapedata = node->annot(); + if (shapedata != nullptr) + return false; + + auto shape_data = stdex::make_unique(); + copy_shape_values(node, shape_data.get()); // copy node's TensorShape info to ShapeInferenceData + node->annot(std::move(shape_data)); + + return true; +} + } // namespace namespace moco @@ -1273,6 +1288,13 @@ bool FixShapeTransform::run(loco::Graph *graph) #include "Dialect/TFNodes.lst" #undef TENSORFLOW_NODE // clang-format on + + if (as(node)) + { + if (fix_shape(as(node))) + changed = true; + } + else { throw std::runtime_error("Not supported loco::Node type in FixShapeTransform"); } -- 2.7.4