summaryrefslogtreecommitdiff
path: root/compiler/moco-tf/src/Frontend.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/moco-tf/src/Frontend.cpp')
-rw-r--r--compiler/moco-tf/src/Frontend.cpp277
1 files changed, 277 insertions, 0 deletions
diff --git a/compiler/moco-tf/src/Frontend.cpp b/compiler/moco-tf/src/Frontend.cpp
new file mode 100644
index 000000000..a17d5dd0e
--- /dev/null
+++ b/compiler/moco-tf/src/Frontend.cpp
@@ -0,0 +1,277 @@
+/*
+ * 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 <moco/tf/Frontend.h>
+#include <moco/Importer.h>
+#include <moco/IR/TFNode.h>
+#include <moco/Log.h>
+
+#include <moco/Import/GraphBuilderRegistry.h>
+
+#include "Canonicalizer.h"
+#include "Optimizer.h"
+#include "TFOptimizer.h"
+
+#include "Transforms.h"
+
+#include "Op/COpCall.h"
+
+#include <loco/Service/ShapeInference.h>
+
+#include <stdex/Memory.h>
+#include <oops/UserExn.h>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/text_format.h>
+
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <stdexcept>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+namespace
+{
+
+bool load_text(std::istream *stream, tensorflow::GraphDef &graph_def)
+{
+ google::protobuf::io::IstreamInputStream iis(stream);
+
+ return google::protobuf::TextFormat::Parse(&iis, &graph_def);
+}
+
+bool load_binary(std::istream *stream, tensorflow::GraphDef &graph_def)
+{
+ google::protobuf::io::IstreamInputStream iis(stream);
+ google::protobuf::io::CodedInputStream cis(&iis);
+
+ return graph_def.ParseFromCodedStream(&cis);
+}
+
+void load_tf(std::istream *stream, moco::tf::Frontend::FileType type,
+ tensorflow::GraphDef &graph_def)
+{
+ bool result = (type == moco::tf::Frontend::FileType::Text) ? load_text(stream, graph_def)
+ : load_binary(stream, graph_def);
+ if (!result)
+ {
+ throw oops::UserExn("Failed to parse prototxt from stream");
+ }
+}
+
+// If Placeholder has no shape attribute, set unknown_rank property to true.
+void set_unknown_rank(tensorflow::GraphDef &tf_graph_def)
+{
+ for (auto &n : *tf_graph_def.mutable_node())
+ {
+ if (n.op().compare("Placeholder"))
+ continue;
+
+ auto iter = n.attr().find("shape");
+ if (iter == n.attr().end())
+ {
+ tensorflow::AttrValue attr;
+ attr.mutable_shape()->set_unknown_rank(true);
+ n.mutable_attr()->insert({"shape", attr});
+ }
+ }
+}
+
+/**
+ * @brief Set input shape according to signature if node has unknown shape in GraphDef.
+ *
+ * @note If shape you provided is wrong or not enough, it returns false.
+ */
+bool set_input_shape(const moco::ModelSignature &signature, tensorflow::GraphDef &tf_graph_def)
+{
+ for (auto &n : *tf_graph_def.mutable_node())
+ {
+ if (n.op().compare("Placeholder"))
+ continue;
+
+ auto node_shape = n.mutable_attr()->at("shape").mutable_shape();
+ auto sig_shape = signature.shape(n.name() + ":0");
+
+ if (node_shape->unknown_rank() || !node_shape->dim_size())
+ {
+ // If shape in GraphDef is unknown, user must provide the shape info.
+ if (sig_shape == nullptr)
+ return false;
+ node_shape->clear_unknown_rank();
+ for (uint32_t i = 0; i < sig_shape->rank(); i++)
+ node_shape->add_dim()->set_size(-1);
+ }
+
+ for (uint32_t d = 0; d < node_shape->dim_size(); d++)
+ {
+ if (node_shape->mutable_dim(d)->size() == -1)
+ {
+ if (sig_shape == nullptr)
+ return false;
+ node_shape->mutable_dim(d)->set_size(sig_shape->dim(d));
+ }
+ else
+ {
+ // If User provide shape info though it already exists in GraphDef, make sure it matches
+ // the shape of GraphDef.
+ if (sig_shape && node_shape->dim(d).size() != sig_shape->dim(d))
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void transform_tf(const moco::ModelSignature &signature, tensorflow::GraphDef &tf_graph_def)
+{
+ set_unknown_rank(tf_graph_def);
+ if (!set_input_shape(signature, tf_graph_def))
+ oops::UserExn("Info you provided may be wrong or not enough. Please check the info file.");
+}
+
+/**
+ * @brief Returns GraphBuilderRegistry that looks up default registry and additions
+ * such as custom op
+ */
+moco::GraphBuilderRegistry make_graph_builder_registry(const moco::ModelSignature &sig)
+{
+ moco::GraphBuilderRegistry registry{&moco::GraphBuilderRegistry::get()};
+
+ // build a COpCallGraphBuilder per custom op type
+ for (const auto &custom_op : sig.customops())
+ {
+ std::unique_ptr<moco::tf::COpCallGraphBuilder> builder =
+ stdex::make_unique<moco::tf::COpCallGraphBuilder>(&sig);
+ registry.add(custom_op, std::move(builder));
+ }
+
+ return registry;
+}
+
+} // namespace
+
+// TODO Find a proper place for this function
+
+namespace
+{
+
+loco::TensorShape tensor_shape(loco::Node *node)
+{
+ assert(loco::shape_known(node));
+ auto node_shape = loco::shape_get(node);
+ return node_shape.as<loco::TensorShape>();
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+Frontend::Frontend()
+{
+ // DO NOTHING
+}
+
+std::unique_ptr<loco::Graph> Frontend::load(const ModelSignature &signature, const char *modelfile,
+ FileType type) const
+{
+ // Using c++ standard library, rather than file descriptor, makes these lines portable
+ std::ifstream ifs{modelfile, std::ios::in | std::ios::binary};
+ return load(signature, &ifs, type);
+}
+
+std::unique_ptr<loco::Graph> Frontend::load(const ModelSignature &signature, std::istream *stream,
+ FileType type) const
+{
+ tensorflow::GraphDef tf_graph_def;
+
+ load_tf(stream, type, tf_graph_def);
+
+ transform_tf(signature, tf_graph_def);
+
+ auto graph = import(signature, tf_graph_def);
+
+ return std::move(graph);
+}
+
+std::unique_ptr<loco::Graph> Frontend::import(const ModelSignature &signature,
+ tensorflow::GraphDef &tf_graph_def) const
+{
+ LOGGER(frontend);
+
+ // Let's use GraphBuilderRegistry with COpCallGraphBuilder
+ GraphBuilderRegistry registry = make_graph_builder_registry(signature);
+
+ Importer importer{&registry};
+
+ INFO(frontend) << ">>";
+ INFO(frontend) << ">> Import stage started";
+ INFO(frontend) << ">>";
+ auto graph = importer.import(signature, tf_graph_def);
+
+ TFOptimizer tfoptimizier;
+
+ // Transform TFNodes
+ INFO(frontend) << ">>";
+ INFO(frontend) << ">> TF optimize stage started";
+ INFO(frontend) << ">>";
+ tfoptimizier.optimize(graph.get());
+
+ // Fill graph-level input/output shape
+ //
+ // ASSUMPTION! All the shapes are known at this point
+ for (uint32_t n = 0; n < graph->inputs()->size(); ++n)
+ {
+ auto input = graph->inputs()->at(n);
+ auto input_node = moco::placeholder_node(graph.get(), n);
+ assert(input_node != nullptr);
+ input->shape(stdex::make_unique<loco::TensorShape>(tensor_shape(input_node)));
+ }
+
+ for (uint32_t n = 0; n < graph->outputs()->size(); ++n)
+ {
+ auto output = graph->outputs()->at(n);
+ auto output_node = moco::push_node(graph.get(), n);
+ assert(output_node != nullptr);
+ output->shape(stdex::make_unique<loco::TensorShape>(::tensor_shape(output_node)));
+ }
+
+ // Convert graph to hold only Canonical dialect
+ Canonicalizer canonicalizer;
+
+ INFO(frontend) << ">>";
+ INFO(frontend) << ">> Canonicalize stage started";
+ INFO(frontend) << ">>";
+ canonicalizer.canonicalize(graph.get());
+
+ // Optimize imported loco::Graph
+ Optimizer optimizer;
+
+ INFO(frontend) << ">>";
+ INFO(frontend) << ">> Canonical optimize stage started";
+ INFO(frontend) << ">>";
+ optimizer.optimize(graph.get());
+
+ return std::move(graph);
+}
+
+} // namespace tf
+} // namespace moco