ONE - On-device Neural Engine
Loading...
Searching...
No Matches
Frontend.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <moco/tf/Frontend.h>
18#include <moco/Importer.h>
19#include <moco/IR/TFNode.h>
20#include <moco/Log.h>
21
23
24#include "Canonicalizer.h"
25#include "Optimizer.h"
26#include "TFOptimizer.h"
27
28#include "Transforms.h"
29
30#include "Op/COpCall.h"
31
33
34#include <oops/UserExn.h>
35
36#include <google/protobuf/io/coded_stream.h>
37#include <google/protobuf/io/zero_copy_stream_impl.h>
38#include <google/protobuf/text_format.h>
39
40#include <memory>
41#include <iostream>
42#include <sstream>
43#include <fstream>
44#include <stdexcept>
45
46#include <fcntl.h>
47#include <unistd.h>
48
49namespace
50{
51
52bool load_text(std::istream *stream, tensorflow::GraphDef &graph_def)
53{
54 google::protobuf::io::IstreamInputStream iis(stream);
55
56 return google::protobuf::TextFormat::Parse(&iis, &graph_def);
57}
58
59bool load_binary(std::istream *stream, tensorflow::GraphDef &graph_def)
60{
61 google::protobuf::io::IstreamInputStream iis(stream);
62 google::protobuf::io::CodedInputStream cis(&iis);
63
64 return graph_def.ParseFromCodedStream(&cis);
65}
66
67void load_tf(std::istream *stream, moco::tf::Frontend::FileType type,
68 tensorflow::GraphDef &graph_def)
69{
70 bool result = (type == moco::tf::Frontend::FileType::Text) ? load_text(stream, graph_def)
71 : load_binary(stream, graph_def);
72 if (!result)
73 {
74 throw oops::UserExn("Failed to parse prototxt from stream");
75 }
76}
77
78// If Placeholder has no shape attribute, set unknown_rank property to true.
79void set_unknown_rank(tensorflow::GraphDef &tf_graph_def)
80{
81 for (auto &n : *tf_graph_def.mutable_node())
82 {
83 if (n.op().compare("Placeholder"))
84 continue;
85
86 auto iter = n.attr().find("shape");
87 if (iter == n.attr().end())
88 {
89 tensorflow::AttrValue attr;
90 attr.mutable_shape()->set_unknown_rank(true);
91 n.mutable_attr()->insert({"shape", attr});
92 }
93 }
94}
95
101bool set_input_shape(const moco::ModelSignature &signature, tensorflow::GraphDef &tf_graph_def)
102{
103 for (auto &n : *tf_graph_def.mutable_node())
104 {
105 if (n.op().compare("Placeholder"))
106 continue;
107
108 auto node_shape = n.mutable_attr()->at("shape").mutable_shape();
109 auto sig_shape = signature.shape(n.name() + ":0");
110
111 if (node_shape->unknown_rank() || !node_shape->dim_size())
112 {
113 // If shape in GraphDef is unknown, user must provide the shape info.
114 if (sig_shape == nullptr)
115 return false;
116 node_shape->clear_unknown_rank();
117 for (uint32_t i = 0; i < sig_shape->rank(); i++)
118 node_shape->add_dim()->set_size(-1);
119 }
120
121 for (uint32_t d = 0; d < node_shape->dim_size(); d++)
122 {
123 if (node_shape->mutable_dim(d)->size() == -1)
124 {
125 if (sig_shape == nullptr)
126 return false;
127 node_shape->mutable_dim(d)->set_size(sig_shape->dim(d));
128 }
129 else
130 {
131 // If User provide shape info though it already exists in GraphDef, make sure it matches
132 // the shape of GraphDef.
133 if (sig_shape && node_shape->dim(d).size() != sig_shape->dim(d))
134 return false;
135 }
136 }
137 }
138 return true;
139}
140
141void transform_tf(const moco::ModelSignature &signature, tensorflow::GraphDef &tf_graph_def)
142{
143 set_unknown_rank(tf_graph_def);
144 if (!set_input_shape(signature, tf_graph_def))
145 oops::UserExn("Info you provided may be wrong or not enough. Please check the info file.");
146}
147
152moco::GraphBuilderRegistry make_graph_builder_registry(const moco::ModelSignature &sig)
153{
155
156 // build a COpCallGraphBuilder per custom op type
157 for (const auto &custom_op : sig.customops())
158 {
159 std::unique_ptr<moco::tf::COpCallGraphBuilder> builder =
160 std::make_unique<moco::tf::COpCallGraphBuilder>(&sig);
161 registry.add(custom_op, std::move(builder));
162 }
163
164 return registry;
165}
166
167} // namespace
168
169// TODO Find a proper place for this function
170
171namespace
172{
173
174loco::TensorShape tensor_shape(loco::Node *node)
175{
176 assert(loco::shape_known(node));
177 auto node_shape = loco::shape_get(node);
179}
180
181} // namespace
182
183namespace moco
184{
185namespace tf
186{
187
189{
190 // DO NOTHING
191}
192
193std::unique_ptr<loco::Graph> Frontend::load(const ModelSignature &signature, const char *modelfile,
194 FileType type) const
195{
196 // Using c++ standard library, rather than file descriptor, makes these lines portable
197 std::ifstream ifs{modelfile, std::ios::in | std::ios::binary};
198 return load(signature, &ifs, type);
199}
200
201std::unique_ptr<loco::Graph> Frontend::load(const ModelSignature &signature, std::istream *stream,
202 FileType type) const
203{
204 tensorflow::GraphDef tf_graph_def;
205
206 load_tf(stream, type, tf_graph_def);
207
208 transform_tf(signature, tf_graph_def);
209
210 auto graph = import(signature, tf_graph_def);
211
212 return std::move(graph);
213}
214
215std::unique_ptr<loco::Graph> Frontend::import(const ModelSignature &signature,
216 tensorflow::GraphDef &tf_graph_def) const
217{
218 LOGGER(frontend);
219
220 // Let's use GraphBuilderRegistry with COpCallGraphBuilder
221 GraphBuilderRegistry registry = make_graph_builder_registry(signature);
222
223 Importer importer{&registry};
224
225 INFO(frontend) << ">>";
226 INFO(frontend) << ">> Import stage started";
227 INFO(frontend) << ">>";
228 auto graph = importer.import(signature, tf_graph_def);
229
230 TFOptimizer tfoptimizier;
231
232 // Transform TFNodes
233 INFO(frontend) << ">>";
234 INFO(frontend) << ">> TF optimize stage started";
235 INFO(frontend) << ">>";
236 tfoptimizier.optimize(graph.get());
237
238 // Fill graph-level input/output shape
239 //
240 // ASSUMPTION! All the shapes are known at this point
241 for (uint32_t n = 0; n < graph->inputs()->size(); ++n)
242 {
243 auto input = graph->inputs()->at(n);
244 auto input_node = moco::placeholder_node(graph.get(), n);
245 assert(input_node != nullptr);
246 input->shape(std::make_unique<loco::TensorShape>(tensor_shape(input_node)));
247 }
248
249 for (uint32_t n = 0; n < graph->outputs()->size(); ++n)
250 {
251 auto output = graph->outputs()->at(n);
252 auto output_node = moco::push_node(graph.get(), n);
253 assert(output_node != nullptr);
254 output->shape(std::make_unique<loco::TensorShape>(::tensor_shape(output_node)));
255 }
256
257 // Convert graph to hold only Canonical dialect
258 Canonicalizer canonicalizer;
259
260 INFO(frontend) << ">>";
261 INFO(frontend) << ">> Canonicalize stage started";
262 INFO(frontend) << ">>";
263 canonicalizer.canonicalize(graph.get());
264
265 // Optimize imported loco::Graph
266 Optimizer optimizer;
267
268 INFO(frontend) << ">>";
269 INFO(frontend) << ">> Canonical optimize stage started";
270 INFO(frontend) << ">>";
271 optimizer.optimize(graph.get());
272
273 return std::move(graph);
274}
275
276} // namespace tf
277} // namespace moco
#define LOGGER(name)
Definition Log.h:65
#define INFO(name)
Definition Log.h:68
enco::Bundle load(void) const override
Definition Frontend.cpp:40
Logical unit of computation.
Definition Node.h:54
ShapeType as(void) const
Class to return graph builder for TF nodes.
static GraphBuilderRegistry & get()
std::unique_ptr< loco::Graph > load(const ModelSignature &, const char *, FileType) const
Definition Frontend.cpp:193
Exception to user.
Definition UserExn.h:42
result
Definition infer.py:103
type
Definition infer.py:18
bool shape_known(const Node *node)
NodeShape shape_get(const Node *node)
Definition Log.h:23
TFPush * push_node(loco::Graph *g, const loco::GraphOutputIndex &index)
Find a TFPush node with a given output index.
Definition TFNode.cpp:121
loco::NodeShape node_shape(const loco::Node *node)
TFPlaceholder * placeholder_node(loco::Graph *g, const loco::GraphInputIndex &idx)
Definition TFNode.cpp:84
int32_t size[5]
Definition Slice.cpp:35
Class to store information to run a model. Normally this info comes from users via CLI params or conf...
void shape(const std::string &node_name, const angkor::TensorShape &shape)
Adds node name and its shape provided from user.