ONE - On-device Neural Engine
Loading...
Searching...
No Matches
onert-micro.cpp
Go to the documentation of this file.
1/*
2 * Copyright (c) 2024 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 <string>
18#include <memory>
19#include <thread>
20#include <vector>
21#include <iostream>
22#include <fstream>
23
25#include "onert-micro.h"
26#include <circle-generated/circle/schema_generated.h>
27#include <circle-generated/circle/traininfo_generated.h>
28
29#define NNFW_RETURN_ERROR_IF_NULL(p) \
30 do \
31 { \
32 if ((p) == NULL) \
33 return NNFW_STATUS_UNEXPECTED_NULL; \
34 } while (0)
35
36// helper for file processing
37using DataBuffer = std::vector<char>;
38
39DataBuffer readFile(const char *path)
40{
41 std::ifstream file(path, std::ios::binary | std::ios::in);
42 if (!file.good())
43 {
44 std::string errmsg = "Failed to open file";
45 std::cerr << errmsg << std::endl;
46 exit(-1); // FIXME: proper way
47 }
48
49 file.seekg(0, std::ios::end);
50 auto fileSize = file.tellg();
51 file.seekg(0, std::ios::beg);
52
53 // reserve capacity
54 DataBuffer model_data(fileSize);
55
56 // read the data
57 file.read(model_data.data(), fileSize);
58 if (file.fail())
59 {
60 std::string errmsg = "Failed to read file";
61 std::cerr << errmsg << std::endl;
62 exit(-1); // FIXME: proper way
63 }
64
65 return model_data;
66}
67
69{
70private:
71public:
77 static NNFW_STATUS create(nnfw_session **session);
78
79private:
81
82public:
84
85 NNFW_STATUS load_model_from_file(const char *package_file_path);
86
91 NNFW_STATUS train_set_input(uint32_t index, void *input);
92 NNFW_STATUS train_set_expected(uint32_t index, void *expected);
93 NNFW_STATUS train_set_output(uint32_t index, NNFW_TYPE type, void *buffer, size_t length);
94 NNFW_STATUS train_run(bool update_weights);
95 NNFW_STATUS train_get_loss(uint32_t index, float *loss);
96 NNFW_STATUS train_export_circle(const char *path);
97
98 NNFW_STATUS train_export_checkpoint(const char *path);
99 NNFW_STATUS train_import_checkpoint(const char *path);
100
101private:
102 uint32_t getInputSize();
103 uint32_t getOutputSize();
104 NNFW_STATUS loadTrainingInfo(char *buf_ptr);
105 NNFW_STATUS loadOptimizerInfo(const circle::ModelTraining *circle_model);
106 NNFW_STATUS loadLossInfo(const circle::ModelTraining *circle_model);
107 NNFW_STATUS loadTrainableOps(const circle::ModelTraining *circle_model, int num_ops);
108
109private:
110 onert_micro::OMTrainingInterpreter *_train_interpreter;
111 onert_micro::OMConfig _config;
112 DataBuffer _model_buf;
113 std::string _model_path;
114 uint8_t *outputbuf;
115};
116
117nnfw_session::nnfw_session() : _train_interpreter{new onert_micro::OMTrainingInterpreter()}
118{
119 // TODO: Remove after implementing train_set_traininfo
120 // Set user defined training settings
121 const uint32_t training_epochs = 10;
122 const float learning_rate = 0.001f;
123 const uint32_t num_train_layers = 10;
126 const float beta = 0.9;
127 const float beta_squares = 0.999;
128 const float epsilon = 1e-07;
129
130 _config.train_mode = true;
131 {
132 onert_micro::OMTrainingContext train_context;
133 train_context.batch_size = 32;
134 train_context.num_of_train_layers = num_train_layers;
135 train_context.learning_rate = learning_rate;
136 train_context.loss = loss;
137 train_context.optimizer = train_optim;
138 train_context.beta = beta;
139 train_context.beta_squares = beta_squares;
140 train_context.epsilon = epsilon;
141 train_context.num_step = 0;
142
143 _config.training_context = train_context;
144 }
145
146 outputbuf = nullptr;
147}
148
150{
151 if (session == nullptr)
153
154 auto new_session = std::unique_ptr<nnfw_session>(new nnfw_session());
155 *session = new_session.release();
156
157 if (*session == nullptr)
158 {
159 return NNFW_STATUS_ERROR;
160 }
161
163}
164
165nnfw_session::~nnfw_session() { delete _train_interpreter; }
166
167NNFW_STATUS nnfw_session::loadOptimizerInfo(const circle::ModelTraining *circle_model)
168{
169 assert(circle_model != nullptr);
170
171 const circle::Optimizer circle_opt = circle_model->optimizer();
172
173 switch (circle_opt)
174 {
175 case circle::Optimizer_SGD:
178 circle_model->optimizer_opt_as_SGDOptions()->learning_rate();
179 break;
180 case circle::Optimizer_ADAM:
183 circle_model->optimizer_opt_as_AdamOptions()->learning_rate();
184 _config.training_context.beta = circle_model->optimizer_opt_as_AdamOptions()->beta_1();
186 circle_model->optimizer_opt_as_AdamOptions()->beta_2();
187 _config.training_context.epsilon = circle_model->optimizer_opt_as_AdamOptions()->epsilon();
188 break;
189 default:
190 std::cerr << "unknown optimzer" << std::endl;
191 return NNFW_STATUS_ERROR;
192 }
194}
195
196NNFW_STATUS nnfw_session::loadLossInfo(const circle::ModelTraining *circle_model)
197{
198 assert(circle_model != nullptr);
199 NNFW_RETURN_ERROR_IF_NULL(circle_model);
200
201 const circle::LossFn circle_loss = circle_model->lossfn();
202
203 switch (circle_loss)
204 {
205 case circle::LossFn::LossFn_CATEGORICAL_CROSSENTROPY:
207 break;
208 case circle::LossFn::LossFn_MEAN_SQUARED_ERROR:
210 break;
211 case circle::LossFn::LossFn_SPARSE_CATEGORICAL_CROSSENTROPY:
212 // TODO enable this conversion after core support sparse_categorial_crossentropy
213 std::cerr << "'sparse_categorical_crossentropy' is not supported yet" << std::endl;
214 return NNFW_STATUS_ERROR;
215 default:
216 std::cerr << "unknown loss function" << std::endl;
217 return NNFW_STATUS_ERROR;
218 }
220}
221
222NNFW_STATUS nnfw_session::loadTrainableOps(const circle::ModelTraining *circle_model, int num_ops)
223{
224 assert(circle_model != nullptr);
225 NNFW_RETURN_ERROR_IF_NULL(circle_model);
226
227 auto ops_list = circle_model->trainable_ops();
228 if (ops_list != nullptr)
230 num_ops - ops_list->data()[0]; // simply assume ops[0] is the least node number
231 else
232 _config.training_context.num_of_train_layers = num_ops;
234}
235
236NNFW_STATUS nnfw_session::loadTrainingInfo(char *buf)
237{
238 auto model = circle::GetModel(buf);
239 auto num_ops = model->subgraphs()->Get(0)->operators()->size();
240 // Load Metadata
241 auto const metadata_list = model->metadata();
242 const uint8_t *data = nullptr;
243 if (metadata_list != nullptr)
244 {
245 for (uint32_t i = 0; i < metadata_list->size(); ++i)
246 {
247 const auto metadata = metadata_list->Get(i);
248 if (strcmp(metadata->name()->c_str(), "CIRCLE_TRAINING") != 0)
249 continue;
250 data = (model->buffers()->Get(metadata->buffer()))->data()->data();
251 }
253
254 const circle::ModelTraining *traininfo_model =
255 circle::GetModelTraining(static_cast<const void *>(data));
256 _config.training_context.batch_size = traininfo_model->batch_size();
257 NNFW_STATUS status = loadOptimizerInfo(traininfo_model);
258 if (status != NNFW_STATUS_NO_ERROR)
259 return status;
260
261 status = loadLossInfo(traininfo_model);
262 if (status != NNFW_STATUS_NO_ERROR)
263 return status;
264
265 status = loadTrainableOps(traininfo_model, num_ops);
266 if (status != NNFW_STATUS_NO_ERROR)
267 return status;
268 }
270}
271
273{
274 _model_buf = readFile(file_path);
275 _config.model_ptr = _model_buf.data();
276 _config.model_size = _model_buf.size();
277 // load training info
278 loadTrainingInfo(_config.model_ptr);
279 // TODO: this import should start on nnfw_prepare if inference_interpreter is introduced
280 _train_interpreter->importTrainModel(_config.model_ptr, _config);
282}
283
285{
286 // TODO: Implement remaining jobs if inference_interpreter is introduced
287 // maybe interpreter initialization ?
289}
290
292{
293 if (update_weights)
294 {
295 // TOOD: micro support update_weights ???
296 // Here we use this flag for distinguish inference and train in trainaing interpreter
297 _train_interpreter->trainSingleStep(_config);
300 }
301 else
302 {
303 // TODO: support multiple input/output
304 assert(outputbuf != nullptr);
305 _train_interpreter->allocateInputs();
306 float *allocated_input_data = (float *)_train_interpreter->getInputDataAt(0);
307 float *user_input_data = (float *)_train_interpreter->getInputData(0);
308 memcpy(allocated_input_data, user_input_data,
309 sizeof(float) * _train_interpreter->getInputSizeAt(0));
310 _train_interpreter->run(_config);
311 float *calculated_ptr = (float *)_train_interpreter->getOutputDataAt(0);
312 memcpy(outputbuf, calculated_ptr, sizeof(float) * _train_interpreter->getOutputSizeAt(0));
313 _train_interpreter->reset();
314 }
316}
317
319{
320 _train_interpreter->saveModel(_config, path);
322}
323
325{
326 _train_interpreter->saveCheckpoint(_config, path);
328}
329
331{
332 _train_interpreter->loadCheckpoint(_config, path);
334}
335
336// TODO: onert's this function takes const type input
337NNFW_STATUS nnfw_session::train_set_input(uint32_t index, void *input)
338{
339 _train_interpreter->setInput((uint8_t *)input, index);
341}
342
343// TODO: onert's this function takes const type expected
344NNFW_STATUS nnfw_session::train_set_expected(uint32_t index, void *expected)
345{
346 _train_interpreter->setTarget((uint8_t *)expected, index);
348}
349
350NNFW_STATUS nnfw_session::train_set_output(uint32_t index, NNFW_TYPE type, void *buffer,
351 size_t length)
352{
353 outputbuf = (uint8_t *)buffer;
355}
356
358{
359 _config.training_context.learning_rate = info->learning_rate;
360 _config.training_context.batch_size = info->batch_size;
363 _config.training_context.beta = info->adam_opt.beta;
364 _config.training_context.beta_squares = info->adam_opt.beta2;
365 _config.training_context.beta = info->adam_opt.epsilon;
366 _config.training_context.num_of_train_layers = info->num_trainble_ops;
368}
369
370NNFW_STATUS nnfw_session::train_get_loss(uint32_t index, float *loss)
371{
373 switch (_config.training_context.loss)
374 {
377 break;
378 default:
380 break;
381 }
382
383 _train_interpreter->evaluateMetric(_config, m, reinterpret_cast<void *>(loss),
386}
387
388// onert-micr.h implementation
389
391
392NNFW_STATUS nnfw_load_model_from_file(nnfw_session *session, const char *package_file_path)
393{
394 return session->load_model_from_file(package_file_path);
395}
396
397NNFW_STATUS nnfw_train_prepare(nnfw_session *session) { return session->train_prepare(); }
398
399NNFW_STATUS nnfw_train(nnfw_session *session, bool update_weights)
400{
401 return session->train_run(update_weights);
402}
403
405{
406 return session->train_export_circle(path);
407}
408
410{
411 return session->train_export_checkpoint(path);
412}
413
415{
416 return session->train_import_checkpoint(path);
417}
418
419NNFW_STATUS nnfw_train_set_input(nnfw_session *session, uint32_t index, void *input,
420 const nnfw_tensorinfo *input_info)
421{
423 return session->train_set_input(index, input);
424}
425
426NNFW_STATUS nnfw_train_set_expected(nnfw_session *session, uint32_t index, void *expected,
427 const nnfw_tensorinfo *expected_info)
428{
430 return session->train_set_expected(index, expected);
431}
432
433NNFW_STATUS nnfw_train_get_loss(nnfw_session *session, uint32_t index, float *loss)
434{
436 return session->train_get_loss(index, loss);
437}
438
440{
442 return session->train_set_traininfo(info);
443}
444
446 void *buffer, size_t length)
447{
449 return session->train_set_output(index, type, buffer, length);
450}
uint32_t getOutputSizeAt(uint32_t position)
OMStatus run(const OMConfig &config)
OMStatus saveModel(const OMConfig &config, const char *save_path)
void setTarget(uint8_t *data, uint32_t target_index)
OMStatus importTrainModel(char *model_ptr, const OMConfig &config)
void setInput(uint8_t *data, uint32_t input_index)
uint32_t getInputSizeAt(uint32_t position)
OMStatus evaluateMetric(const OMConfig &config, OMMetrics metric, void *metric_val, uint32_t test_size)
OMStatus trainSingleStep(OMConfig &config)
OMStatus loadCheckpoint(OMConfig &config, const char *load_path)
OMStatus saveCheckpoint(const OMConfig &config, const char *save_path)
volatile const char info[]
@ CROSS_ENTROPY
Definition OMConfig.h:54
@ CROSS_ENTROPY_METRICS
Definition OMConfig.h:42
NNFW_STATUS nnfw_create_session(nnfw_session **session)
Create a new session instance.
NNFW_STATUS nnfw_train_get_loss(nnfw_session *session, uint32_t index, float *loss)
Get loss value for expected output.
#define NNFW_RETURN_ERROR_IF_NULL(p)
NNFW_STATUS nnfw_train_export_checkpoint(nnfw_session *session, const char *path)
Export circle checkpoint.
NNFW_STATUS nnfw_load_model_from_file(nnfw_session *session, const char *package_file_path)
Load model from nnpackage file or directory.
NNFW_STATUS nnfw_train_prepare(nnfw_session *session)
Prepare session to be ready for training.
NNFW_STATUS nnfw_train(nnfw_session *session, bool update_weights)
Train the model.
NNFW_STATUS nnfw_train_set_traininfo(nnfw_session *session, const nnfw_train_info *info)
Set training information.
DataBuffer readFile(const char *path)
NNFW_STATUS nnfw_train_set_input(nnfw_session *session, uint32_t index, void *input, const nnfw_tensorinfo *input_info)
Set training input.
NNFW_STATUS nnfw_train_set_output(nnfw_session *session, uint32_t index, NNFW_TYPE type, void *buffer, size_t length)
Set training output buffer.
NNFW_STATUS nnfw_train_import_checkpoint(nnfw_session *session, const char *path)
Import circle checkpoint.
NNFW_STATUS nnfw_train_set_expected(nnfw_session *session, uint32_t index, void *expected, const nnfw_tensorinfo *expected_info)
Set training expected output.
NNFW_STATUS nnfw_train_export_circle(nnfw_session *session, const char *path)
Export current training model into circle model.
std::vector< char > DataBuffer
NNFW_STATUS
Result values returned from a call to an API function.
Definition onert-micro.h:86
@ NNFW_STATUS_UNEXPECTED_NULL
Definition onert-micro.h:95
@ NNFW_STATUS_NO_ERROR
Definition onert-micro.h:88
@ NNFW_STATUS_ERROR
Definition onert-micro.h:93
@ NNFW_TRAIN_OPTIMIZER_ADAM
NNFW_TYPE
Definition onert-micro.h:75
NNFW_STATUS train_prepare()
NNFW_STATUS train_set_output(uint32_t index, NNFW_TYPE type, void *buffer, size_t length)
NNFW_STATUS train_set_expected(uint32_t index, void *expected)
NNFW_STATUS train_run(bool update_weights)
NNFW_STATUS train_export_checkpoint(const char *path)
NNFW_STATUS train_import_checkpoint(const char *path)
NNFW_STATUS train_expected_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
NNFW_STATUS train_set_input(uint32_t index, void *input)
NNFW_STATUS train_get_loss(uint32_t index, float *loss)
NNFW_STATUS load_model_from_file(const char *package_file_path)
NNFW_STATUS train_input_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
NNFW_STATUS train_export_circle(const char *path)
static NNFW_STATUS create(nnfw_session **session)
Factory method. It creates and initialize nnfw_session.
NNFW_STATUS train_set_traininfo(const nnfw_train_info *info)
tensor info describes the type and shape of tensors
Training information to prepare training.
OMTrainingContext training_context
Definition OMConfig.h:107
OMTrainOptimizer optimizer
Definition OMConfig.h:78