blob: 0775467162727cd3b1802457e49ba99329d4da24 [file] [log] [blame]
alexander3c798932021-03-26 21:42:19 +00001/*
2 * Copyright (c) 2021 Arm Limited. All rights reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17#include "Model.hpp"
18
19#include "hal.h"
20
21#include <cstdint>
22
23/* Initialise the model */
24arm::app::Model::~Model()
25{
26 if (this->_m_pInterpreter) {
27 delete this->_m_pInterpreter;
28 }
29
30 /**
31 * No clean-up function available for allocator in TensorFlow Lite Micro yet.
32 **/
33}
34
35arm::app::Model::Model() :
36 _m_inited (false),
37 _m_type(kTfLiteNoType)
38{
39 this->_m_pErrorReporter = &this->_m_uErrorReporter;
40}
41
42bool arm::app::Model::Init(tflite::MicroAllocator* allocator)
43{
44 /* Following tf lite micro example:
45 * Map the model into a usable data structure. This doesn't involve any
46 * copying or parsing, it's a very lightweight operation. */
47 const uint8_t* model_addr = ModelPointer();
48 debug("loading model from @ 0x%p\n", model_addr);
49 this->_m_pModel = ::tflite::GetModel(model_addr);
50
51 if (this->_m_pModel->version() != TFLITE_SCHEMA_VERSION) {
52 this->_m_pErrorReporter->Report(
53 "[ERROR] model's schema version %d is not equal "
54 "to supported version %d.",
55 this->_m_pModel->version(), TFLITE_SCHEMA_VERSION);
56 return false;
57 }
58
59 /* Pull in only the operation implementations we need.
60 * This relies on a complete list of all the ops needed by this graph.
61 * An easier approach is to just use the AllOpsResolver, but this will
62 * incur some penalty in code space for op implementations that are not
63 * needed by this graph.
64 * static ::tflite::ops::micro::AllOpsResolver resolver; */
65 /* NOLINTNEXTLINE(runtime-global-variables) */
66 debug("loading op resolver\n");
67
68 this->EnlistOperations();
69
70 /* Create allocator instance, if it doesn't exist */
71 this->_m_pAllocator = allocator;
72 if (!this->_m_pAllocator) {
73 /* Create an allocator instance */
74 info("Creating allocator using tensor arena in %s\n",
75 ACTIVATION_BUF_SECTION_NAME);
76
77 this->_m_pAllocator = tflite::MicroAllocator::Create(
78 this->GetTensorArena(),
79 this->GetActivationBufferSize(),
80 this->_m_pErrorReporter);
81
82 if (!this->_m_pAllocator) {
83 printf_err("Failed to create allocator\n");
84 return false;
85 }
86 debug("Created new allocator @ 0x%p\n", this->_m_pAllocator);
87 } else {
88 debug("Using existing allocator @ 0x%p\n", this->_m_pAllocator);
89 }
90
91 this->_m_pInterpreter = new ::tflite::MicroInterpreter(
92 this->_m_pModel, this->GetOpResolver(),
93 this->_m_pAllocator, this->_m_pErrorReporter);
94
95 if (!this->_m_pInterpreter) {
96 printf_err("Failed to allocate interpreter\n");
97 return false;
98 }
99
100 /* Allocate memory from the tensor_arena for the model's tensors. */
101 info("Allocating tensors\n");
102 TfLiteStatus allocate_status = this->_m_pInterpreter->AllocateTensors();
103
104 if (allocate_status != kTfLiteOk) {
105 this->_m_pErrorReporter->Report("[ERROR] allocateTensors() failed");
106 printf_err("tensor allocation failed!\n");
107 delete this->_m_pInterpreter;
108 return false;
109 }
110
111 /* Get information about the memory area to use for the model's input. */
112 this->_m_input.resize(this->GetNumInputs());
113 for (size_t inIndex = 0; inIndex < this->GetNumInputs(); inIndex++)
114 this->_m_input[inIndex] = this->_m_pInterpreter->input(inIndex);
115
116 this->_m_output.resize(this->GetNumOutputs());
117 for (size_t outIndex = 0; outIndex < this->GetNumOutputs(); outIndex++)
118 this->_m_output[outIndex] = this->_m_pInterpreter->output(outIndex);
119
120 if (this->_m_input.empty() || this->_m_output.empty()) {
121 printf_err("failed to get tensors\n");
122 return false;
123 } else {
124 this->_m_type = this->_m_input[0]->type; /* Input 0 should be the main input */
125
126 /* Clear the input & output tensors */
127 for (size_t inIndex = 0; inIndex < this->GetNumInputs(); inIndex++) {
128 std::memset(this->_m_input[inIndex]->data.data, 0, this->_m_input[inIndex]->bytes);
129 }
130 for (size_t outIndex = 0; outIndex < this->GetNumOutputs(); outIndex++) {
131 std::memset(this->_m_output[outIndex]->data.data, 0, this->_m_output[outIndex]->bytes);
132 }
133
134 this->LogInterpreterInfo();
135 }
136
137 this->_m_inited = true;
138 return true;
139}
140
141tflite::MicroAllocator* arm::app::Model::GetAllocator()
142{
143 if (this->IsInited()) {
144 return this->_m_pAllocator;
145 }
146 return nullptr;
147}
148
149void arm::app::Model::LogTensorInfo(TfLiteTensor* tensor)
150{
151 if (!tensor) {
152 printf_err("Invalid tensor\n");
153 assert(tensor);
154 return;
155 }
156
157 debug("\ttensor is assigned to 0x%p\n", tensor);
158 info("\ttensor type is %s\n", TfLiteTypeGetName(tensor->type));
159 info("\ttensor occupies %u bytes with dimensions\n",
160 (uint32_t)tensor->bytes);
161 for (int i = 0 ; i < tensor->dims->size; ++i) {
162 info ("\t\t%d: %3d\n", i, tensor->dims->data[i]);
163 }
164
165 TfLiteQuantization quant = tensor->quantization;
166 if (kTfLiteAffineQuantization == quant.type) {
167 auto* quantParams = (TfLiteAffineQuantization*)quant.params;
168 info("Quant dimension: %u\n", quantParams->quantized_dimension);
169 for (int i = 0; i < quantParams->scale->size; ++i) {
170 info("Scale[%d] = %f\n", i, quantParams->scale->data[i]);
171 }
172 for (int i = 0; i < quantParams->zero_point->size; ++i) {
173 info("ZeroPoint[%d] = %d\n", i, quantParams->zero_point->data[i]);
174 }
175 }
176}
177
178void arm::app::Model::LogInterpreterInfo()
179{
180 if (!this->_m_pInterpreter) {
181 printf_err("Invalid interpreter\n");
182 return;
183 }
184
185 info("Model INPUT tensors: \n");
186 for (auto input : this->_m_input) {
187 this->LogTensorInfo(input);
188 }
189
190 info("Model OUTPUT tensors: \n");
191 for (auto output : this->_m_output) {
192 this->LogTensorInfo(output);
193 }
194
195 info("Activation buffer (a.k.a tensor arena) size used: %zu\n",
196 this->_m_pInterpreter->arena_used_bytes());
197
198 const uint32_t nOperators = this->_m_pInterpreter->operators_size();
199 info("Number of operators: %u\n", nOperators);
200
201 /* For each operator, display registration information */
202 for (uint32_t i = 0 ; i < nOperators; ++i) {
203 const tflite::NodeAndRegistration nodeReg =
204 this->_m_pInterpreter->node_and_registration(i);
205 const TfLiteRegistration* reg = nodeReg.registration;
206 std::string opName{""};
207
208 if (reg) {
209 if (tflite::BuiltinOperator_CUSTOM == reg->builtin_code) {
210 opName = std::string(reg->custom_name);
211 } else {
212 opName = std::string(EnumNameBuiltinOperator(
213 tflite::BuiltinOperator(reg->builtin_code)));
214 }
215 }
216 info("\tOperator %u: %s\n", i, opName.c_str());
217 }
218}
219
220bool arm::app::Model::IsInited() const
221{
222 return this->_m_inited;
223}
224
225bool arm::app::Model::IsDataSigned() const
226{
227 return this->GetType() == kTfLiteInt8;
228}
229
230bool arm::app::Model::RunInference()
231{
232 bool inference_state = false;
233 if (this->_m_pModel && this->_m_pInterpreter) {
234 if (kTfLiteOk != this->_m_pInterpreter->Invoke()) {
235 printf_err("Invoke failed.\n");
236 } else {
237 inference_state = true;
238 }
239 } else {
240 printf_err("Error: No interpreter!\n");
241 }
242 return inference_state;
243}
244
245TfLiteTensor* arm::app::Model::GetInputTensor(size_t index) const
246{
247 if (index < this->GetNumInputs()) {
248 return this->_m_input.at(index);
249 }
250 return nullptr;
251}
252
253TfLiteTensor* arm::app::Model::GetOutputTensor(size_t index) const
254{
255 if (index < this->GetNumOutputs()) {
256 return this->_m_output.at(index);
257 }
258 return nullptr;
259}
260
261size_t arm::app::Model::GetNumInputs() const
262{
263 if (this->_m_pModel && this->_m_pInterpreter) {
264 return this->_m_pInterpreter->inputs_size();
265 }
266 return 0;
267}
268
269size_t arm::app::Model::GetNumOutputs() const
270{
271 if (this->_m_pModel && this->_m_pInterpreter) {
272 return this->_m_pInterpreter->outputs_size();
273 }
274 return 0;
275}
276
277
278TfLiteType arm::app::Model::GetType() const
279{
280 return this->_m_type;
281}
282
283TfLiteIntArray* arm::app::Model::GetInputShape(size_t index) const
284{
285 if (index < this->GetNumInputs()) {
286 return this->_m_input.at(index)->dims;
287 }
288 return nullptr;
289}
290
291TfLiteIntArray* arm::app::Model::GetOutputShape(size_t index) const
292{
293 if (index < this->GetNumOutputs()) {
294 return this->_m_output.at(index)->dims;
295 }
296 return nullptr;
297}
298
299bool arm::app::Model::ShowModelInfoHandler()
300{
301 if (!this->IsInited()) {
302 printf_err("Model is not initialised! Terminating processing.\n");
303 return false;
304 }
305
306 PrintTensorFlowVersion();
307 info("Model info:\n");
308 this->LogInterpreterInfo();
309
310#if defined(ARM_NPU)
311 info("Use of Arm uNPU is enabled\n");
312#else /* ARM_NPU */
313 info("Use of Arm uNPU is disabled\n");
314#endif /* ARM_NPU */
315
316 return true;
317}
318namespace arm {
319namespace app {
320 static uint8_t _tensor_arena[ACTIVATION_BUF_SZ] ACTIVATION_BUF_ATTRIBUTE;
321} /* namespace app */
322} /* namespace arm */
323
324size_t arm::app::Model::GetActivationBufferSize()
325{
326 return ACTIVATION_BUF_SZ;
327}
328
329uint8_t *arm::app::Model::GetTensorArena()
330{
331 return _tensor_arena;
332}