ONE - On-device Neural Engine
Loading...
Searching...
No Matches
ModelChef.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2018 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 "tflchef/ModelChef.h"
19#include <souschef/Registry.h>
20
21#include "Convert.h"
22
23#include <souschef/DataChefs.h>
24
25#include "OpChef.h"
26#include "OpChefs.h"
27
28#include <souschef/Dataset.h>
29#include <souschef/Dims.h>
30
31#include "Log.h"
32
33#include <iterator>
34#include <map>
35#include <string>
36#include <vector>
37
38#include <cassert>
39#include <fstream>
40#include <iostream>
41#include <numeric>
42#include <sstream>
43#include <stdexcept>
44
45using namespace souschef;
46
47namespace
48{
49
50struct DataChefRegistry final : public Registry<DataChefFactory>
51{
52};
53
54DataChefRegistry &data_chef_registry(const tflchef::TensorType &type)
55{
56 static DataChefRegistry s32;
57 static DataChefRegistry s64;
58 static DataChefRegistry fp32;
59 static DataChefRegistry u8;
60 static DataChefRegistry string;
61 static DataChefRegistry boolean;
62 static DataChefRegistry s16;
63 static DataChefRegistry fp16;
64 static DataChefRegistry s8;
65 static DataChefRegistry s4;
66
67 switch (type)
68 {
69 case tflchef::INT32:
70 return s32;
71 case tflchef::INT64:
72 return s64;
73 case tflchef::FLOAT32:
74 return fp32;
75 case tflchef::FLOAT16:
76 return fp16;
77 case tflchef::UINT8:
78 return u8;
79 case tflchef::STRING:
80 return string;
81 case tflchef::BOOL:
82 return boolean;
83 case tflchef::INT16:
84 return s16;
85 case tflchef::INT8:
86 return s8;
87 case tflchef::INT4:
88 return s4;
89 default:
90 break;
91 }
92
93 throw std::runtime_error{"Unknown tensor type"};
94}
95
96struct OpChefRegistry final : public Registry<OpChefFactory>
97{
98};
99
100OpChefRegistry &op_chef_registry(void)
101{
102 static OpChefRegistry registry;
103 return registry;
104}
105
107std::map<tflite::BuiltinOperator, int32_t>
108gather_builtincode_map(const ::tflchef::ModelRecipe &model_recipe)
109{
110 // Key and value of the map are BuiltinOperator and operator version
111 std::map<tflite::BuiltinOperator, int32_t> builtin_map;
112
113 for (const auto &operation : model_recipe.operation())
114 {
115 if (operation.type() == "Custom")
116 continue;
117
118 auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation);
119 // Various operation version is unified as the highest version among them
120 if (builtin_map.find(op_chef->code()) == builtin_map.end() ||
121 builtin_map[op_chef->code()] < operation.version())
122 builtin_map[op_chef->code()] = operation.version();
123 }
124
125 // Add ops used in Graphs(subgraphs)
126 for (int g = 0; g < model_recipe.graph_size(); ++g)
127 {
128 const auto &graph = model_recipe.graph(g);
129 for (const auto &operation : graph.operation())
130 {
131 if (operation.type() == "Custom")
132 continue;
133
134 auto op_chef = op_chef_registry().lookup(operation.type()).create(&operation);
135 // Various operation version is unified as the highest version among them
136 if (builtin_map.find(op_chef->code()) == builtin_map.end() ||
137 builtin_map[op_chef->code()] < operation.version())
138 builtin_map[op_chef->code()] = operation.version();
139 }
140 }
141
142 return builtin_map;
143}
144
146std::set<std::string> gather_customcode_set(const ::tflchef::ModelRecipe &model_recipe)
147{
148 std::set<std::string> customcode_set;
149 for (const auto &operation : model_recipe.operation())
150 {
151 if (operation.type() == "Custom")
152 {
153 assert(not operation.custom_code().empty());
154 customcode_set.insert(operation.custom_code());
155 }
156 }
157
158 // Add ops used in Graphs(subgraphs)
159 for (int g = 0; g < model_recipe.graph_size(); ++g)
160 {
161 const auto &graph = model_recipe.graph(g);
162 for (const auto &operation : graph.operation())
163 {
164 if (operation.type() == "Custom")
165 {
166 assert(not operation.custom_code().empty());
167 customcode_set.insert(operation.custom_code());
168 }
169 }
170 }
171
172 return customcode_set;
173}
174
175} // namespace
176
177namespace
178{
179
180using SymboleTable_t = std::map<std::string, int32_t>;
181using SparsityParams_t = flatbuffers::Offset<tflite::SparsityParameters>;
182using SparsityDims_t = std::vector<sparsity::TfLiteDimensionType>;
183using QuantParams_t = flatbuffers::Offset<tflite::QuantizationParameters>;
184
185class ModelChef
186{
187public:
188 ModelChef() = default;
189
190public:
191 void init(void);
192 void cook(const ::tflchef::ModelRecipe &model_recipe);
193
194private:
195 void prepare_initial_buffer(void);
196 void gather_operator_codes(const ::tflchef::ModelRecipe &model_recipe);
197 void gather_signature_defs(const ::tflchef::ModelRecipe &model_recipe);
198
199 void buffer_sparse_f32(int32_t &buffer_index, DimsI32_t &dims,
200 std::vector<int> &traversal_order_vec, SparsityDims_t &format_vec,
201 souschef::Data &data_vec, SparsityParams_t &sparsity_index);
202 void buffer_sparse_f16(int32_t &buffer_index, DimsI32_t &dims,
203 std::vector<int> &traversal_order_vec, SparsityDims_t &format_vec,
204 souschef::Data &data_vec, SparsityParams_t &sparsity_index);
205 void buffer_sparse(int32_t &buffer_index, const tflchef::Operand &operand, DimsI32_t &dims,
206 souschef::Data &data_vec, SparsityParams_t &sparsity_index);
207
208 void buffer_dense(int32_t &buffer_index, const tflchef::Operand &operand, int32_t count,
209 souschef::Data &data_vec);
210
211 void buffer_empty(int32_t &buffer_index, int32_t buffer_start, const int size_input,
212 const tflchef::Operand &operand, const std::vector<std::string> &input_names,
213 const std::vector<std::string> &output_names);
214
215 void operand_quant(const tflchef::Operand &operand, QuantParams_t &quant_index);
216
217 void operand_sparsity(const tflchef::Operand &operand, SparsityParams_t &sparsity_index);
218
219 template <typename T> void cook_operands(const T &graph);
220
221 template <typename T> void prepare_tensor_symbols(const T &graph, SymboleTable_t &symbol_table);
222
223 template <typename T> void cook_operations(const T &graph, SymboleTable_t &symbol_table);
224
225 template <typename T> void cook_graph(const T &graph, SymboleTable_t &symbol_table);
226
227 bool finalize_ext_buffer(void);
228
229public:
230 const char *get_buffer_pointer(void) const;
231 size_t get_size(void) const;
232
233private:
234 std::unique_ptr<flatbuffers::FlatBufferBuilder> _flatbuffer_builder;
235
236 std::vector<flatbuffers::Offset<::tflite::SignatureDef>> _signdef_vec;
237 std::vector<flatbuffers::Offset<::tflite::Buffer>> _buffer_vec;
238 std::vector<flatbuffers::Offset<::tflite::OperatorCode>> _code_vec;
239 std::vector<flatbuffers::Offset<::tflite::SubGraph>> _subgraph_vec;
240 std::map<tflite::BuiltinOperator, int32_t> _builtin_code_map;
241 std::vector<std::string> _custom_code_vec;
242 // _symbol_tables stores symbol_table of each sub graph
243 // this is used to find tensor ID(index) with tensor name
244 std::vector<SymboleTable_t> _symbol_tables;
245
246 // per graph that needs clear afer graph is processed
247 // Operand-related
248 std::vector<flatbuffers::Offset<::tflite::Tensor>> _tensor_vec;
249 // Operation-related
250 std::vector<flatbuffers::Offset<::tflite::Operator>> _operator_vec;
251
252 std::string _graph_name;
253
254 // store Buffer data to external of FB and use (Buffer) offset/size fields
255 bool _ext_offset = false;
256 std::map<int32_t, std::vector<uint8_t>> _buffer_data_map;
257 std::string _ext_data;
258};
259
260void ModelChef::init(void)
261{
262 _flatbuffer_builder =
263 std::unique_ptr<flatbuffers::FlatBufferBuilder>(new flatbuffers::FlatBufferBuilder(1024));
264}
265
266std::vector<flatbuffers::Offset<tflite::DimensionMetadata>>
267make_dim_metadata_vec(flatbuffers::FlatBufferBuilder *flatbuffer_builder, int32_t dims_count,
268 const std::vector<int> &traversal_order_vec, const SparsityDims_t &format_vec,
269 const std::vector<std::vector<int32_t>> &dim_metadata_src)
270{
271 // Build sparsity parameter.
272 std::vector<flatbuffers::Offset<tflite::DimensionMetadata>> dim_metadata_vec(dims_count);
273 for (int32_t i = 0; i < dims_count; i++)
274 {
275 const int32_t metadata_idx = 2 * i;
276 if (format_vec[traversal_order_vec[i]] == sparsity::kTfLiteDimSparseCSR)
277 {
278 auto array_segments =
279 tflite::CreateInt32Vector(*flatbuffer_builder,
280 flatbuffer_builder->CreateVector(dim_metadata_src[metadata_idx]))
281 .Union();
282 auto array_indices =
283 tflite::CreateInt32Vector(
284 *flatbuffer_builder, flatbuffer_builder->CreateVector(dim_metadata_src[metadata_idx + 1]))
285 .Union();
286 dim_metadata_vec[i] =
287 tflite::CreateDimensionMetadata(*flatbuffer_builder, tflite::DimensionType_SPARSE_CSR, 0,
288 tflite::SparseIndexVector_Int32Vector, array_segments,
289 tflite::SparseIndexVector_Int32Vector, array_indices);
290 }
291 else
292 {
293 dim_metadata_vec[i] = tflite::CreateDimensionMetadata(
294 *flatbuffer_builder, tflite::DimensionType_DENSE, dim_metadata_src[metadata_idx][0]);
295 }
296 }
297 return dim_metadata_vec;
298}
299
300void ModelChef::buffer_sparse_f32(int32_t &buffer_index, DimsI32_t &dims,
301 std::vector<int> &traversal_order_vec, SparsityDims_t &format_vec,
302 souschef::Data &data_vec, SparsityParams_t &sparsity_index)
303{
304 const int32_t dims_count = dims.size();
305
306 ::sparsity::FormatConverter<float> converter(dims, traversal_order_vec, format_vec);
307 converter.DenseToSparse(reinterpret_cast<const float *>(data_vec.data()));
308 const auto &sparse_data = converter.GetData();
309
310 std::vector<uint8_t> sparse_uint8;
311 for (int c = 0; c < sparse_data.size(); ++c)
312 {
313 const float value = sparse_data.at(c);
314 const uint8_t *arr = reinterpret_cast<const uint8_t *>(&value);
315 for (uint32_t b = 0; b < sizeof(float); ++b)
316 {
317 sparse_uint8.emplace_back(arr[b]);
318 }
319 }
320 if (_ext_offset)
321 {
322 buffer_index = _buffer_vec.size();
323 _buffer_data_map[buffer_index] = sparse_uint8;
324
325 auto buffer = tflite::CreateBuffer(*_flatbuffer_builder, 0, 1, 1);
326 _buffer_vec.emplace_back(buffer);
327 }
328 else
329 {
330 auto data = _flatbuffer_builder->CreateVector(sparse_uint8);
331 // Create Buffer
332 tflite::BufferBuilder buffer_builder{*_flatbuffer_builder};
333 buffer_builder.add_data(data);
334 auto buffer = buffer_builder.Finish();
335
336 // Update Buffer Index & Vector
337 buffer_index = _buffer_vec.size();
338 _buffer_vec.emplace_back(buffer);
339 }
340
341 // save SparsityParameters
342 auto traversal_order = _flatbuffer_builder->CreateVector(traversal_order_vec);
343
344 // Create block map
345 std::vector<int> block_map_vec{};
346 auto block_map = _flatbuffer_builder->CreateVector(block_map_vec);
347
348 // Create dimension metadata
349 const auto &dim_metadata_src = converter.GetDimMetadata();
350 auto dim_metadata_vec = make_dim_metadata_vec(_flatbuffer_builder.get(), dims_count,
351 traversal_order_vec, format_vec, dim_metadata_src);
352 auto dim_metadata = _flatbuffer_builder->CreateVector(dim_metadata_vec);
353 sparsity_index = tflite::CreateSparsityParameters(*_flatbuffer_builder, traversal_order,
354 block_map, dim_metadata);
355}
356
357void ModelChef::buffer_sparse_f16(int32_t &buffer_index, DimsI32_t &dims,
358 std::vector<int> &traversal_order_vec, SparsityDims_t &format_vec,
359 souschef::Data &data_vec, SparsityParams_t &sparsity_index)
360{
361 const int32_t dims_count = dims.size();
362
363 ::sparsity::FormatConverter<uint16_t> converter(dims, traversal_order_vec, format_vec);
364 converter.DenseToSparse(reinterpret_cast<const uint16_t *>(data_vec.data()));
365 const auto &sparse_data = converter.GetData();
366
367 std::vector<uint8_t> sparse_uint8;
368 for (int c = 0; c < sparse_data.size(); ++c)
369 {
370 const uint16_t value = sparse_data.at(c);
371 const uint8_t *arr = reinterpret_cast<const uint8_t *>(&value);
372 for (uint32_t b = 0; b < sizeof(uint16_t); ++b)
373 {
374 sparse_uint8.emplace_back(arr[b]);
375 }
376 }
377 if (_ext_offset)
378 {
379 buffer_index = _buffer_vec.size();
380 _buffer_data_map[buffer_index] = sparse_uint8;
381
382 auto buffer = tflite::CreateBuffer(*_flatbuffer_builder, 0, 1, 1);
383 _buffer_vec.emplace_back(buffer);
384 }
385 else
386 {
387 auto data = _flatbuffer_builder->CreateVector(sparse_uint8);
388
389 // Create Buffer
390 tflite::BufferBuilder buffer_builder{*_flatbuffer_builder};
391 buffer_builder.add_data(data);
392 auto buffer = buffer_builder.Finish();
393
394 // Update Buffer Index & Vector
395 buffer_index = _buffer_vec.size();
396 _buffer_vec.emplace_back(buffer);
397 }
398
399 // save SparsityParameters
400 auto traversal_order = _flatbuffer_builder->CreateVector(traversal_order_vec);
401
402 // Create block map
403 std::vector<int> block_map_vec{};
404 auto block_map = _flatbuffer_builder->CreateVector(block_map_vec);
405
406 // Create dimension metadata
407 const auto &dim_metadata_src = converter.GetDimMetadata();
408 auto dim_metadata_vec = make_dim_metadata_vec(_flatbuffer_builder.get(), dims_count,
409 traversal_order_vec, format_vec, dim_metadata_src);
410 auto dim_metadata = _flatbuffer_builder->CreateVector(dim_metadata_vec);
411 sparsity_index = tflite::CreateSparsityParameters(*_flatbuffer_builder, traversal_order,
412 block_map, dim_metadata);
413}
414
415void ModelChef::buffer_sparse(int32_t &buffer_index, const tflchef::Operand &operand,
416 DimsI32_t &dims, souschef::Data &data_vec,
417 SparsityParams_t &sparsity_index)
418{
419 assert(not operand.has_sparsity());
420 assert(operand.has_shape());
421 assert(operand.type() != tflchef::TensorType::INT4);
422
423 const int32_t dims_count = dims.size();
424 std::vector<int> traversal_order_vec;
425 SparsityDims_t format_vec;
426 for (int32_t o = 0; o < dims_count; ++o)
427 traversal_order_vec.push_back(o);
428 for (int32_t o = 0; o < dims_count - 1; ++o)
429 format_vec.push_back(sparsity::kTfLiteDimDense);
430 format_vec.push_back(sparsity::kTfLiteDimSparseCSR);
431
432 if (operand.type() == tflchef::FLOAT32)
433 {
434 buffer_sparse_f32(buffer_index, dims, traversal_order_vec, format_vec, data_vec,
435 sparsity_index);
436 }
437 else if (operand.type() == tflchef::FLOAT16)
438 {
439 buffer_sparse_f16(buffer_index, dims, traversal_order_vec, format_vec, data_vec,
440 sparsity_index);
441 }
442 else
443 {
444 throw std::runtime_error{"NYI: unsupported operand type"};
445 }
446}
447
448void ModelChef::buffer_dense(int32_t &buffer_index, const tflchef::Operand &operand, int32_t count,
449 souschef::Data &data_vec)
450{
451 // pack for INT4 and replace data_vec
452 if (operand.type() == tflchef::TensorType::INT4)
453 {
454 uint32_t packed = (count + 1) / 2;
455 std::vector<uint8_t> data_packed(packed);
456 for (uint32_t idx = 0; idx < packed; ++idx)
457 {
458 uint32_t sidx = idx * 2;
459 data_packed[idx] = data_vec[sidx++] & 0x0f;
460 if (sidx < count)
461 data_packed[idx] |= data_vec[sidx] << 4;
462 }
463 data_vec = data_packed;
464 }
465
466 if (_ext_offset)
467 {
468 buffer_index = _buffer_vec.size();
469 _buffer_data_map[buffer_index] = data_vec;
470
471 auto buffer = tflite::CreateBuffer(*_flatbuffer_builder, 0, 1, 1);
472 _buffer_vec.emplace_back(buffer);
473 }
474 else
475 {
476 auto data = _flatbuffer_builder->CreateVector(data_vec);
477
478 // Create Buffer
479 tflite::BufferBuilder buffer_builder{*_flatbuffer_builder};
480 buffer_builder.add_data(data);
481 auto buffer = buffer_builder.Finish();
482
483 // Update Buffer Index & Vector
484 buffer_index = _buffer_vec.size();
485 _buffer_vec.emplace_back(buffer);
486 }
487}
488
489void ModelChef::buffer_empty(int32_t &buffer_index, int32_t buffer_start, const int size_input,
490 const tflchef::Operand &operand,
491 const std::vector<std::string> &input_names,
492 const std::vector<std::string> &output_names)
493{
494 // if this is input or output, assign to that buffer_index
495 int idx = 0;
496 for (auto it = input_names.begin(); it != input_names.end(); ++it, ++idx)
497 {
498 if (*it == operand.name())
499 {
500 buffer_index = buffer_start + idx;
501 break;
502 }
503 }
504 if (buffer_index == 0)
505 {
506 idx = 0;
507 for (auto it = output_names.begin(); it != output_names.end(); ++it, ++idx)
508 {
509 if (*it == operand.name())
510 {
511 buffer_index = buffer_start + size_input + idx;
512 break;
513 }
514 }
515 }
516 if (buffer_index == 0)
517 {
518 // we couldn't find the buffer; create an empty buffer for this tensor
519 buffer_index = _buffer_vec.size();
520
521 tflite::BufferBuilder buffer_builder{*_flatbuffer_builder};
522 _buffer_vec.emplace_back(buffer_builder.Finish());
523 }
524}
525
526void ModelChef::operand_quant(const tflchef::Operand &operand, QuantParams_t &quant_index)
527{
528 const auto &quant = operand.quant();
529
530 // Create each parameters
531 // NOTE if some parameters are not given, those will be set to default value
532 std::vector<float> quant_max_vec(quant.max_size());
533 std::vector<float> quant_min_vec(quant.min_size());
534 std::vector<float> quant_scale_vec(quant.scale_size());
535 std::vector<int64_t> quant_zero_point_vec(quant.zero_point_size());
536
537 for (uint32_t i = 0; i < quant.max_size(); ++i)
538 quant_max_vec.at(i) = quant.max(i);
539 for (uint32_t i = 0; i < quant.min_size(); ++i)
540 quant_min_vec.at(i) = quant.min(i);
541 for (uint32_t i = 0; i < quant.scale_size(); ++i)
542 quant_scale_vec.at(i) = quant.scale(i);
543 for (uint32_t i = 0; i < quant.zero_point_size(); ++i)
544 quant_zero_point_vec.at(i) = quant.zero_point(i);
545
546 auto quant_max = _flatbuffer_builder->CreateVector(quant_max_vec);
547 auto quant_min = _flatbuffer_builder->CreateVector(quant_min_vec);
548 auto quant_scale = _flatbuffer_builder->CreateVector(quant_scale_vec);
549 auto quant_zero_point = _flatbuffer_builder->CreateVector(quant_zero_point_vec);
550
551 // Create QuantizationParameters
552 tflite::QuantizationParametersBuilder quant_builder{*_flatbuffer_builder};
553 quant_builder.add_max(quant_max);
554 quant_builder.add_min(quant_min);
555 quant_builder.add_scale(quant_scale);
556 quant_builder.add_zero_point(quant_zero_point);
557 quant_builder.add_quantized_dimension(quant.quantized_dimension());
558
559 // Update QuantizationParameters Index
560 quant_index = quant_builder.Finish();
561}
562
563void ModelChef::operand_sparsity(const tflchef::Operand &operand, SparsityParams_t &sparsity_index)
564{
565 const auto &sparsity = operand.sparsity();
566
567 // Create traversal order
568 std::vector<int> traversal_order_vec{sparsity.traversal_order().dim().begin(),
569 sparsity.traversal_order().dim().end()};
570 auto traversal_order = _flatbuffer_builder->CreateVector(traversal_order_vec);
571
572 // Create block map
573 std::vector<int> block_map_vec{sparsity.block_map().dim().begin(),
574 sparsity.block_map().dim().end()};
575 auto block_map = _flatbuffer_builder->CreateVector(block_map_vec);
576
577 // Create dimension metadata
578 std::vector<flatbuffers::Offset<tflite::DimensionMetadata>> dim_metadata_vec;
579 auto recipe_dim_metadata = sparsity.dim_metadata();
580 for (const auto &dm : recipe_dim_metadata)
581 {
582 // Create array segments
583 auto tflite_array_segments =
584 as_tflite_sparse_index_vec(*_flatbuffer_builder, dm.array_segments());
585
586 // Create array indices
587 auto tflite_array_indices =
588 as_tflite_sparse_index_vec(*_flatbuffer_builder, dm.array_indices());
589
590 auto tflite_dim_metadata_builder = tflite::DimensionMetadataBuilder{*_flatbuffer_builder};
591 tflite_dim_metadata_builder.add_format(as_tflite_dimensiontype(dm.format()));
592 tflite_dim_metadata_builder.add_dense_size(dm.dense_size());
593 tflite_dim_metadata_builder.add_array_segments(tflite_array_segments);
594 tflite_dim_metadata_builder.add_array_segments_type(
595 as_tflite_sparse_idx_vec_type(dm.array_segments().type()));
596 tflite_dim_metadata_builder.add_array_indices(tflite_array_indices);
597 tflite_dim_metadata_builder.add_array_indices_type(
598 as_tflite_sparse_idx_vec_type(dm.array_indices().type()));
599 auto tflite_dim_metadata = tflite_dim_metadata_builder.Finish();
600 dim_metadata_vec.emplace_back(tflite_dim_metadata);
601 }
602 auto dim_metadata = _flatbuffer_builder->CreateVector(dim_metadata_vec);
603
604 sparsity_index = tflite::CreateSparsityParameters(*_flatbuffer_builder, traversal_order,
605 block_map, dim_metadata);
606}
607
608template <typename T> void ModelChef::cook_operands(const T &graph)
609{
610 int32_t buffer_start = _buffer_vec.size();
611 int32_t buffer_index = 0;
612
613 // Create buffer(s) 1~n(I) for input(s)
614 const auto size_input = graph.input_size();
615 for (int ci = 0; ci < size_input; ++ci)
616 {
617 tflite::BufferBuilder buffer_builder{*_flatbuffer_builder};
618 _buffer_vec.emplace_back(buffer_builder.Finish());
619 }
620 // Create buffer(s) n(I)+1~n(I)+n(O) for output(s)
621 const auto size_output = graph.output_size();
622 for (int co = 0; co < size_output; ++co)
623 {
624 tflite::BufferBuilder buffer_builder{*_flatbuffer_builder};
625 _buffer_vec.emplace_back(buffer_builder.Finish());
626 }
627
628 auto input_names = as_dataset(graph.input()).vectorize();
629 auto output_names = as_dataset(graph.output()).vectorize();
630
631 for (const auto &operand : graph.operand())
632 {
633 assert(operand.has_name());
634 assert(operand.has_type());
635
636 SparsityParams_t sparsity_index;
637
638 flatbuffers::Offset<flatbuffers::Vector<int32_t>> shape;
640 if (operand.has_shape())
641 {
642 dims = as_dims(operand.shape());
643 shape = _flatbuffer_builder->CreateVector(dims);
644 }
645
646 auto name = _flatbuffer_builder->CreateString(operand.name());
647
648 buffer_index = 0;
649
650 // Create Buffer if filler is specified
651 if (operand.has_filler())
652 {
653 // prohibit constant as graph input/output
654 for (auto it = input_names.begin(); it != input_names.end(); ++it)
655 {
656 if (*it == operand.name())
657 throw std::runtime_error{"Constant '" + *it + "' cannot be graph I/O"};
658 }
659 for (auto it = output_names.begin(); it != output_names.end(); ++it)
660 {
661 if (*it == operand.name())
662 throw std::runtime_error{"Constant '" + *it + "' cannot be graph I/O"};
663 }
664
665 const auto &filler = operand.filler();
666
667 assert(filler.has_tag());
668
669 auto args = ranged_arguments(filler.arg().begin(), filler.arg().end());
670 auto chef = data_chef_registry(operand.type()).lookup(filler.tag()).create(args);
671
672 assert(chef != nullptr);
673
674 // Create Data
675 int32_t count = (element_count(dims) > 0) ? element_count(dims) : filler.arg_size();
676 auto data_vec = chef->generate(count);
677
678 if (operand.has_make_sparse() && operand.make_sparse())
679 {
680 buffer_sparse(buffer_index, operand, dims, data_vec, sparsity_index);
681 }
682 else
683 {
684 buffer_dense(buffer_index, operand, count, data_vec);
685 }
686 }
687 else
688 {
689 buffer_empty(buffer_index, buffer_start, size_input, operand, input_names, output_names);
690 }
691 assert(buffer_index != 0);
692
693 QuantParams_t quant_index;
694
695 // Create QuantizationParameters if quant is specified
696 if (operand.has_quant())
697 {
698 operand_quant(operand, quant_index);
699 }
700
701 if (operand.has_sparsity())
702 {
703 operand_sparsity(operand, sparsity_index);
704 }
705
706 flatbuffers::Offset<flatbuffers::Vector<int32_t>> shape_signature;
707 if (operand.has_shape_signature())
708 {
709 auto signature = as_dims(operand.shape_signature());
710 shape_signature = _flatbuffer_builder->CreateVector(signature);
711 }
712
713 // Create Tensor
714 tflite::TensorBuilder tensor_builder{*_flatbuffer_builder};
715
716 tensor_builder.add_shape(shape);
717 tensor_builder.add_type(as_tflite_tensortype(operand.type()));
718 tensor_builder.add_buffer(buffer_index);
719 tensor_builder.add_name(name);
720 tensor_builder.add_is_variable(operand.is_variable());
721 if (operand.has_quant())
722 tensor_builder.add_quantization(quant_index);
723 tensor_builder.add_sparsity(sparsity_index);
724 if (operand.has_shape_signature())
725 tensor_builder.add_shape_signature(shape_signature);
726
727 // Append!
728 _tensor_vec.emplace_back(tensor_builder.Finish());
729 }
730}
731
732template <typename T>
733void ModelChef::prepare_tensor_symbols(const T &graph, SymboleTable_t &symbol_table)
734{
735 LOGGER(l);
736
737 for (const auto &operand : graph.operand())
738 {
739 // Update Tensor Name -> Tensor Index Map
740 int32_t tensor_index = symbol_table.size();
741 const auto &tensor_name = operand.name();
742
743 INFO(l) << "Symbol [" << tensor_name << "] = Tensor " << tensor_index << std::endl;
744
745 symbol_table[tensor_name] = tensor_index;
746 }
747}
748
749template <typename T> void ModelChef::cook_operations(const T &graph, SymboleTable_t &symbol_table)
750{
751 auto lookup = [&](const std::string &name) {
752 if (symbol_table.find(name) != symbol_table.end())
753 return symbol_table.at(name);
754 else if (name == "")
755 return -1; // -1 in TFLite means that optional input tensor is empty.
756 else
757 {
758 std::string msg = "tflchef : input not found in " + _graph_name + " graph";
759 throw std::runtime_error(msg.c_str());
760 }
761 };
762
763 // Create Operator
764 for (const auto &operation : graph.operation())
765 {
766 assert(operation.has_type());
767
768 std::string op_type = operation.type();
769 if (not operation.custom_code().empty())
770 op_type = operation.custom_code();
771
772 auto op_chef = op_chef_registry().lookup(op_type).create(&operation);
773
774 // Create 'inputs'
775 std::vector<int32_t> input_vec = as_dataset(operation.input()).map(lookup).vectorize();
776 auto inputs = _flatbuffer_builder->CreateVector(input_vec);
777
778 // Create 'outputs'
779 std::vector<int32_t> output_vec = as_dataset(operation.output()).map(lookup).vectorize();
780 auto outputs = _flatbuffer_builder->CreateVector(output_vec);
781
782 // Create Option
783 auto options = op_chef->value(*_flatbuffer_builder);
784
785 // Create Custom option
786 auto circle_custom_options = op_chef->custom_value(*_flatbuffer_builder);
787
788 // Create Operator
789 tflite::OperatorBuilder op_builder{*_flatbuffer_builder};
790
791 // Note that opcode_index is an index into the operator_codes vector.
792 // operator_codes consists of buildtin_code and custom_code, which is inserted sequentially.
793 uint32_t opcode_index = 0;
794 auto op_it = _builtin_code_map.find(op_chef->code());
795 // builtin operator
796 if (op_it != _builtin_code_map.end())
797 {
798 opcode_index = std::distance(_builtin_code_map.begin(), op_it);
799 }
800 // custom operator
801 else
802 {
803 assert(not operation.custom_code().empty());
804 const auto &custom_code = operation.custom_code();
805 auto op_it = std::find(_custom_code_vec.begin(), _custom_code_vec.end(), custom_code);
806 assert(op_it != _custom_code_vec.end());
807 opcode_index = _builtin_code_map.size();
808 opcode_index += std::distance(_custom_code_vec.begin(), op_it);
809 }
810
811 op_builder.add_opcode_index(opcode_index);
812 op_builder.add_inputs(inputs);
813 op_builder.add_outputs(outputs);
814 op_builder.add_builtin_options_type(op_chef->type());
815 op_builder.add_builtin_options(options);
816 op_builder.add_custom_options(circle_custom_options);
817 op_builder.add_custom_options_format(tflite::CustomOptionsFormat_FLEXBUFFERS);
818 // Append Operator
819 _operator_vec.emplace_back(op_builder.Finish());
820 }
821}
822
823template <typename T> void ModelChef::cook_graph(const T &graph, SymboleTable_t &symbol_table)
824{
825 LOGGER(l);
826
827 assert(symbol_table.empty()); // FIX_CALLER_UNLESS
828 assert(_tensor_vec.empty()); // FIX_CALLER_UNLESS
829 assert(_operator_vec.empty()); // FIX_CALLER_UNLESS
830
831 // default name for graph
832 if (graph.has_name())
833 _graph_name = graph.name();
834
835 auto lookup = [&](const std::string &name) {
836 if (symbol_table.find(name) != symbol_table.end())
837 return symbol_table.at(name);
838 else if (name == "")
839 return -1; // -1 in TFLite means that optional input tensor is empty.
840 else
841 {
842 std::string msg = "tflchef : input not found in " + _graph_name + " graph";
843 throw std::runtime_error(msg.c_str());
844 }
845 };
846
847 cook_operands(graph);
848
849 prepare_tensor_symbols(graph, symbol_table);
850
851 cook_operations(graph, symbol_table);
852
853 // Create network input/output vector
854 std::vector<int32_t> input_vec = as_dataset(graph.input()).map(lookup).vectorize();
855 std::vector<int32_t> output_vec = as_dataset(graph.output()).map(lookup).vectorize();
856
857 // Create "SubGraph" arguments
858 auto tensors = _flatbuffer_builder->CreateVector(_tensor_vec);
859 auto inputs = _flatbuffer_builder->CreateVector(input_vec);
860 auto outputs = _flatbuffer_builder->CreateVector(output_vec);
861 auto operators = _flatbuffer_builder->CreateVector(_operator_vec);
862 auto name = _flatbuffer_builder->CreateString(_graph_name);
863
864 tflite::SubGraphBuilder subgraph_builder{*_flatbuffer_builder};
865
866 subgraph_builder.add_tensors(tensors);
867 subgraph_builder.add_inputs(inputs);
868 subgraph_builder.add_outputs(outputs);
869 subgraph_builder.add_operators(operators);
870 subgraph_builder.add_name(name);
871
872 _subgraph_vec.emplace_back(subgraph_builder.Finish());
873}
874
875void ModelChef::gather_operator_codes(const ::tflchef::ModelRecipe &model_recipe)
876{
877 // Create OperatorCode with Builtin Operator
878 _builtin_code_map = gather_builtincode_map(model_recipe);
879 for (auto const &opcode : _builtin_code_map)
880 {
881 tflite::OperatorCodeBuilder code_builder{*_flatbuffer_builder};
882 // 127 is BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES
883 // This is the way to handle deprecated builtin code
884 // See
885 // https://github.com/tensorflow/tensorflow/blob/a0afe8f9218be5eb9ed5dffc2dff652996da8c28/tensorflow/lite/schema/schema.fbs#L1061-L1077
886 if (opcode.first < 127)
887 {
888 code_builder.add_deprecated_builtin_code(opcode.first);
889 }
890 else
891 {
892 code_builder.add_deprecated_builtin_code(
893 ::tflite::BuiltinOperator_PLACEHOLDER_FOR_GREATER_OP_CODES);
894 }
895 code_builder.add_version(opcode.second);
896 code_builder.add_builtin_code(opcode.first);
897 auto code = code_builder.Finish();
898 // Update OperatorCode vector
899 _code_vec.emplace_back(code);
900 }
901
902 // Create OperatorCode with Custom Operator
903 {
904 std::set<std::string> custom_code_set = gather_customcode_set(model_recipe);
905 std::vector<std::string> custom_code_vec{custom_code_set.begin(), custom_code_set.end()};
906 _custom_code_vec = custom_code_vec;
907 }
908
909 for (const auto &opcode : _custom_code_vec)
910 {
911 auto custom_code = _flatbuffer_builder->CreateString(opcode);
912 tflite::OperatorCodeBuilder code_builder{*_flatbuffer_builder};
913 code_builder.add_deprecated_builtin_code(tflite::BuiltinOperator_CUSTOM);
914 code_builder.add_custom_code(custom_code);
915 code_builder.add_builtin_code(tflite::BuiltinOperator_CUSTOM);
916 auto code = code_builder.Finish();
917 // Update OperatorCode vector
918 _code_vec.emplace_back(code);
919 }
920}
921
922void ModelChef::prepare_initial_buffer(void)
923{
924 // Create an Empty Buffer
925 //
926 // Buffer 0 SHOULD be an empty buffer in TensorFlow Lite model file
927 // (Please refer to the comment for Tensor.buffer field in schema)
928 tflite::BufferBuilder buffer_builder{*_flatbuffer_builder};
929 _buffer_vec.emplace_back(buffer_builder.Finish());
930}
931
932void ModelChef::gather_signature_defs(const ::tflchef::ModelRecipe &model_recipe)
933{
934 for (int s = 0; s < model_recipe.signature_def_size(); ++s)
935 {
936 // load from recipe
937 const auto &rec_signature_def = model_recipe.signature_def(s);
938
939 std::vector<flatbuffers::Offset<::tflite::TensorMap>> tensormap_inputs;
940 std::vector<flatbuffers::Offset<::tflite::TensorMap>> tensormap_outputs;
941
942 // which subgraph index to cook
943 auto subgraph_index = 0;
944 if (rec_signature_def.has_subgraph_index())
945 {
946 subgraph_index = rec_signature_def.subgraph_index();
947 }
948 assert(subgraph_index < _symbol_tables.size());
949 auto &symbol_table = _symbol_tables[subgraph_index];
950
951 // cook for inputs
952 for (int si = 0; si < rec_signature_def.inputs_size(); ++si)
953 {
954 // recipe for input TensorMap
955 const auto &rec_tm_input = rec_signature_def.inputs(si);
956 auto name = _flatbuffer_builder->CreateString(rec_tm_input.name());
957 uint32_t tensor_index = 0;
958 // either tensor or tensor_index should exist
959 assert(rec_tm_input.has_tensor() || rec_tm_input.has_tensor_index());
960 if (rec_tm_input.has_tensor())
961 {
962 // we can get tensor_index from symbol_table
963 const auto &tensor = rec_tm_input.tensor();
964 tensor_index = symbol_table[tensor];
965 }
966 else
967 {
968 // or we can use tensor_index itself
969 tensor_index = rec_tm_input.tensor_index();
970 }
971
972 ::tflite::TensorMapBuilder tensormap_builder{*_flatbuffer_builder};
973 tensormap_builder.add_name(name);
974 tensormap_builder.add_tensor_index(tensor_index);
975 tensormap_inputs.push_back(tensormap_builder.Finish());
976 }
977 // cook for outputs, same as inputs
978 for (int so = 0; so < rec_signature_def.outputs_size(); ++so)
979 {
980 const auto &rec_tm_output = rec_signature_def.outputs(so);
981 auto name = _flatbuffer_builder->CreateString(rec_tm_output.name());
982 uint32_t tensor_index = 0;
983 assert(rec_tm_output.has_tensor() || rec_tm_output.has_tensor_index());
984 if (rec_tm_output.has_tensor())
985 {
986 const auto &tensor = rec_tm_output.tensor();
987 tensor_index = symbol_table[tensor];
988 }
989 else
990 {
991 tensor_index = rec_tm_output.tensor_index();
992 }
993
994 ::tflite::TensorMapBuilder tensormap_builder{*_flatbuffer_builder};
995 tensormap_builder.add_name(name);
996 tensormap_builder.add_tensor_index(tensor_index);
997 tensormap_outputs.push_back(tensormap_builder.Finish());
998 }
999
1000 auto inputs = _flatbuffer_builder->CreateVector(tensormap_inputs);
1001 auto outputs = _flatbuffer_builder->CreateVector(tensormap_outputs);
1002 auto signature_key = _flatbuffer_builder->CreateString(rec_signature_def.signature_key());
1003 // TODO add validation for signature_key
1004
1005 ::tflite::SignatureDefBuilder signature_def_builder{*_flatbuffer_builder};
1006 signature_def_builder.add_inputs(inputs);
1007 signature_def_builder.add_outputs(outputs);
1008 signature_def_builder.add_signature_key(signature_key);
1009 signature_def_builder.add_subgraph_index(rec_signature_def.subgraph_index());
1010
1011 _signdef_vec.emplace_back(signature_def_builder.Finish());
1012 }
1013}
1014
1015bool ModelChef::finalize_ext_buffer(void)
1016{
1017 // NOTE modification of std::string object in the middle may reallocate it.
1018 // we will use std::string::reserve() to prevent this.
1019
1020 auto align16 = [](size_t &v) {
1021 while (v % 16 != 0)
1022 v++;
1023 };
1024
1025 // get total memory for flatbuffer + all buffer_data
1026 size_t result_size = _flatbuffer_builder->GetSize();
1027 align16(result_size);
1028 for (auto &it : _buffer_data_map)
1029 {
1030 std::vector<uint8_t> &buffer_data = it.second;
1031 result_size += buffer_data.size();
1032 align16(result_size);
1033 }
1034 align16(result_size);
1035 result_size += 16; // additional for safety
1036
1037 std::string result;
1038 auto *buff_ptr = reinterpret_cast<const char *>(_flatbuffer_builder->GetBufferPointer());
1039
1040 auto padalign16 = [](std::string &str) {
1041 while (str.size() % 16 != 0)
1042 str += '\0';
1043 };
1044
1045 result.reserve(result_size);
1046 result.append(buff_ptr, _flatbuffer_builder->GetSize());
1047
1048 auto mutable_model = tflite::GetMutableModel(result.data());
1049 auto mutable_buffers = mutable_model->mutable_buffers();
1050 bool ret = true;
1051
1052 padalign16(result);
1053 for (auto &it : _buffer_data_map)
1054 {
1055 int32_t buffer_index = it.first;
1056 std::vector<uint8_t> &buffer_data = it.second;
1057 uint64_t offset = result.size();
1058 uint64_t size = buffer_data.size();
1059
1060 tflite::Buffer *mutable_buffer = mutable_buffers->GetMutableObject(buffer_index);
1061 ret &= mutable_buffer->mutate_offset(offset);
1062 ret &= mutable_buffer->mutate_size(size);
1063
1064 result.append(buffer_data.begin(), buffer_data.end());
1065 padalign16(result);
1066 }
1067 padalign16(result);
1068
1069 // use final result
1070 _ext_data = result;
1071
1072 return ret;
1073}
1074
1075void ModelChef::cook(const ::tflchef::ModelRecipe &model_recipe)
1076{
1077 // use Custom/Buffer offset
1078 _ext_offset = model_recipe.has_ext_offset() ? model_recipe.ext_offset() : false;
1079
1080 prepare_initial_buffer();
1081
1082 gather_operator_codes(model_recipe);
1083
1084 //
1085 // Create Main graph
1086 //
1087
1088 _graph_name = "main";
1089 // Tensor Name -> Tensor ID mapping (per Graph)
1090 SymboleTable_t symbol_table;
1091 cook_graph<::tflchef::ModelRecipe>(model_recipe, symbol_table);
1092 _symbol_tables.push_back(symbol_table);
1093
1094 //
1095 // Create subgraphs if exist
1096 //
1097 for (int g = 0; g < model_recipe.graph_size(); ++g)
1098 {
1099 const auto &graph = model_recipe.graph(g);
1100
1101 std::ostringstream stringStream;
1102 stringStream << "sub_" << (g + 1);
1103
1104 _graph_name = stringStream.str();
1105
1106 symbol_table.clear();
1107 _tensor_vec.clear();
1108 _operator_vec.clear();
1109 cook_graph<::tflchef::Graph>(graph, symbol_table);
1110 _symbol_tables.push_back(symbol_table);
1111 }
1112
1113 gather_signature_defs(model_recipe);
1114
1115 // Create "Model" arguments
1116 auto buffers = _flatbuffer_builder->CreateVector(_buffer_vec);
1117 auto signdefs = _flatbuffer_builder->CreateVector(_signdef_vec);
1118 auto operator_codes = _flatbuffer_builder->CreateVector(_code_vec);
1119 auto subgraphs = _flatbuffer_builder->CreateVector(_subgraph_vec);
1120 auto description = _flatbuffer_builder->CreateString("Generated by tflchef");
1121
1122 // Create "Model"
1123 tflite::ModelBuilder model_builder{*_flatbuffer_builder};
1124
1125 model_builder.add_version(3);
1126 model_builder.add_operator_codes(operator_codes);
1127 model_builder.add_signature_defs(signdefs);
1128 model_builder.add_subgraphs(subgraphs);
1129 model_builder.add_description(description);
1130 model_builder.add_buffers(buffers);
1131
1132 auto model = model_builder.Finish();
1133
1134 // Finalize
1135 ::tflite::FinishModelBuffer(*_flatbuffer_builder, model);
1136
1137 if (_ext_offset)
1138 finalize_ext_buffer();
1139}
1140
1141const char *ModelChef::get_buffer_pointer(void) const
1142{
1143 if (_ext_offset)
1144 return _ext_data.data();
1145 return reinterpret_cast<const char *>(_flatbuffer_builder->GetBufferPointer());
1146}
1147
1148size_t ModelChef::get_size(void) const
1149{
1150 if (_ext_offset)
1151 return _ext_data.size();
1152 return _flatbuffer_builder->GetSize();
1153}
1154
1155} // namespace
1156
1157namespace
1158{
1159
1160class GeneratedModelImpl final : public tflchef::GeneratedModel::Impl
1161{
1162public:
1163 GeneratedModelImpl()
1164 {
1165 // DO NOTHING
1166 }
1167
1168public:
1169 const char *base(void) const override { return _mc.get_buffer_pointer(); }
1170
1171 size_t size(void) const override { return _mc.get_size(); }
1172
1173public:
1174 ModelChef &model_chef(void) { return _mc; }
1175
1176private:
1177 ModelChef _mc;
1178};
1179
1180} // namespace
1181
1182namespace tflchef
1183{
1184
1188GeneratedModel cook(const ::tflchef::ModelRecipe &model_recipe)
1189{
1190// Initialize Op Chef Registry
1191#define OP_CHEF(NAME, FACTORY_CLASS) \
1192 op_chef_registry().add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS()));
1193#include "OpChef.def"
1194#undef OP_CHEF
1195
1196// Initialize Data Chef Registry
1197#define DATA_CHEF(TYPE, NAME, FACTORY_CLASS) \
1198 data_chef_registry(::tflchef::TYPE) \
1199 .add(#NAME, std::unique_ptr<FACTORY_CLASS>(new FACTORY_CLASS()));
1200#include "DataChef.def"
1201#undef DATA_CHEF
1202
1203 std::unique_ptr<GeneratedModelImpl> gen_model(new GeneratedModelImpl());
1204
1205 ModelChef &mc = gen_model->model_chef();
1206
1207 mc.init();
1208 mc.cook(model_recipe);
1209
1210 // Return "GenerateModel"
1211 return GeneratedModel{std::move(gen_model)};
1212}
1213
1214} // namespace tflchef
int32_t type
OpBuilder op_builder(coco::Module *m)
Definition IRBuilder.h:144
#define LOGGER(name)
Definition Log.h:65
#define INFO(name)
Definition Log.h:68
OffsetT< VectorT< T > > CreateVector(const T *v, size_t len)
Serialize an array into a FlatBuffer vector.
__global uchar * offset(const Image *img, int x, int y)
Definition helpers.h:540
GeneratedModel cook(const ModelRecipe &model_recipe)
Code * code(const SessionID &sess)
Definition Session.cpp:54
args
Definition infer.py:21
result
Definition infer.py:103
arr
Definition infer.py:92
str
Definition infer.py:18
std::vector< int > dims(const std::string &src)
Definition Utils.h:35
flatbuffers::Offset< flatbuffers::Vector< uint8_t > > circle_custom_options(flatbuffers::FlatBufferBuilder &fb, const luci::CircleNode *node)
const char * tensor_name(const circle::Tensor *tensor)
name
Definition setup.py:158
description
Definition setup.py:160
This file provides string <-> number cast helpers.
Definition Arguments.h:24
Dataset< T > as_dataset(const ::google::protobuf::RepeatedPtrField< T > &field)
Definition Dataset.h:72
RangedArguments< InputIt > ranged_arguments(InputIt beg, InputIt end)
DimsI32_t as_dims(const SHAPETYPE &shape)
Definition Dims.h:32
std::vector< uint8_t > Data
Definition DataChef.h:29
int32_t element_count(const DimsI32_t &dims)
Definition Dims.h:44
Dims< int32_t > DimsI32_t
Definition Dims.h:30
GeneratedModel cook(const ModelRecipe &model_recipe)
int32_t size[5]
Definition Slice.cpp:35
virtual size_t size(void) const =0
virtual const char * base(void) const =0
tflite::DimensionType as_tflite_dimensiontype(const tflchef::DimensionType &value)
Definition Convert.cpp:106
tflite::SparseIndexVector as_tflite_sparse_idx_vec_type(const tflchef::SparseIndexVecType &value)
Definition Convert.cpp:121
flatbuffers::Offset< void > as_tflite_sparse_index_vec(flatbuffers::FlatBufferBuilder &fb, const ::tflchef::TensorSparsity_IndexVec &value)
Definition Convert.cpp:141
tflite::TensorType as_tflite_tensortype(const tflchef::TensorType &value)
Definition Convert.cpp:60