ONE - On-device Neural Engine
Loading...
Searching...
No Matches
RecipeChef.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2020 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
18#include <mio_circle/Helper.h>
19
20#include "Convert.h"
21#include "CircleImport.h"
22#include "CircleOpChef.h"
23#include "CircleOpChefs.h"
24#include "CircleOpRegistry.h"
25
26#include <fstream>
27#include <sstream>
28
29namespace circlechef
30{
31
32void set_inputs(CircleImport *import, circlechef::Operation *operation, const circle::Operator *op)
33{
34 auto tensors = import->tensors();
35 const std::vector<int32_t> &inputs = as_index_vector(op->inputs());
36
37 for (auto input : inputs)
38 {
39 if (input == -1)
40 {
41 operation->add_input("");
42 }
43 else
44 {
45 auto tensor = tensors->Get(input);
46 std::string name = mio::circle::tensor_name(tensor);
47 operation->add_input(name);
48 }
49 }
50}
51
52void set_outputs(CircleImport *import, circlechef::Operation *operation, const circle::Operator *op)
53{
54 auto tensors = import->tensors();
55 const std::vector<int32_t> &outputs = as_index_vector(op->outputs());
56
57 for (auto output : outputs)
58 {
59 auto tensor = tensors->Get(output);
60 std::string name = mio::circle::tensor_name(tensor);
61 operation->add_output(name);
62 }
63}
64
71std::unique_ptr<ModelRecipe> generate_recipe(const circle::Model *model)
72{
73 std::unique_ptr<ModelRecipe> model_recipe{new ModelRecipe()};
74
75 CircleImport circle_import(model);
76
77 assert(circle_import.num_subgraph() == 1);
78 circle_import.select_sub_graph(0);
79
80 auto tensors = circle_import.tensors();
81 auto buffers = circle_import.buffers();
82 auto operators = circle_import.operators();
83
84 // operand fillers for adding all operators
85 for (uint32_t i = 0; i < operators->size(); ++i)
86 {
87 const auto *op = operators->Get(i);
88 circle::BuiltinOperator builtincode = circle_import.builtin_code(op);
89
90 if (const auto *graph_builder = CircleOpRegistry::get().lookup(builtincode))
91 {
92 graph_builder->filler(op, &circle_import, model_recipe.get());
93 }
94 else
95 {
96 std::string opcodename = circle_import.opcode_name(op);
97 throw std::runtime_error{"Not supported: " + opcodename};
98 }
99 }
100
101 // add all operands(tensors)
102 for (uint32_t i = 0; i < tensors->size(); ++i)
103 {
104 auto tensor = tensors->Get(i);
105
106 // check buffer
107 if (tensor->buffer() >= buffers->size())
108 throw std::runtime_error{"file load failed"};
109
110 ::circlechef::Operand *operand = model_recipe->add_operand();
111
112 operand->set_name(mio::circle::tensor_name(tensor));
113 operand->set_type(as_circlechef_type(tensor->type()));
114
115 std::vector<int32_t> dims = as_index_vector(tensor->shape());
116 ::circlechef::TensorShape *shape = operand->mutable_shape();
117 for (auto dim : dims)
118 {
119 shape->add_dim(dim);
120 }
121
122 // filler for weights, bias and so on
123 std::vector<int32_t> expvalues;
124 std::vector<float> expfvalues;
125 if (circle_import.get_tensor_filler(i))
126 {
127 circlechef::TensorFiller *filler = operand->mutable_filler();
128 // Note: it is OK to use random weights for functionality validation
129 filler->set_tag("gaussian");
130 filler->add_arg("0.0"); // average
131 filler->add_arg("0.1"); // standard deviation
132 }
133 else if (circle_import.get_tensor_filler(i, expvalues))
134 {
135 circlechef::TensorFiller *filler = operand->mutable_filler();
136 filler->set_tag("explicit");
137 for (auto value : expvalues)
138 {
139 std::ostringstream ss;
140 ss << value;
141 filler->add_arg(ss.str());
142 }
143 }
144 else if (circle_import.get_tensor_filler(i, expfvalues))
145 {
146 circlechef::TensorFiller *filler = operand->mutable_filler();
147 filler->set_tag("explicit");
148 for (auto value : expfvalues)
149 {
150 std::ostringstream ss;
151 ss << value;
152 filler->add_arg(ss.str());
153 }
154 }
155
156 auto quant = tensor->quantization();
157 if (quant != nullptr)
158 {
159 // Note: Calling 'operand->mutable_quant()' will create empty 'quant' node
160 // in the recipe file. We want this only when valid parameter exist.
161 if (quant->min() != nullptr && quant->min()->size() > 0)
162 {
163 circlechef::TensorQuantization *chef_quant = operand->mutable_quant();
164 for (uint32_t idx = 0; idx < quant->min()->size(); ++idx)
165 chef_quant->add_min(quant->min()->Get(idx));
166 }
167 if (quant->max() != nullptr && quant->max()->size() > 0)
168 {
169 circlechef::TensorQuantization *chef_quant = operand->mutable_quant();
170 for (uint32_t idx = 0; idx < quant->max()->size(); idx++)
171 chef_quant->add_max(quant->max()->Get(idx));
172 }
173 if (quant->scale() != nullptr && quant->scale()->size() > 0)
174 {
175 circlechef::TensorQuantization *chef_quant = operand->mutable_quant();
176 for (uint32_t idx = 0; idx < quant->scale()->size(); ++idx)
177 chef_quant->add_scale(quant->scale()->Get(idx));
178 }
179 if (quant->zero_point() != nullptr && quant->zero_point()->size() > 0)
180 {
181 circlechef::TensorQuantization *chef_quant = operand->mutable_quant();
182 for (uint32_t idx = 0; idx < quant->zero_point()->size(); ++idx)
183 chef_quant->add_zero_point(quant->zero_point()->Get(idx));
184 }
185 circlechef::TensorQuantization *chef_quant = operand->mutable_quant();
186 chef_quant->set_quantized_dimension(quant->quantized_dimension());
187 }
188
189 auto shape_signature = tensor->shape_signature();
190 if (shape_signature != nullptr)
191 {
192 circlechef::ShapeSignature *chef_shape_signature = operand->mutable_shape_signature();
193 for (uint32_t i = 0; i < shape_signature->size(); ++i)
194 {
195 chef_shape_signature->add_dim(shape_signature->Get(i));
196 }
197 }
198 }
199
200 // add all operators
201 for (uint32_t i = 0; i < operators->size(); ++i)
202 {
203 const auto *op = operators->Get(i);
204 circle::BuiltinOperator builtincode = circle_import.builtin_code(op);
205
206 if (const auto *graph_builder = CircleOpRegistry::get().lookup(builtincode))
207 {
208 auto operation = graph_builder->build(op, &circle_import, model_recipe.get());
209
210 // common for all operators: inputs, outputs
211 set_inputs(&circle_import, operation, op);
212 set_outputs(&circle_import, operation, op);
213 }
214 else
215 {
216 std::string opcodename = circle_import.opcode_name(op);
217 throw std::runtime_error{"Not supported: " + opcodename};
218 }
219 }
220
221 // network inputs/outputs
222 const std::vector<int32_t> &inputs = circle_import.inputs();
223 const std::vector<int32_t> &outputs = circle_import.outputs();
224
225 for (const auto input : inputs)
226 {
227 auto tensor = tensors->Get(input);
228 std::string name = mio::circle::tensor_name(tensor);
229
230 model_recipe->add_input(name);
231 }
232 for (const auto output : outputs)
233 {
234 auto tensor = tensors->Get(output);
235 std::string name = mio::circle::tensor_name(tensor);
236
237 model_recipe->add_output(name);
238 }
239
240 return std::move(model_recipe);
241}
242
243bool write_recipe(const std::string &filename, std::unique_ptr<ModelRecipe> &recipe)
244{
245 std::fstream fo(filename, std::ios::binary | std::ios::out);
246
247 if (!fo.is_open())
248 {
249 throw std::runtime_error{"file store failed"};
250 }
251
252 // Note: SerializeToString() or SerializeToOstream() writes in binary mode
253 // DebugString() and Utf8DebugString() will print as a human readable text
254 fo << recipe->Utf8DebugString();
255
256 fo.close();
257
258 return true;
259}
260
261} // namespace circlechef
Loads TF lite file and provides helpers to access attributes.
const CircleBuffers_t * buffers()
bool select_sub_graph(uint32_t subgraph)
const CircleOperators_t * operators()
const CircleTensors_t * tensors()
const std::vector< int32_t > & outputs() const
uint32_t num_subgraph() const
circle::BuiltinOperator builtin_code(const circle::Operator *op) const
std::string opcode_name(const circle::Operator *op) const
const std::vector< int32_t > & inputs() const
static CircleOpRegistry & get()
const CircleOpChef * lookup(circle::BuiltinOperator op) const
Returns registered CircleOpChef pointer for BuiltinOperator or nullptr if not registered.
return_type Get(uoffset_t i) const
bool get_tensor_filler(uint32_t tensor_index)
This will return true if the tensor by index, needs a filler option.
std::vector< T > as_index_vector(const flatbuffers::Vector< T > *flat_array)
Definition Convert.h:43
std::unique_ptr< ModelRecipe > generate_recipe(const circle::Model *model)
Create ModelRecipe from circle::Model.
void set_outputs(CircleImport *import, circlechef::Operation *operation, const circle::Operator *op)
bool write_recipe(const std::string &filename, std::unique_ptr< ModelRecipe > &recipe)
Write ModelRecipe to file with given name.
void set_inputs(CircleImport *import, circlechef::Operation *operation, const circle::Operator *op)
circlechef::TensorType as_circlechef_type(const circle::TensorType type)
Definition Convert.cpp:22
const char * tensor_name(const ::circle::Tensor *tensor)
Definition Helper.cpp:69