blob: dd1f5773af42d5755fe411ddcce510c542b41566 [file] [log] [blame]
telsoa01c577f2c2018-08-31 09:22:23 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa01c577f2c2018-08-31 09:22:23 +01004//
5#include "TfLiteParser.hpp"
6
7#include <armnn/ArmNN.hpp>
8#include <armnn/Exceptions.hpp>
9#include <armnn/TypesUtils.hpp>
10#include <boost/filesystem.hpp>
11
12// armnnUtils:
13#include <Permute.hpp>
14#include <VerificationHelpers.hpp>
15
16// The generated code based on the Tf Lite schema:
17#include <schema_generated.h>
18
19#include <boost/core/ignore_unused.hpp>
20#include <boost/assert.hpp>
21#include <boost/format.hpp>
22#include <boost/log/trivial.hpp>
23
24#include <fstream>
25#include <algorithm>
26#include <limits>
27
28using namespace armnn;
29using armnn::CheckLocation;
30namespace armnnTfLiteParser
31{
32namespace
33{
34const PermutationVector NHWCToArmNN = { 0, 2, 3, 1 };
35const PermutationVector ArmNNToNHWC = { 0, 3, 1, 2 };
36
37const uint32_t VIRTUAL_OPERATOR_ID = std::numeric_limits<uint32_t>::max();
38
39void CheckSubgraph(const TfLiteParser::ModelPtr & model,
40 size_t subgraphIndex,
41 const CheckLocation & location)
42{
43 if (model.get() == nullptr)
44 {
45 throw ParseException(
46 boost::str(
47 boost::format("%1% was called with invalid (null) model. "
48 "Possible reason is that the model is not yet loaded and Unpack(ed). "
49 "subgraph:%2% at %3%") %
50 location.m_Function %
51 subgraphIndex %
52 location.FileLine()));
53 }
54 else if (subgraphIndex >= model->subgraphs.size())
55 {
56 throw ParseException(
57 boost::str(
58 boost::format("%1% was called with an invalid subgraph index. "
59 "subgraph:%2% at %3%") %
60 location.m_Function %
61 subgraphIndex %
62 location.FileLine()));
63 }
64}
65
66#define CHECK_SUBGRAPH(MODEL, SUBGRAPH_INDEX) \
67 CheckSubgraph(MODEL, SUBGRAPH_INDEX, CHECK_LOCATION())
68
69void CheckModel(const TfLiteParser::ModelPtr & model,
70 size_t subgraphIndex,
71 size_t operatorIndex,
72 const CheckLocation & location)
73{
74 if (model.get() == nullptr)
75 {
76 throw ParseException(
77 boost::str(
78 boost::format("%1% was called with invalid (null) model. "
79 "Possible reason is that the model is not yet loaded and Unpack(ed). "
80 "subgraph:%2% operator:%3% at %4%") %
81 location.m_Function %
82 subgraphIndex %
83 operatorIndex %
84 location.FileLine()));
85 }
86 else if (subgraphIndex >= model->subgraphs.size())
87 {
88 throw ParseException(
89 boost::str(
90 boost::format("%1% was called with an invalid subgraph index. "
91 "subgraph:%2% operator:%3% at %4%") %
92 location.m_Function %
93 subgraphIndex %
94 operatorIndex %
95 location.FileLine()));
96 }
97 else if (operatorIndex >= model->subgraphs[subgraphIndex]->operators.size() &&
98 operatorIndex != VIRTUAL_OPERATOR_ID)
99 {
100 throw ParseException(
101 boost::str(
102 boost::format("%1% was called with an invalid operator index. "
103 "subgraph:%2% operator:%3% at %4%") %
104 location.m_Function %
105 subgraphIndex %
106 operatorIndex %
107 location.FileLine()));
108 }
109}
110
111#define CHECK_MODEL(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX) \
112 CheckModel(MODEL, SUBGRAPH_INDEX, OPERATOR_INDEX, CHECK_LOCATION())
113
114void CheckTensor(const TfLiteParser::ModelPtr & model,
115 size_t subgraphIndex,
116 size_t tensorIndex,
117 const CheckLocation & location)
118{
119 // not checking model, because I assume CHECK_MODEL already run
120 // and checked that. An assert would do.
121 BOOST_ASSERT_MSG(model.get() != nullptr, "Expecting a valid model in this function");
122
123 // also subgraph index should be checked by CHECK_MODEL so
124 // I only add an assert here
125 BOOST_ASSERT_MSG(subgraphIndex < model->subgraphs.size(), "Expecting a valid subgraph index");
126
127 // the tensor index is the only one to check here
128 if (tensorIndex >= model->subgraphs[subgraphIndex]->tensors.size())
129 {
130 throw ParseException(
131 boost::str(
132 boost::format("%1% was called with an invalid tensor index. "
133 "subgraph:%2% tensor:%3% at %4%") %
134 location.m_Function %
135 subgraphIndex %
136 tensorIndex %
137 location.FileLine()));
138 }
139}
140
141#define CHECK_TENSOR(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX) \
142 CheckTensor(MODEL, SUBGRAPH_INDEX, TENSOR_INDEX, CHECK_LOCATION())
143
144void CheckTensorPtr(TfLiteParser::TensorRawPtr rawPtr,
145 const CheckLocation & location)
146{
147 if (rawPtr == nullptr)
148 {
149 throw ParseException(
150 boost::str(
151 boost::format("%1% was called with a null tensor pointer. "
152 "at %2%") %
153 location.m_Function %
154 location.FileLine()));
155
156 }
157}
158
159#define CHECK_TENSOR_PTR(TENSOR_PTR) \
160 CheckTensorPtr(TENSOR_PTR, CHECK_LOCATION())
161
162void CheckBuffer(const TfLiteParser::ModelPtr & model,
163 size_t bufferIndex,
164 const CheckLocation & location)
165{
166 if (model.get() == nullptr)
167 {
168 throw ParseException(
169 boost::str(
170 boost::format("%1% was called with invalid (null) model. "
171 "Possible reason is that the model is not yet loaded and Unpack(ed). "
172 "buffer:%2% at %3%") %
173 location.m_Function %
174 bufferIndex %
175 location.FileLine()));
176 }
177 else if (bufferIndex >= model->buffers.size())
178 {
179 throw ParseException(
180 boost::str(
181 boost::format("%1% was called with an invalid buffer index. "
182 "buffer index:%2% at %3%") %
183 location.m_Function %
184 bufferIndex %
185 location.FileLine()));
186 }
187 else if (model->buffers[bufferIndex].get() == nullptr)
188 {
189 throw ParseException(
190 boost::str(
191 boost::format("The buffer #%1% is null. %3%") %
192 bufferIndex %
193 location.AsString()));
194 }
195}
196
197#define CHECK_BUFFER(MODEL, BUFFER_INDEX) \
198 CheckBuffer(MODEL, BUFFER_INDEX, CHECK_LOCATION())
199
200void CheckBufferSize(TfLiteParser::BufferRawPtr bufferPtr,
201 const armnn::TensorInfo & tensorInfo,
202 uint32_t bufferId,
203 const CheckLocation & location)
204{
205 if (bufferPtr == nullptr)
206 {
207 throw ParseException(
208 boost::str(
209 boost::format("BufferPtr is null for buffer:%1%. %2%") %
210 bufferId %
211 location.AsString()));
212 }
213 else if(tensorInfo.GetNumElements() > bufferPtr->data.size() ||
214 tensorInfo.GetNumBytes() > bufferPtr->data.size())
215 {
216 std::stringstream ss;
217 ss << "Buffer #" << bufferId << " has " << bufferPtr->data.size() << " bytes. "
218 << "For tensor: " << tensorInfo.GetShape()
219 << " expecting: " << tensorInfo.GetNumBytes() << " bytes and "
220 << tensorInfo.GetNumElements() << " elements. " << location.AsString();
221 throw ParseException(ss.str());
222 }
223}
224
225#define CHECK_BUFFER_SIZE(BUFFER_PTR, TENSOR_INFO, BUFFER_ID) \
226 CheckBufferSize(BUFFER_PTR, TENSOR_INFO, BUFFER_ID, CHECK_LOCATION())
227
228bool IsActivationSupported(tflite::ActivationFunctionType activationType)
229{
230 switch(activationType)
231 {
232 case tflite::ActivationFunctionType_NONE:
233 case tflite::ActivationFunctionType_RELU:
234 case tflite::ActivationFunctionType_RELU6:
235 case tflite::ActivationFunctionType_TANH:
236 {
237 return true;
238 }
239 default:
240 {
241 return false;
242 }
243 }
244}
245
246#define CHECK_SUPPORTED_FUSED_ACTIVATION(OPTION, SUBGRAPH_INDEX, OPERATOR_INDEX) \
247 do { \
248 if (IsActivationSupported(OPTION->fused_activation_function) == false) \
249 { \
250 throw ParseException( \
251 boost::str( \
252 boost::format("TfLite parser doesn't suppport fused activation: " \
253 "%1%/%2% in %3% subgraph:%4% operator:%5% at %6%") % \
254 OPTION->fused_activation_function % \
255 tflite::EnumNameActivationFunctionType(\
256 OPTION->fused_activation_function) % \
257 __func__ % \
258 SUBGRAPH_INDEX % \
259 OPERATOR_INDEX % \
260 CHECK_LOCATION().FileLine())); \
261 } \
262 } while(false)
263
264
265std::vector<unsigned int> AsUnsignedVector(const std::vector<int32_t> & in)
266{
267 std::vector<unsigned int> result;
268 result.reserve(in.size());
269 for (auto & i : in)
270 {
271 result.push_back(CHECKED_NON_NEGATIVE(i));
272 }
273 return result;
274}
275
276void CalcPadding(uint32_t inputSize,
277 uint32_t filterSize,
278 uint32_t stride,
279 uint32_t& paddingFront,
280 uint32_t& paddingBack,
281 tflite::Padding padding)
282{
283 paddingFront = 0;
284 paddingBack = 0;
285 if (padding == tflite::Padding_SAME)
286 {
287 uint32_t outputSize = (inputSize + stride - 1) / stride;
288 uint32_t temp = (outputSize - 1) * stride + filterSize;
289 if (temp > inputSize)
290 {
291 paddingFront = (temp - inputSize) / 2;
292 paddingBack = (temp - inputSize) - paddingFront;
293 }
294 }
295}
296
297armnn::TensorInfo ToTensorInfo(TfLiteParser::TensorRawPtr tensorPtr)
298{
299 armnn::DataType type;
300 CHECK_TENSOR_PTR(tensorPtr);
301
302 switch (tensorPtr->type)
303 {
304 case tflite::TensorType_UINT8:
305 type = armnn::DataType::QuantisedAsymm8;
306 break;
307 case tflite::TensorType_FLOAT32:
308 type = armnn::DataType::Float32;
309 break;
310 case tflite::TensorType_INT32:
311 type = armnn::DataType::Signed32;
312 break;
313
314 default:
315 {
316 CheckLocation location = CHECK_LOCATION();
317 throw ParseException(
318 boost::str(
319 boost::format("Unsupported data type %1% = %2% for tensor: %3%. %4%") %
320 tensorPtr->type %
321 tflite::EnumNameTensorType(tensorPtr->type) %
322 tensorPtr->name %
323 location.AsString()));
324 }
325 }
326
327 float quantizationScale = 0.0f;
328 int32_t quantizationOffset = 0;
329
330 if (tensorPtr->quantization.get())
331 {
332 CHECK_VALID_SIZE(tensorPtr->quantization->scale.size(), 0, 1);
333 CHECK_VALID_SIZE(tensorPtr->quantization->zero_point.size(), 0, 1);
334
335 if (tensorPtr->quantization->scale.size() == 1)
336 {
337 quantizationScale = tensorPtr->quantization->scale[0];
338 }
339 if (tensorPtr->quantization->zero_point.size() == 1)
340 {
341 // NOTE: we lose precision here when converting from 64 bit to 32
342 // but this is what we support at the monent in ArmNN
343 quantizationOffset = static_cast<int32_t>(tensorPtr->quantization->zero_point[0]);
344 }
345 }
346
347 auto const & dimensions = AsUnsignedVector(tensorPtr->shape);
348
349 // two statements (on purpose) for easier debugging:
350 armnn::TensorInfo result(static_cast<unsigned int>(tensorPtr->shape.size()),
351 dimensions.data(),
352 type,
353 quantizationScale,
354 quantizationOffset);
355 return result;
356}
357
358template<typename T>
359std::pair<armnn::ConstTensor, std::unique_ptr<T[]>>
360CreateConstTensorImpl(TfLiteParser::BufferRawPtr bufferPtr,
361 TfLiteParser::TensorRawPtr tensorPtr,
362 armnn::TensorInfo & tensorInfo,
363 bool convertFromTfToArmnnFormat)
364{
365 BOOST_ASSERT_MSG(tensorPtr != nullptr, "tensorPtr is null");
366 BOOST_ASSERT_MSG(bufferPtr != nullptr,
367 boost::str(
368 boost::format("Buffer for buffer:%1% is null") % tensorPtr->buffer).c_str());
369
370 std::unique_ptr<T[]> data(new T[tensorInfo.GetNumElements()]);
371
372 if (convertFromTfToArmnnFormat)
373 {
374 tensorInfo = armnnUtils::Permuted(tensorInfo, NHWCToArmNN);
375 armnnUtils::Permute(tensorInfo.GetShape(),
376 NHWCToArmNN,
377 reinterpret_cast<const T *>(bufferPtr->data.data()),
378 data.get());
379 }
380 else
381 {
382 ::memcpy(data.get(), bufferPtr->data.data(), tensorInfo.GetNumBytes());
383 }
384 return std::make_pair(ConstTensor(tensorInfo, data.get()), std::move(data));
385}
386
387IConnectableLayer* SwizzleIn(INetwork& network,
388 IConnectableLayer* layer,
389 unsigned int inputSlotIndex,
390 const TensorInfo & inputInfo)
391{
392 BOOST_ASSERT(layer != nullptr);
393 // Add swizzle layer
394 std::stringstream name;
395 name << "swizzle_for-" << layer->GetName() << ":in" << inputSlotIndex;
396 IConnectableLayer* const swizzleLayer = network.AddPermuteLayer(NHWCToArmNN, name.str().c_str());
397 // Set swizzled output shape
398 const TensorInfo swizzleOutInfo = armnnUtils::Permuted(inputInfo, NHWCToArmNN);
399 swizzleLayer->GetOutputSlot(0).SetTensorInfo(swizzleOutInfo);
400 // Connect the swizzle layer to the actual layer
401 swizzleLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(inputSlotIndex));
402
403 return swizzleLayer;
404}
405
406IConnectableLayer* DeswizzleOut(INetwork& network,
407 IConnectableLayer* layer,
408 unsigned int outputSlotIndex,
409 const TensorInfo & outputInfo)
410{
411 BOOST_ASSERT(layer != nullptr);
412 // Add deswizzle layer
413 std::stringstream name;
414 name << "deswizzle_for-" << layer->GetName() << ":out" << outputSlotIndex;
415 IConnectableLayer* const deswizzleLayer = network.AddPermuteLayer(ArmNNToNHWC, name.str().c_str());
416 // Set deswizzled output shape
417 deswizzleLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
418 // Set original layer output shape
419 const TensorInfo deswizzleOutInfo = armnnUtils::Permuted(outputInfo, NHWCToArmNN);
420 layer->GetOutputSlot(outputSlotIndex).SetTensorInfo(deswizzleOutInfo);
421 // Connect the actual layer to the deswizzle layer
422 layer->GetOutputSlot(outputSlotIndex).Connect(deswizzleLayer->GetInputSlot(0));
423
424 return deswizzleLayer;
425}
426
427std::pair<IConnectableLayer*, IConnectableLayer*> SwizzleInDeswizzleOut(INetwork& network,
428 IConnectableLayer* layer,
429 unsigned int inputSlotIndex,
430 const TensorInfo & inputInfo,
431 unsigned int outputSlotIndex,
432 const TensorInfo & outputInfo)
433{
434 IConnectableLayer* const swizzleLayer = SwizzleIn(network, layer, inputSlotIndex, inputInfo);
435 IConnectableLayer* const deswizzleLayer = DeswizzleOut(network, layer, outputSlotIndex, outputInfo);
436 return std::make_pair(swizzleLayer, deswizzleLayer);
437}
438
439armnn::LayerBindingId GenerateLayerBindingId(size_t subgraphIndex, size_t tensorIndex)
440{
441 // generate the binding id by shifting the tensor id by 8 bit
442 // and add the subgraph id, which allows 256 subgraphs
443 return static_cast<armnn::LayerBindingId>((tensorIndex<<8)+subgraphIndex);
444}
445
446} // <anonymous>
447
448TfLiteParser::TfLiteParser()
449: m_Network(nullptr, nullptr)
450, m_ParserFunctions(tflite::BuiltinOperator_MAX+1, &TfLiteParser::ParseUnsupportedOperator)
451{
452 // register supported operators
453 m_ParserFunctions[tflite::BuiltinOperator_AVERAGE_POOL_2D] = &TfLiteParser::ParseAveragePool2D;
454 m_ParserFunctions[tflite::BuiltinOperator_CONV_2D] = &TfLiteParser::ParseConv2D;
455 m_ParserFunctions[tflite::BuiltinOperator_DEPTHWISE_CONV_2D] = &TfLiteParser::ParseDepthwiseConv2D;
456 m_ParserFunctions[tflite::BuiltinOperator_SOFTMAX] = &TfLiteParser::ParseSoftmax;
457 m_ParserFunctions[tflite::BuiltinOperator_SQUEEZE] = &TfLiteParser::ParseSqueeze;
Sadik Armagan58f39192018-09-17 14:14:39 +0100458 m_ParserFunctions[tflite::BuiltinOperator_RELU] = &TfLiteParser::ParseRelu;
459 m_ParserFunctions[tflite::BuiltinOperator_RELU6] = &TfLiteParser::ParseRelu6;
telsoa01c577f2c2018-08-31 09:22:23 +0100460}
461
462void TfLiteParser::ResetParser()
463{
464 m_Network = armnn::INetworkPtr(nullptr, nullptr);
465 m_Model = nullptr;
466 m_SubgraphConnections.clear();
467}
468
469INetworkPtr TfLiteParser::CreateNetworkFromBinaryFile(const char* graphFile)
470{
471 ResetParser();
472 m_Model = LoadModelFromFile(graphFile);
473 return CreateNetworkFromModel();
474}
475
476INetworkPtr TfLiteParser::CreateNetworkFromBinary(const std::vector<uint8_t> & binaryContent)
477{
478 ResetParser();
479 m_Model = LoadModelFromBinary(binaryContent.data(), binaryContent.size());
480 return CreateNetworkFromModel();
481}
482
483INetworkPtr TfLiteParser::CreateNetworkFromModel()
484{
485 m_Network = INetwork::Create();
486 BOOST_ASSERT(m_Model.get() != nullptr);
487
488 bool failedToCreate = false;
489 std::stringstream errors;
490
491 if (m_Model->subgraphs.size() != 1)
492 {
493 throw ParseException(
494 boost::str(
495 boost::format("Current TfLite parser only supports 1 subgraph. Current one has: %1% %2%") %
496 m_Model->subgraphs.size() %
497 CHECK_LOCATION().AsString()));
498 }
499
500 size_t subgraphIndex = 0;
501 for (SubGraphPtr const & subgraph : m_Model->subgraphs)
502 {
503 m_SubgraphConnections.emplace_back(subgraph->tensors.size());
504
505 size_t operatorIndex = 0;
506 for (OperatorPtr const & op : subgraph->operators)
507 {
508 try
509 {
510 if (op->custom_options.size() > 0)
511 {
512 throw ParseException(
513 boost::str(
514 boost::format("Custom options for op: %1% is not supported. "
515 "It has %2% bytes of custom options. %3%") %
516 op->opcode_index %
517 op->custom_options.size() %
518 CHECK_LOCATION().AsString()));
519 }
520
521 auto const & opCodePtr = m_Model->operator_codes[op->opcode_index];
522 auto builtinCode = opCodePtr->builtin_code;
523
524 if (builtinCode > tflite::BuiltinOperator_MAX)
525 {
526 throw ParseException(
527 boost::str(
528 boost::format("Operator code %1% is out of range 0-%2%. "
529 "subgraph:%3% operator idx:%4%. %5%") %
530 builtinCode %
531 tflite::BuiltinOperator_MAX %
532 subgraphIndex %
533 operatorIndex %
534 CHECK_LOCATION().AsString()));
535 }
536
537 // lookup and call the parser function
538 auto & parserFunction = m_ParserFunctions[builtinCode];
539 (this->*parserFunction)(subgraphIndex, operatorIndex);
540 }
541 catch (const ParseException& e)
542 {
543 failedToCreate = true;
544 std::stringstream errorString;
545
546 errorString << "Failed to parse operator #" << operatorIndex
547 << " within subgraph #" << subgraphIndex
548 << " error: " << e.what();
549 BOOST_LOG_TRIVIAL(error) << errorString.str();
550
551 errors << errorString.str() << "\n";
552 }
553 ++operatorIndex;
554 }
555
556 SetupInputLayers(subgraphIndex);
557 SetupOutputLayers(subgraphIndex);
558
559 ++subgraphIndex;
560 }
561
562 if (failedToCreate)
563 {
564 // we can skip everything and let the outer exception handler deal with the error
565 throw ParseException(errors.str());
566 }
567
568 // establish the connections from the layer outputs to the inputs of the subsequent layers
569 for (size_t subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex)
570 {
571 for (size_t tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex)
572 {
573 if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot != nullptr)
574 {
575 for (size_t inputSlotIdx = 0;
576 inputSlotIdx < m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size();
577 ++inputSlotIdx)
578 {
579 m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot->Connect(
580 *(m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots[inputSlotIdx]));
581 }
582 }
583 }
584 }
585
586 return std::move(m_Network);
587}
588
589void TfLiteParser::RegisterProducerOfTensor(size_t subgraphIndex,
590 size_t tensorIndex,
591 armnn::IOutputSlot* slot)
592{
593 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
594 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
595 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
596
597 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
598
599 // assuming there is only one producer for that tensor
600 if (tensorSlots.outputSlot != nullptr)
601 {
602 throw ParseException(boost::str(
603 boost::format("Another layer has already registered itself as the producer of "
604 "subgraph:%1% tensor:%2% %3%") %
605 subgraphIndex %
606 tensorIndex %
607 CHECK_LOCATION().AsString()));
608 }
609
610 tensorSlots.outputSlot = slot;
611}
612
613void TfLiteParser::RegisterConsumerOfTensor(size_t subgraphIndex,
614 size_t tensorIndex,
615 armnn::IInputSlot* slot)
616{
617 CHECK_TENSOR(m_Model, subgraphIndex, tensorIndex);
618 BOOST_ASSERT(m_SubgraphConnections.size() > subgraphIndex);
619 BOOST_ASSERT(m_SubgraphConnections[subgraphIndex].size() > tensorIndex);
620
621 TensorSlots & tensorSlots = m_SubgraphConnections[subgraphIndex][tensorIndex];
622 tensorSlots.inputSlots.push_back(slot);
623}
624
625void TfLiteParser::ParseUnsupportedOperator(size_t subgraphIndex, size_t operatorIndex)
626{
627 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
628 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
629 //
630 auto opcodeIndex = operatorPtr->opcode_index;
631 auto opcode = m_Model->operator_codes[opcodeIndex]->builtin_code;
632
633 throw ParseException(
634 boost::str(
635 boost::format("Operator not supported. "
636 "subgraph:%1% operator:%2% "
637 "opcode_index:%3% opcode:%4% / %5% %6%") %
638 subgraphIndex %
639 operatorIndex %
640 opcodeIndex %
641 opcode %
642 tflite::EnumNameBuiltinOperator(opcode) %
643 CHECK_LOCATION().AsString()));
644}
645
646void TfLiteParser::ParseAveragePool2D(size_t subgraphIndex, size_t operatorIndex)
647{
648 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
649
650 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
651 const auto * options = operatorPtr->builtin_options.AsPool2DOptions();
652
653 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
654
655 Pooling2dDescriptor desc;
656
657 desc.m_PoolType = PoolingAlgorithm::Average;
658 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
659 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
660 desc.m_PoolWidth = CHECKED_NON_NEGATIVE(options->filter_width);
661 desc.m_PoolHeight = CHECKED_NON_NEGATIVE(options->filter_height);
662 desc.m_PaddingMethod = PaddingMethod::Exclude;
663 desc.m_OutputShapeRounding = OutputShapeRounding::Floor;
664
665 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
666 CHECK_VALID_SIZE(inputs.size(), 1);
667 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
668
669 // assuming input is NHWC
670 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
671 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
672
673 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
674 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
675
676 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
677 CHECK_VALID_SIZE(outputs.size(), 1);
678 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
679
680 auto layerName = boost::str(boost::format("AveragePool2D:%1%:%2%") % subgraphIndex % operatorIndex);
681 IConnectableLayer* layer = m_Network->AddPooling2dLayer(desc, layerName.c_str());
682
683 BOOST_ASSERT(layer != nullptr);
684
685 // add permute layers to swizzle the input and deswizzle the output
686 std::pair<IConnectableLayer*, IConnectableLayer*> permuteLayers =
687 SwizzleInDeswizzleOut(*m_Network, layer, 0, inputTensorInfo, 0, outputTensorInfo);
688
689 // register the input connection slots for the layer, connections are made after all layers have been created
690 // only the tensors for the inputs are relevant, exclude the const tensors
691 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
692 RegisterInputSlots(subgraphIndex, operatorIndex, permuteLayers.first, {inputTensorIndexes[0]});
693
694 // we need to add the activation layer and fortunately we don't need to care about the data layout
695 // beause the activation function is element-wise, so it is OK to have the activation after the trailing
696 // swizzle layer
Sadik Armagan58f39192018-09-17 14:14:39 +0100697 layer = AddFusedActivationLayer(permuteLayers.second, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100698 // register the output connection slots for the layer, connections are made after all layers have been created
699 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
700 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
701}
702
703void TfLiteParser::ParseConv2D(size_t subgraphIndex, size_t operatorIndex)
704{
705 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
706
707 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
708 const auto * options = operatorPtr->builtin_options.AsConv2DOptions();
709
710 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
711
712 Convolution2dDescriptor desc;
713 desc.m_BiasEnabled = false;
714 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
715 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
716
717 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
718 CHECK_VALID_SIZE(inputs.size(), 2, 3);
719
720 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
721 CHECK_VALID_SIZE(outputs.size(), 1);
722
723 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
724 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
725
726 // assuming input is NHWC
727 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
728 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
729
730 // assuming the filter is OHWI : Output, H, W, Input
731 // which is essentially the same as NHWC
732 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
733 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
734
735 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
736 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
737
738 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, true);
739 armnn::IConnectableLayer* layer;
740
741 auto layerName = boost::str(boost::format("Conv2D:%1%:%2%") % subgraphIndex % operatorIndex);
742
743 if (inputs.size() == 3)
744 {
745 desc.m_BiasEnabled = true;
746 armnn::TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
747 auto biasTensorAndData = CreateConstTensor(inputs[2], biasTensorInfo, false);
748 layer = m_Network->AddConvolution2dLayer(desc,
749 filterTensorAndData.first,
750 biasTensorAndData.first,
751 layerName.c_str());
752 }
753 else
754 {
755 layer = m_Network->AddConvolution2dLayer(desc,
756 filterTensorAndData.first,
757 layerName.c_str());
758 }
759
760 BOOST_ASSERT(layer != nullptr);
761
762 // add permute layers to swizzle the input and deswizzle the output
763 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
764 std::pair<IConnectableLayer*, IConnectableLayer*> permuteLayers =
765 SwizzleInDeswizzleOut(*m_Network, layer, 0, inputTensorInfo, 0, outputTensorInfo);
766
767 // register the input connection slots for the layer, connections are made after all layers have been created
768 // only the tensors for the inputs are relevant, exclude the const tensors
769 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
770 RegisterInputSlots(subgraphIndex, operatorIndex, permuteLayers.first, {inputTensorIndexes[0]});
771
772 // we need to add the activation layer and fortunately we don't need to care about the data layout
773 // beause the activation function is element-wise, so it is OK to have the activation after the trailing
774 // swizzle layer
Sadik Armagan58f39192018-09-17 14:14:39 +0100775 layer = AddFusedActivationLayer(permuteLayers.second, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100776 // register the output connection slots for the layer, connections are made after all layers have been created
777 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
778 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
779}
780
781void TfLiteParser::ParseDepthwiseConv2D(size_t subgraphIndex, size_t operatorIndex)
782{
783 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
784
785 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
786 const auto * options = operatorPtr->builtin_options.AsDepthwiseConv2DOptions();
787
788 CHECK_SUPPORTED_FUSED_ACTIVATION(options, subgraphIndex, operatorIndex);
789
790 DepthwiseConvolution2dDescriptor desc;
791 desc.m_BiasEnabled = false;
792 desc.m_StrideX = CHECKED_NON_NEGATIVE(options->stride_w);
793 desc.m_StrideY = CHECKED_NON_NEGATIVE(options->stride_h);
794 // ACL only supports a depth (channel) multiplier of 1, it is not currently stored in the descriptor
795 CHECK_VALID_SIZE(CHECKED_NON_NEGATIVE(options->depth_multiplier), 1);
796
797 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
798 CHECK_VALID_SIZE(inputs.size(), 2, 3);
799 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
800 CHECK_VALID_SIZE(outputs.size(), 1);
801
802 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
803 armnn::TensorInfo filterTensorInfo = ToTensorInfo(inputs[1]);
804
805 // assuming input is NHWC
806 unsigned int inputHeight = inputTensorInfo.GetShape()[1];
807 unsigned int inputWidth = inputTensorInfo.GetShape()[2];
808 // assuming the filter is OHWI : Output, H, W, Input
809 unsigned int filterHeight = filterTensorInfo.GetShape()[1];
810 unsigned int filterWidth = filterTensorInfo.GetShape()[2];
811
812 CalcPadding(inputHeight, filterHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, options->padding);
813 CalcPadding(inputWidth, filterWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, options->padding);
814
815 auto filterTensorAndData = CreateConstTensor(inputs[1], filterTensorInfo, true);
816 armnn::IConnectableLayer* layer;
817 auto layerName = boost::str(boost::format("DepthwiseConv2D:%1%:%2%") % subgraphIndex % operatorIndex);
818
819 if (inputs.size() == 3)
820 {
821 desc.m_BiasEnabled = true;
822 TensorInfo biasTensorInfo = ToTensorInfo(inputs[2]);
823 auto biasTensorAndData = CreateConstTensor(inputs[2], biasTensorInfo, false);
824 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
825 filterTensorAndData.first,
826 biasTensorAndData.first,
827 layerName.c_str());
828 }
829 else
830 {
831 layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
832 filterTensorAndData.first,
833 layerName.c_str());
834 }
835 BOOST_ASSERT(layer != nullptr);
836
837 // add permute layers to swizzle the input and deswizzle the output
838 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
839 std::pair<IConnectableLayer*, IConnectableLayer*> permuteLayers =
840 SwizzleInDeswizzleOut(*m_Network, layer, 0, inputTensorInfo, 0, outputTensorInfo);
841
842 // register the input connection slots for the layer, connections are made after all layers have been created
843 // only the tensors for the inputs are relevant, exclude the const tensors
844 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
845 RegisterInputSlots(subgraphIndex, operatorIndex, permuteLayers.first, {inputTensorIndexes[0]});
846
847 // we need to add the activation layer and fortunately we don't need to care about the data layout
848 // beause the activation function is element-wise, so it is OK to have the activation after the trailing
849 // swizzle layer
Sadik Armagan58f39192018-09-17 14:14:39 +0100850 layer = AddFusedActivationLayer(permuteLayers.second, 0, options->fused_activation_function);
telsoa01c577f2c2018-08-31 09:22:23 +0100851 // register the output connection slots for the layer, connections are made after all layers have been created
852 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
853 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
854}
855
856void TfLiteParser::ParseSoftmax(size_t subgraphIndex, size_t operatorIndex)
857{
858 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
859 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
860 const auto * options = operatorPtr->builtin_options.AsSoftmaxOptions();
861
862 SoftmaxDescriptor desc;
863 desc.m_Beta = options->beta;
864
865 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
866 CHECK_VALID_SIZE(inputs.size(), 1);
867 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
868 CHECK_VALID_SIZE(outputs.size(), 1);
869
870 auto layerName = boost::str(boost::format("Softmax:%1%:%2%") % subgraphIndex % operatorIndex);
871 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(desc, layerName.c_str());
872
873 armnn::TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
874 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
875
876 // register the input connection slots for the layer, connections are made after all layers have been created
877 // only the tensors for the inputs are relevant, exclude the const tensors
878 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
879 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
880
881 // register the output connection slots for the layer, connections are made after all layers have been created
882 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
883 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
884}
885
886armnn::TensorInfo TfLiteParser::OutputShapeOfSqueeze(const std::vector<uint32_t> & squeezeDimsIn,
887 const armnn::TensorInfo & inputTensorInfo)
888{
889 CHECK_VALID_SIZE(squeezeDimsIn.size(), 0, 1, 2, 3, 4);
890 std::vector<uint32_t> squeezeDims = squeezeDimsIn;
891 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
892
893 if (inputTensorInfo.GetNumDimensions() > 4)
894 {
895 std::stringstream ss;
896 ss << "Input tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
897 << " shape:" << inputTensorInfo.GetShape() << " "
898 << CHECK_LOCATION().AsString();
899 throw ParseException(ss.str());
900 }
901
902 if (squeezeDims.empty())
903 {
904 squeezeDims.assign(dimensionSequence,
905 dimensionSequence+inputTensorInfo.GetNumDimensions());
906 }
907
908 std::vector<uint32_t> outputDims;
909 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
910 {
911 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
912 auto currentDimension = inputTensorInfo.GetShape()[i];
913 if (skipSqueeze || currentDimension != 1)
914 {
915 outputDims.push_back(currentDimension);
916 }
917 }
918
919 if (outputDims.size() > 4)
920 {
921 std::stringstream ss;
922 ss << "Output tensor has unexpected number of dimensions:" << inputTensorInfo.GetNumDimensions()
923 << " shape:" << inputTensorInfo.GetShape() << " "
924 << CHECK_LOCATION().AsString();
925 throw ParseException(ss.str());
926 }
927
928 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
929 outputDims.data());
930
931 // we need to preserve the tensor type and the quantization data as well
932 TensorInfo outTensorInfo = inputTensorInfo;
933 outTensorInfo.SetShape(outShape);
934
935 return outTensorInfo;
936}
937
938void TfLiteParser::ParseSqueeze(size_t subgraphIndex, size_t operatorIndex)
939{
940 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
941
942 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
943 CHECK_VALID_SIZE(inputs.size(), 1);
944
945 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
946 CHECK_VALID_SIZE(outputs.size(), 1);
947
948 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
949 const auto * options = operatorPtr->builtin_options.AsSqueezeOptions();
950
951 armnn::TensorInfo inputTensorInfo = ToTensorInfo(inputs[0]);
952 armnn::TensorInfo outputTensorInfo =
953 TfLiteParser::OutputShapeOfSqueeze(AsUnsignedVector(options->squeeze_dims),
954 inputTensorInfo);
955
956 ReshapeDescriptor reshapeDesc;
957 reshapeDesc.m_TargetShape = outputTensorInfo.GetShape();
958
959 auto layerName = boost::str(boost::format("Squeeze:%1%:%2%") % subgraphIndex % operatorIndex);
960 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, layerName.c_str());
961 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
962
963 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
964 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
965
966 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
967 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
968}
969
Sadik Armagan58f39192018-09-17 14:14:39 +0100970void TfLiteParser::ParseRelu(size_t subgraphIndex, size_t operatorIndex)
971{
972 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
973
974 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
975 boost::ignore_unused(operatorPtr);
976
977 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
978 CHECK_VALID_SIZE(inputs.size(), 1);
979
980 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
981 CHECK_VALID_SIZE(outputs.size(), 1);
982
983 auto layerName = str(boost::format("Activation:RELU:%1%:%2%") % subgraphIndex % operatorIndex);
984 ActivationDescriptor activationDesc;
985 activationDesc.m_Function = ActivationFunction::ReLu;
986 IConnectableLayer* const layer =
987 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
988
989 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
990 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
991
992 // register the input connection slots for the layer, connections are made after all layers have been created
993 // only the tensors for the inputs are relevant, exclude the const tensors
994 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
995 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
996
997 // register the output connection slots for the layer, connections are made after all layers have been created
998 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
999 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1000}
1001
1002void TfLiteParser::ParseRelu6(size_t subgraphIndex, size_t operatorIndex)
1003{
1004 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1005
1006 const auto & operatorPtr = m_Model->subgraphs[subgraphIndex]->operators[operatorIndex];
1007 boost::ignore_unused(operatorPtr);
1008
1009 auto inputs = GetInputs(m_Model, subgraphIndex, operatorIndex);
1010 CHECK_VALID_SIZE(inputs.size(), 1);
1011
1012 auto outputs = GetOutputs(m_Model, subgraphIndex, operatorIndex);
1013 CHECK_VALID_SIZE(outputs.size(), 1);
1014
1015 auto layerName = str(boost::format("Activation:RELU6:%1%:%2%") % subgraphIndex % operatorIndex);
1016 ActivationDescriptor activationDesc;
1017 activationDesc.m_Function = ActivationFunction::BoundedReLu;
1018 activationDesc.m_A = 6.0f;
1019 activationDesc.m_B = 0.0f;
1020 IConnectableLayer* const layer =
1021 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1022
1023 TensorInfo outputTensorInfo = ToTensorInfo(outputs[0]);
1024 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
1025
1026 // register the input connection slots for the layer, connections are made after all layers have been created
1027 // only the tensors for the inputs are relevant, exclude the const tensors
1028 auto inputTensorIndexes = AsUnsignedVector(GetInputTensorIds(m_Model, subgraphIndex, operatorIndex));
1029 RegisterInputSlots(subgraphIndex, operatorIndex, layer, {inputTensorIndexes[0]});
1030
1031 // register the output connection slots for the layer, connections are made after all layers have been created
1032 auto outputTensorIndexes = AsUnsignedVector(GetOutputTensorIds(m_Model, subgraphIndex, operatorIndex));
1033 RegisterOutputSlots(subgraphIndex, operatorIndex, layer, {outputTensorIndexes[0]});
1034}
1035
1036armnn::IConnectableLayer* TfLiteParser::AddFusedActivationLayer(armnn::IConnectableLayer* prevLayer,
1037 unsigned int outputSlot,
1038 tflite::ActivationFunctionType activationType)
telsoa01c577f2c2018-08-31 09:22:23 +01001039{
1040 ActivationDescriptor activationDesc;
1041 std::string layerName = prevLayer->GetName();
1042
1043 switch(activationType)
1044 {
1045 case tflite::ActivationFunctionType_NONE:
1046 {
1047 // this is a no-op: return previous layer
1048 return prevLayer;
1049 }
1050 case tflite::ActivationFunctionType_RELU:
1051 {
1052 activationDesc.m_Function = ActivationFunction::ReLu;
1053 layerName += ":RELU";
1054 break;
1055 }
1056 case tflite::ActivationFunctionType_RELU6:
1057 {
1058 activationDesc.m_Function = ActivationFunction::BoundedReLu;
1059 activationDesc.m_A = 6.0f;
1060 activationDesc.m_B = 0.0f;
1061 layerName += ":RELU6";
1062 break;
1063 }
1064 case tflite::ActivationFunctionType_TANH:
1065 {
1066 activationDesc.m_Function = ActivationFunction::TanH;
1067 activationDesc.m_A = 1.0f;
1068 activationDesc.m_B = 1.0f;
1069 layerName += ":TANH";
1070 break;
1071 }
1072
1073 // I only put these here as a reminder what others we could support
1074 case tflite::ActivationFunctionType_RELU_N1_TO_1:
1075 case tflite::ActivationFunctionType_SIGN_BIT:
1076 default:
1077 {
1078 throw ParseException(
1079 boost::str(
1080 boost::format("TfLite parser doesn't suppport fused activation: "
1081 "%1%/%2% %3% ") %
1082 activationType %
1083 tflite::EnumNameActivationFunctionType(activationType) %
1084 CHECK_LOCATION().AsString()));
1085
1086 }
1087 }
1088
1089 IConnectableLayer* activationLayer =
1090 m_Network->AddActivationLayer(activationDesc, layerName.c_str());
1091
1092 auto & prevOutputSlot = prevLayer->GetOutputSlot(outputSlot);
1093 prevOutputSlot.Connect(activationLayer->GetInputSlot(0));
1094 activationLayer->GetOutputSlot(0).SetTensorInfo(prevOutputSlot.GetTensorInfo());
1095 return activationLayer;
1096}
1097
1098TfLiteParser::ModelPtr TfLiteParser::LoadModelFromFile(const char * fileName)
1099{
1100 if (fileName == nullptr)
1101 {
1102 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) file name %1%") %
1103 CHECK_LOCATION().AsString()));
1104 }
1105 boost::system::error_code errorCode;
1106 boost::filesystem::path pathToFile(fileName);
1107 if (!boost::filesystem::exists(pathToFile, errorCode))
1108 {
1109 throw FileNotFoundException(boost::str(boost::format("Cannot find the file (%1%) errorCode: %2% %3%") %
1110 fileName %
1111 errorCode %
1112 CHECK_LOCATION().AsString()));
1113 }
1114 std::ifstream file(fileName, std::ios::binary);
1115 std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
1116 return LoadModelFromBinary(reinterpret_cast<const uint8_t *>(fileContent.c_str()),
1117 fileContent.size());
1118}
1119
1120TfLiteParser::ModelPtr TfLiteParser::LoadModelFromBinary(const uint8_t * binaryContent, size_t len)
1121{
1122 if (binaryContent == nullptr)
1123 {
1124 throw InvalidArgumentException(boost::str(boost::format("Invalid (null) binary content %1%") %
1125 CHECK_LOCATION().AsString()));
1126 }
1127 flatbuffers::Verifier verifier(binaryContent, len);
1128 if (verifier.VerifyBuffer<tflite::Model>() == false)
1129 {
1130 throw ParseException(
1131 boost::str(boost::format("Buffer doesn't conform to the expected Tensorflow Lite "
1132 "flatbuffers format. size:%1% %2%") %
1133 len %
1134 CHECK_LOCATION().AsString()));
1135 }
1136 return tflite::UnPackModel(binaryContent);
1137}
1138
1139TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model,
1140 size_t subgraphIndex,
1141 size_t operatorIndex)
1142{
1143 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1144
1145 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1146 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1147
1148 size_t inputCount = operatorPtr->inputs.size();
1149 TensorRawPtrVector result(inputCount);
1150 for (size_t i=0; i<inputCount; ++i)
1151 {
1152 uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[i]);
1153 result[i] = subGraphPtr->tensors[inputId].get();
1154 }
1155 return result;
1156}
1157
1158TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model,
1159 size_t subgraphIndex,
1160 size_t operatorIndex)
1161{
1162 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1163
1164 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1165 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1166
1167 size_t outputCount = operatorPtr->outputs.size();
1168 TensorRawPtrVector result(outputCount);
1169 for (size_t i=0; i<outputCount; ++i)
1170 {
1171 uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]);
1172 CHECK_TENSOR(model, subgraphIndex, outputId);
1173 result[i] = subGraphPtr->tensors[outputId].get();
1174 }
1175 return result;
1176}
1177
1178TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPtr & model,
1179 size_t subgraphIndex)
1180{
1181 CHECK_SUBGRAPH(model, subgraphIndex);
1182 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1183
1184 size_t inputCount = subGraphPtr->inputs.size();
1185 TensorIdRawPtrVector result(inputCount);
1186 for (size_t i=0; i<inputCount; ++i)
1187 {
1188 uint32_t inputId = CHECKED_NON_NEGATIVE(subGraphPtr->inputs[i]);
1189 CHECK_TENSOR(model, subgraphIndex, inputId);
1190 result[i] = std::make_pair(inputId, subGraphPtr->tensors[inputId].get());
1191 }
1192 return result;
1193}
1194
1195TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelPtr & model,
1196 size_t subgraphIndex)
1197{
1198 CHECK_SUBGRAPH(model, subgraphIndex);
1199 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1200
1201 size_t outputCount = subGraphPtr->outputs.size();
1202 TensorIdRawPtrVector result(outputCount);
1203 for (size_t i=0; i<outputCount; ++i)
1204 {
1205 uint32_t outputId = CHECKED_NON_NEGATIVE(subGraphPtr->outputs[i]);
1206 result[i] = std::make_pair(outputId, subGraphPtr->tensors[outputId].get());
1207 }
1208 return result;
1209}
1210
1211std::vector<int32_t>& TfLiteParser::GetInputTensorIds(const ModelPtr& model,
1212 size_t subgraphIndex,
1213 size_t operatorIndex)
1214{
1215 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1216 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1217 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1218 return operatorPtr->inputs;
1219}
1220
1221std::vector<int32_t>& TfLiteParser::GetOutputTensorIds(const ModelPtr& model,
1222 size_t subgraphIndex,
1223 size_t operatorIndex)
1224{
1225 CHECK_MODEL(model, subgraphIndex, operatorIndex);
1226 const auto & subGraphPtr = model->subgraphs[subgraphIndex];
1227 const auto & operatorPtr = subGraphPtr->operators[operatorIndex];
1228 return operatorPtr->outputs;
1229}
1230
1231void TfLiteParser::RegisterInputSlots(size_t subgraphIndex,
1232 size_t operatorIndex,
1233 IConnectableLayer* layer,
1234 const std::vector<unsigned int>& tensorIndexes)
1235{
1236 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1237 BOOST_ASSERT(layer != nullptr);
1238 if (tensorIndexes.size() != layer->GetNumInputSlots())
1239 {
1240 throw ParseException(
1241 boost::str(boost::format("The number of tensor inputs (%1%) does not match the number expected (%2%)"
1242 " for subgraph:%3% operator index:%4% %5%") %
1243 tensorIndexes.size() %
1244 layer->GetNumInputSlots() %
1245 subgraphIndex %
1246 operatorIndex %
1247 CHECK_LOCATION().AsString()));
1248 }
1249
1250 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumInputSlots(); ++slotIndex)
1251 {
1252 unsigned int tensorIndex = tensorIndexes[slotIndex];
1253 armnn::IInputSlot* slot = &(layer->GetInputSlot(slotIndex));
1254 RegisterConsumerOfTensor(subgraphIndex, tensorIndex, slot);
1255 }
1256}
1257
1258void TfLiteParser::RegisterOutputSlots(size_t subgraphIndex,
1259 size_t operatorIndex,
1260 IConnectableLayer* layer,
1261 const std::vector<unsigned int>& tensorIndexes)
1262{
1263 CHECK_MODEL(m_Model, subgraphIndex, operatorIndex);
1264 BOOST_ASSERT(layer != nullptr);
1265 if (tensorIndexes.size() != layer->GetNumOutputSlots())
1266 {
1267 throw ParseException(
1268 boost::str(boost::format("The number of tensor outputs (%1%) does not match the number expected (%2%)"
1269 " for subgraph:%3% operator index:%4% %5%") %
1270 tensorIndexes.size() %
1271 layer->GetNumOutputSlots() %
1272 subgraphIndex %
1273 operatorIndex %
1274 CHECK_LOCATION().AsString()));
1275 }
1276
1277 for (unsigned int slotIndex = 0; slotIndex < layer->GetNumOutputSlots(); ++slotIndex)
1278 {
1279 unsigned int tensorIndex = tensorIndexes[slotIndex];
1280 armnn::IOutputSlot* slot = &(layer->GetOutputSlot(slotIndex));
1281 RegisterProducerOfTensor(subgraphIndex, tensorIndex, slot);
1282 }
1283}
1284
1285void TfLiteParser::SetupInputLayers(size_t subgraphIndex)
1286{
1287 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1288
1289 auto inputs = GetSubgraphInputs(m_Model, subgraphIndex);
1290 for (auto const & tensorIdAndPtr : inputs)
1291 {
1292 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1293 IConnectableLayer* layer =
1294 m_Network->AddInputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1295
1296 auto tensorInfo = ToTensorInfo(tensorIdAndPtr.second);
1297 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
1298
1299 RegisterOutputSlots(subgraphIndex,
1300 VIRTUAL_OPERATOR_ID,
1301 layer,
1302 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1303 }
1304}
1305
1306void TfLiteParser::SetupOutputLayers(size_t subgraphIndex)
1307{
1308 CHECK_SUBGRAPH(m_Model, subgraphIndex);
1309
1310 auto outputs = GetSubgraphOutputs(m_Model, subgraphIndex);
1311 for (auto const & tensorIdAndPtr : outputs)
1312 {
1313 auto bindingId = GenerateLayerBindingId(subgraphIndex, tensorIdAndPtr.first);
1314 IConnectableLayer* layer =
1315 m_Network->AddOutputLayer(bindingId, tensorIdAndPtr.second->name.c_str());
1316
1317 RegisterInputSlots(subgraphIndex,
1318 VIRTUAL_OPERATOR_ID,
1319 layer,
1320 { static_cast<uint32_t>(tensorIdAndPtr.first) });
1321 }
1322}
1323
1324// example usage: BufferRawPtr bufferPtr = GetBuffer(m_Model, inputs[0]->buffer);
1325TfLiteParser::BufferRawPtr TfLiteParser::GetBuffer(const ModelPtr& model, size_t bufferIndex)
1326{
1327 CHECK_BUFFER(model, bufferIndex);
1328 return model->buffers[bufferIndex].get();
1329}
1330
1331std::pair<armnn::ConstTensor, TfLiteParser::SupportedDataStorage>
1332TfLiteParser::CreateConstTensor(TensorRawPtr tensorPtr,
1333 armnn::TensorInfo & tensorInfo,
1334 bool convertFromTfToArmnnFormat)
1335{
1336 CHECK_TENSOR_PTR(tensorPtr);
1337 auto bufferPtr = GetBuffer(m_Model, tensorPtr->buffer);
1338 CHECK_BUFFER_SIZE(bufferPtr, tensorInfo, tensorPtr->buffer);
1339
1340 switch (tensorInfo.GetDataType())
1341 {
1342 case armnn::DataType::Float32:
1343 {
1344 auto constData = CreateConstTensorImpl<float>(bufferPtr,
1345 tensorPtr,
1346 tensorInfo,
1347 convertFromTfToArmnnFormat);
1348 SupportedDataStorage storage(std::move(constData.second));
1349 return std::make_pair(constData.first, std::move(storage));
1350 }
1351 case armnn::DataType::QuantisedAsymm8:
1352 {
1353 auto constData = CreateConstTensorImpl<uint8_t>(bufferPtr,
1354 tensorPtr,
1355 tensorInfo,
1356 convertFromTfToArmnnFormat);
1357 SupportedDataStorage storage(std::move(constData.second));
1358 return std::make_pair(constData.first, std::move(storage));
1359 }
1360 case armnn::DataType::Signed32:
1361 {
1362 auto constData = CreateConstTensorImpl<int32_t>(bufferPtr,
1363 tensorPtr,
1364 tensorInfo,
1365 convertFromTfToArmnnFormat);
1366 SupportedDataStorage storage(std::move(constData.second));
1367 return std::make_pair(constData.first, std::move(storage));
1368 }
1369 default:
1370 {
1371 std::stringstream errString;
1372 errString << "Unexpected datatype when creating const tensor: "
1373 << armnn::GetDataTypeName(tensorInfo.GetDataType())
1374 << " shape:" << tensorInfo.GetShape()
1375 << CHECK_LOCATION().AsString();
1376 throw ParseException(errString.str());
1377 }
1378 }
1379}
1380
1381BindingPointInfo TfLiteParser::GetNetworkInputBindingInfo(size_t subgraphId,
1382 const std::string& name) const
1383{
1384 CHECK_SUBGRAPH(m_Model, subgraphId);
1385 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
1386 for (auto const & input : inputs)
1387 {
1388 if (input.second->name == name)
1389 {
1390 auto bindingId = GenerateLayerBindingId(subgraphId, input.first);
1391 return std::make_pair(bindingId, ToTensorInfo(input.second));
1392 }
1393 }
1394
1395 std::stringstream bindings;
1396 for (auto const & input : inputs)
1397 {
1398 bindings << "'" << input.second->name << "' ";
1399 }
1400
1401 throw ParseException(
1402 boost::str(
1403 boost::format("No input binding found for subgraph:%1% and name:%2%. "
1404 "Possible inputs are: [%3%] %4%") %
1405 subgraphId %
1406 name %
1407 bindings.str() %
1408 CHECK_LOCATION().AsString()));
1409}
1410
1411BindingPointInfo TfLiteParser::GetNetworkOutputBindingInfo(size_t subgraphId,
1412 const std::string& name) const
1413{
1414 CHECK_SUBGRAPH(m_Model, subgraphId);
1415 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
1416 for (auto const & output : outputs)
1417 {
1418 if (output.second->name == name)
1419 {
1420 auto bindingId = GenerateLayerBindingId(subgraphId, output.first);
1421 return std::make_pair(bindingId, ToTensorInfo(output.second));
1422 }
1423 }
1424
1425 std::stringstream bindings;
1426 for (auto const & output : outputs)
1427 {
1428 bindings << "'" << output.second->name << "' ";
1429 }
1430
1431 throw ParseException(
1432 boost::str(
1433 boost::format("No output binding found for subgraph:%1% and name:%2%. "
1434 "Possible outputs are: [%3%] %4%") %
1435 subgraphId %
1436 name %
1437 bindings.str() %
1438 CHECK_LOCATION().AsString()));
1439}
1440
1441size_t TfLiteParser::GetSubgraphCount() const
1442{
1443 return m_Model->subgraphs.size();
1444}
1445
1446std::vector<std::string> TfLiteParser::GetSubgraphInputTensorNames(size_t subgraphId) const
1447{
1448 CHECK_SUBGRAPH(m_Model, subgraphId);
1449 auto inputs = GetSubgraphInputs(m_Model, subgraphId);
1450 std::vector<std::string> result;
1451 result.reserve(inputs.size());
1452 for (auto const & input : inputs)
1453 {
1454 result.push_back(input.second->name);
1455 }
1456 return result;
1457}
1458
1459std::vector<std::string> TfLiteParser::GetSubgraphOutputTensorNames(size_t subgraphId) const
1460{
1461 CHECK_SUBGRAPH(m_Model, subgraphId);
1462 auto outputs = GetSubgraphOutputs(m_Model, subgraphId);
1463 std::vector<std::string> result;
1464 result.reserve(outputs.size());
1465 for (auto const & output : outputs)
1466 {
1467 result.push_back(output.second->name);
1468 }
1469 return result;
1470}
1471
1472ITfLiteParser* ITfLiteParser::CreateRaw()
1473{
1474 return new TfLiteParser();
1475}
1476
1477ITfLiteParserPtr ITfLiteParser::Create()
1478{
1479 return ITfLiteParserPtr(CreateRaw(), &ITfLiteParser::Destroy);
1480}
1481
1482void ITfLiteParser::Destroy(ITfLiteParser* parser)
1483{
1484 delete parser;
1485}
1486
1487TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<float[]> && data)
1488: m_FloatData(std::move(data))
1489, m_Uint8Data(nullptr)
1490, m_Int32Data(nullptr)
1491{
1492}
1493
1494TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<uint8_t[]> && data)
1495: m_FloatData(nullptr)
1496, m_Uint8Data(std::move(data))
1497, m_Int32Data(nullptr)
1498{
1499}
1500
1501TfLiteParser::SupportedDataStorage::SupportedDataStorage(std::unique_ptr<int32_t[]> && data)
1502: m_FloatData(nullptr)
1503, m_Uint8Data(nullptr)
1504, m_Int32Data(std::move(data))
1505{
1506}
1507
1508} // armnnTfLiteParser