From: 채성우/On-Device Lab(SR)/Engineer/삼성전자 Date: Mon, 8 Jul 2019 09:36:23 +0000 (+0000) Subject: [moco/tf] Introduce ReLU6 (#4131) X-Git-Tag: nncc_backup~152 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=14ad12a8d4ecdd502e9e7af5d38294679277feac;p=platform%2Fcore%2Fml%2Fnnfw.git [moco/tf] Introduce ReLU6 (#4131) * [moco/tf] Introduce ReLU6 This commit introduce ReLU6 operation to moco. Signed-off-by: seongwoo * This commit apply for comments. --- diff --git a/contrib/moco-tf/src/Op/Relu6.cpp b/contrib/moco-tf/src/Op/Relu6.cpp new file mode 100644 index 0000000..f4bad85 --- /dev/null +++ b/contrib/moco-tf/src/Op/Relu6.cpp @@ -0,0 +1,86 @@ +/* + * 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 "GraphBuilder.h" + +#include + +namespace moco +{ +namespace tf +{ +/** + * @brief GraphBuilder for Relu6 node + */ +class Relu6GraphBuilder final : public GraphBuilder +{ +public: + bool validate(const tensorflow::NodeDef &) const override; + void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override; +}; + +class ReLU6GraphUpdate final : public GraphUpdate +{ +public: + ReLU6GraphUpdate(loco::ReLU6 *node, const TensorName &&name) : _node(node), _name(name) {} + + void input(const SymbolTable *) const override; + +private: + loco::ReLU6 *_node; + const TensorName _name; +}; + +bool Relu6GraphBuilder::validate(const tensorflow::NodeDef &node) const +{ + // ReLU6 node SHOULD have only one input + if (node.input_size() != 1) + return false; + return true; +} + +void Relu6GraphBuilder::build(const tensorflow::NodeDef &node, GraphBuilderContext *context) const +{ + assert(context != nullptr); + + loco::Graph *graph = context->graph(); + SymbolTable *tensor_names = context->tensor_names(); + UpdateQueue *updates = context->updates(); + + // Create a "ReLU6" node for Relu6 + auto relu6_node = graph->nodes()->create(); + + // register string-name to node + TensorName output_name(node.name(), 0); + tensor_names->enroll(output_name, relu6_node); + + // Queue node input update + auto update = stdex::make_unique(relu6_node, TensorName(node.input(0))); + updates->enroll(std::move(update)); +} + +void ReLU6GraphUpdate::input(const SymbolTable *table) const +{ + loco::Node *target = table->node(_name); + _node->input(target); +} + +} // namespace tf +} // namespace moco + +#include "GraphBuilderRegistry.h" + +REGISTER_OP_BUILDER(Relu6, Relu6GraphBuilder) diff --git a/contrib/moco-tf/src/Op/Relu6.test.cpp b/contrib/moco-tf/src/Op/Relu6.test.cpp new file mode 100644 index 0000000..6104a5f --- /dev/null +++ b/contrib/moco-tf/src/Op/Relu6.test.cpp @@ -0,0 +1,112 @@ +/* + * 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 "TestHelper.h" + +#include "Importer.h" + +#include + +#include + +#include + +using namespace moco::tf::test; + +namespace +{ + +// clang-format off +const char *relu6_01_pbtxtdata = STRING_CONTENT( +node { + name: "Placeholder" + op: "Placeholder" + attr { + key: "dtype" + value { + type: DT_FLOAT + } + } + attr { + key: "shape" + value { + shape { + dim { + size: 2 + } + dim { + size: 3 + } + } + } + } +} +node { + name: "ReLU6" + op: "Relu6" + input: "Placeholder" + attr { + key: "T" + value { + type: DT_FLOAT + } + } +} +); +// clang-format on + +} // namespace + +TEST(TensorFlowImport, relu6_01) +{ + moco::tf::Importer importer; + moco::tf::ModelSignature signature; + + signature.add_input(moco::tf::TensorName("Placeholder", 0)); + signature.add_output(moco::tf::TensorName("ReLU6", 0)); + + tensorflow::GraphDef graph_def; + EXPECT_TRUE(parse_graphdef(relu6_01_pbtxtdata, graph_def)); + std::unique_ptr graph = importer.import(signature, graph_def); + + // Check input shape is correct + loco::Graph::InputContext *inputs = graph->inputs(); + ASSERT_EQ(inputs->size(), 1); + loco::GraphInput *input = inputs->at(0); + loco::Pull *pull = input->node(); + ASSERT_EQ(pull->dtype(), loco::DataType::FLOAT32); + ASSERT_EQ(pull->rank(), 2); + loco::Dimension dim2 = loco::make_dimension(2); + loco::Dimension dim3 = loco::make_dimension(3); + ASSERT_EQ(pull->dim(0).value(), dim2.value()); + ASSERT_EQ(pull->dim(1).value(), dim3.value()); + + // Check nodes are correct type + loco::Graph::OutputContext *outputs = graph->outputs(); + ASSERT_EQ(outputs->size(), 1); + loco::GraphOutput *output = outputs->at(0); + loco::Push *push = output->node(); + // Currently we don't know the shape of output node(s) so skip shape checking + + loco::Graph::NodeContext *nodes = graph->nodes(); + ASSERT_EQ(nodes->size(), 3); + loco::Pull *node0 = dynamic_cast(nodes->at(0)); + ASSERT_EQ(node0, pull); + loco::Push *node2 = dynamic_cast(nodes->at(2)); + ASSERT_EQ(node2, push); + loco::ReLU6 *node1 = dynamic_cast(nodes->at(1)); + ASSERT_NE(node1, nullptr); +} diff --git a/contrib/moco-tf/src/Transforms/CanonicalNodes.lst b/contrib/moco-tf/src/Transforms/CanonicalNodes.lst index ee0c903..154898f 100644 --- a/contrib/moco-tf/src/Transforms/CanonicalNodes.lst +++ b/contrib/moco-tf/src/Transforms/CanonicalNodes.lst @@ -17,4 +17,5 @@ CANONICAL_NODE(MaxPool2D) CANONICAL_NODE(Pull) CANONICAL_NODE(Push) CANONICAL_NODE(ReLU) +CANONICAL_NODE(ReLU6) CANONICAL_NODE(TensorConcat) diff --git a/contrib/moco-tf/src/Transforms/FixPaddingTransform.cpp b/contrib/moco-tf/src/Transforms/FixPaddingTransform.cpp index 6893078..9bba380 100644 --- a/contrib/moco-tf/src/Transforms/FixPaddingTransform.cpp +++ b/contrib/moco-tf/src/Transforms/FixPaddingTransform.cpp @@ -306,6 +306,12 @@ bool fix_padding(loco::ReLU *node) return false; } +bool fix_padding(loco::ReLU6 *node) +{ + // Nothing to do with padding + return false; +} + bool fix_padding(loco::TensorConcat *node) { // Nothing to do with padding diff --git a/contrib/moco-tf/src/Transforms/FixShapeTransform.cpp b/contrib/moco-tf/src/Transforms/FixShapeTransform.cpp index 6088b46..5b3ce12 100644 --- a/contrib/moco-tf/src/Transforms/FixShapeTransform.cpp +++ b/contrib/moco-tf/src/Transforms/FixShapeTransform.cpp @@ -476,6 +476,13 @@ bool fix_shape(loco::ReLU *node) return copy_shapedata(input, node); } +bool fix_shape(loco::ReLU6 *node) +{ + // Output shape is same as the input + auto input = node->input(); + return copy_shapedata(input, node); +} + bool fix_shape(loco::TensorConcat *node) { auto concat_data = node->annot();