#include "ir/NNPkg.h"
#include "ir/OpCode.h"
#include "util/TracingCtx.h"
+#include "odc/QuantizeManager.h"
#include <fstream>
#include <iostream>
return onert::ir::Layout::UNKNOWN;
}
-NNFW_STATUS getTensorIndexImpl(const onert::ir::Graph &graph, const char *tensorname,
+NNFW_STATUS getTensorIndexImpl(const onert::ir::IGraph &graph, const char *tensorname,
uint32_t *index, bool is_input)
{
if (!tensorname || !index)
return std::unique_ptr<onert::ir::Model>(nullptr);
}
+#ifdef ONERT_TRAIN
+uint64_t getBufSize(const nnfw_tensorinfo *info)
+{
+ static int elmsize[] = {
+ sizeof(float), /* NNFW_TYPE_TENSOR_FLOAT32 = 0 */
+ sizeof(int), /* NNFW_TYPE_TENSOR_INT32 = 1 */
+ sizeof(uint8_t), /* NNFW_TYPE_TENSOR_QUANT8_ASYMM = 2 */
+ sizeof(bool), /* NNFW_TYPE_TENSOR_BOOL = 3 */
+ sizeof(uint8_t), /* NNFW_TYPE_TENSOR_UINT8 = 4 */
+ sizeof(int64_t), /* NNFW_TYPE_TENSOR_INT64 = 5 */
+ sizeof(int8_t), /* NNFW_TYPE_TENSOR_QUANT8_ASYMM_SIGNED = 6 */
+ sizeof(int16_t), /* NNFW_TYPE_TENSOR_QUANT16_SYMM_SIGNED = 7 */
+ };
+
+ uint64_t n = 1;
+ for (int32_t i = 0; i < info->rank; ++i)
+ {
+ assert(info->dims[i] >= 0);
+ n *= info->dims[i];
+ }
+ return elmsize[info->dtype] * n;
+}
+#endif // ONERT_TRAIN
} // namespace
nnfw_session::nnfw_session()
: _nnpkg{nullptr}, _coptions{}, _compiler_artifact{nullptr}, _execution{nullptr},
- _kernel_registry{nullptr}
+ _kernel_registry{nullptr}, _quant_manager{nullptr}
{
// DO NOTHING
}
return NNFW_STATUS_UNEXPECTED_NULL;
}
+ // Create quantize manager
+ _quant_manager = std::make_unique<onert::odc::QuantizeManager>(std::string(model_file_path));
+
std::string filename{model_file_path};
// TODO: Use std::filesystem::path when we can use c++17.
auto dotidx = filename.find_last_of('.');
return NNFW_STATUS_ERROR;
}
+ // Create quantize manager
+ // TODO Support multiple models
+ auto const model_filename = package_path + std::string("/") + models[0].asString();
+ _quant_manager = std::make_unique<onert::odc::QuantizeManager>(model_filename);
+
for (uint16_t i = 0; i < num_models; ++i)
{
auto model_file_path = package_path + std::string("/") + models[i].asString();
auto model = loadModel(model_file_path, model_type);
if (model == nullptr)
return NNFW_STATUS_ERROR;
- model->primary_subgraph()->bindKernelBuilder(_kernel_registry->getBuilder());
+ model->bindKernelBuilder(_kernel_registry->getBuilder());
_nnpkg->push(onert::ir::ModelIndex{i}, std::move(model));
_coptions.push_back(onert::compiler::CompilerOptions::fromGlobalConfig());
}
{
// In this case, if we apply input shape, it will propagate after compilation and excution
- auto &info = _nnpkg->inputInfo(index);
- info.shape(new_shape);
+ _nnpkg->changeInputShape(index, new_shape);
}
else // when called after nnfw_session::prepare()
_execution->changeInputShape(onert::ir::IOIndex(index), new_shape);
return NNFW_STATUS_NO_ERROR;
}
-const onert::ir::Graph *nnfw_session::primary_subgraph()
+const onert::ir::IGraph *nnfw_session::primary_subgraph()
{
if (_nnpkg != nullptr)
{
return NNFW_STATUS_NO_ERROR;
}
+
+#ifdef ONERT_TRAIN
+NNFW_STATUS nnfw_session::train_prepare(const nnfw_train_info *info)
+{
+ // We may need different state to represent training model is loaded
+ if (!isStateModelLoaded())
+ {
+ std::cerr << "Error during model prepare training: ";
+ if (_state == State::PREPARED_TRAINING)
+ std::cerr << "prepare should be run once";
+ else
+ std::cerr << "invalid state";
+ std::cerr << std::endl;
+ return NNFW_STATUS_INVALID_STATE;
+ }
+
+ try
+ {
+ nnfw_train_info tinfo;
+ if (info != nullptr)
+ {
+ tinfo = *info;
+ }
+
+ auto convertLossType = [](const int &type) {
+ if (type == NNFW_TRAIN_LOSS_MEAN_SQUARED_ERROR)
+ return onert::ir::operation::Loss::Type::MEAN_SQUARED_ERROR;
+ if (type == NNFW_TRAIN_LOSS_CATEGORICAL_CROSSENTROPY)
+ return onert::ir::operation::Loss::Type::CATEGORICAL_CROSSENTROPY;
+ else
+ throw std::runtime_error("not supported loss type");
+ };
+ onert::compiler::train::LossInfo loss_info;
+ loss_info.type = convertLossType(tinfo.loss);
+
+ auto convertOptType = [](const int &type) {
+ if (type == NNFW_TRAIN_OPTIMIZER_SGD)
+ return onert::exec::train::optimizer::OptimizerCode::SGD;
+ else if (type == NNFW_TRAIN_OPTIMIZER_ADAM)
+ return onert::exec::train::optimizer::OptimizerCode::Adam;
+ else
+ throw std::runtime_error("not supported optimizer type");
+ };
+ onert::compiler::train::OptimizerInfo opt_info;
+ opt_info.learning_rate = tinfo.learning_rate;
+ opt_info.optim_code = convertOptType(tinfo.opt);
+
+ onert::compiler::train::TrainingInfo training_info;
+ training_info.setBatchSize(tinfo.batch_size);
+ training_info.setLossInfo(loss_info);
+ training_info.setOptimizerInfo(opt_info);
+
+ auto compiler =
+ onert::compiler::CompilerFactory::get().create(_nnpkg, _coptions, &training_info);
+ _nnpkg.reset();
+ _compiler_artifact = compiler->compile();
+ _execution = std::make_unique<onert::exec::Execution>(_compiler_artifact->_executors);
+ }
+ catch (const std::exception &e)
+ {
+ std::cerr << "Error during nnfw_session::train_prepare : " << e.what() << std::endl;
+ return NNFW_STATUS_ERROR;
+ }
+
+ _state = State::PREPARED_TRAINING;
+ return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_input_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
+{
+ if (!isStatePreparedOrFinishedTraining())
+ {
+ std::cerr << "Error during nnfw_session::train_input_tensorinfo : invalid state" << std::endl;
+ return NNFW_STATUS_INVALID_STATE;
+ }
+
+ // Check index is valid: [0, getInputSize())
+
+ // NYI
+ (void)index;
+ (void)ti;
+ return NNFW_STATUS_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_expected_tensorinfo(uint32_t index, nnfw_tensorinfo *ti)
+{
+ if (!isStatePreparedOrFinishedTraining())
+ {
+ std::cerr << "Error during nnfw_session::train_expected_tensorinfo : invalid state"
+ << std::endl;
+ return NNFW_STATUS_INVALID_STATE;
+ }
+
+ // Check index is valid: [0, getExpectedSize())
+
+ // NYI
+ (void)index;
+ (void)ti;
+ return NNFW_STATUS_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_set_input(uint32_t index, const void *input,
+ const nnfw_tensorinfo *input_tensorinfo)
+{
+ if (input == nullptr)
+ {
+ std::cerr << "Error during nnfw_session::train_set_input : input buffer is null" << std::endl;
+ return NNFW_STATUS_UNEXPECTED_NULL;
+ }
+
+ if (!isStatePreparedOrFinishedTraining())
+ {
+ std::cerr << "Error during nnfw_session::train_set_input : invalid state" << std::endl;
+ return NNFW_STATUS_INVALID_STATE;
+ }
+
+ if (index >= getInputSize())
+ {
+ std::cerr << "Error during nnfw_session::train_set_input : index is out of range" << std::endl;
+ return NNFW_STATUS_ERROR;
+ }
+
+ try
+ {
+ auto ind = onert::ir::IOIndex(index);
+ auto size = _execution->getInputTotalSize(ind);
+ if (input_tensorinfo && getBufSize(input_tensorinfo) != size)
+ {
+ std::cerr
+ << "Error during nnfw_session::train_set_input : not supporeted to change tensorinfo"
+ << std::endl;
+ return NNFW_STATUS_ERROR;
+ }
+
+ _execution->setInput(ind, input, size);
+ }
+ catch (const std::exception &e)
+ {
+ std::cerr << "Error during nnfw_session::train_set_input : " << e.what() << std::endl;
+ return NNFW_STATUS_ERROR;
+ }
+
+ return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_set_expected(uint32_t index, const void *expected,
+ const nnfw_tensorinfo *expected_tensorinfo)
+{
+ if (expected == nullptr)
+ {
+ std::cerr << "Error during nnfw_session::train_set_expected : expected buffer is null"
+ << std::endl;
+ return NNFW_STATUS_UNEXPECTED_NULL;
+ }
+
+ if (!isStatePreparedOrFinishedTraining())
+ {
+ std::cerr << "Error during nnfw_session::train_set_expected : invalid state" << std::endl;
+ return NNFW_STATUS_INVALID_STATE;
+ }
+
+ if (index >= getOutputSize())
+ {
+ std::cerr << "Error during nnfw_session::train_set_expected : index is out of range"
+ << std::endl;
+ return NNFW_STATUS_ERROR;
+ }
+
+ try
+ {
+ auto output_ind = onert::ir::IOIndex(index);
+ auto size = _execution->getOutputTotalSize(output_ind);
+ if (expected_tensorinfo && getBufSize(expected_tensorinfo) != size)
+ {
+ std::cerr << "Error during nnfw_session::train_set_expected : invalid tensorinfo"
+ << std::endl;
+ return NNFW_STATUS_ERROR;
+ }
+
+ // NOTE Find the loss input index
+ // Input is added as many as the number of outputs.
+ // The loss index is calculated from the value obtained by subtracting the
+ // total output(added loss input) from the total input size.
+ auto input_index = getInputSize() - getOutputSize() + index;
+ auto input_ind = onert::ir::IOIndex(input_index);
+ _execution->setInput(input_ind, expected, size);
+ }
+ catch (const std::exception &e)
+ {
+ std::cerr << "Error during nnfw_session::train_set_expected : " << e.what() << std::endl;
+ return NNFW_STATUS_ERROR;
+ }
+
+ return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_run(bool update_weights)
+{
+ if (!isStatePreparedOrFinishedTraining())
+ {
+ std::cerr << "Error during nnfw_session::train_run : invalid state" << std::endl;
+ return NNFW_STATUS_INVALID_STATE;
+ }
+
+ try
+ {
+ if (update_weights)
+ {
+ _execution->train(_training_step++);
+ }
+ else
+ _execution->execute();
+ }
+ catch (const onert::InsufficientBufferSizeException &e)
+ {
+ // Currently insufficient buffer always means output buffer.
+ std::cerr << "Error during nnfw_session::train_run : " << e.what() << std::endl;
+ return NNFW_STATUS_INSUFFICIENT_OUTPUT_SIZE;
+ }
+ catch (const std::exception &e)
+ {
+ std::cerr << "Error during nnfw_session::train_run : " << e.what() << std::endl;
+ return NNFW_STATUS_ERROR;
+ }
+
+ _state = State::FINISHED_TRAINING;
+ return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_get_loss(uint32_t index, float *loss)
+{
+ if (loss == nullptr)
+ {
+ std::cerr << "Error during nnfw_session::train_get_loss : loss is null" << std::endl;
+ return NNFW_STATUS_UNEXPECTED_NULL;
+ }
+
+ if (!isStateFinishedTraining())
+ {
+ std::cerr << "Error during nnfw_session::train_get_loss : invalid state" << std::endl;
+ return NNFW_STATUS_INVALID_STATE;
+ }
+
+ if (index >= getOutputSize())
+ {
+ std::cerr << "Error during nnfw_session::train_get_loss : index is out of range" << std::endl;
+ return NNFW_STATUS_ERROR;
+ }
+
+ try
+ {
+ auto ind = onert::ir::IOIndex(index);
+ *loss = _execution->getLoss(ind);
+ }
+ catch (const std::exception &e)
+ {
+ std::cerr << "Error during nnfw_session::train_get_loss : " << e.what() << std::endl;
+ return NNFW_STATUS_ERROR;
+ }
+
+ return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::train_export_circle(const char *path)
+{
+ if (path == nullptr)
+ {
+ std::cerr << "Error during nnfw_session::train_export_circle : path is null" << std::endl;
+ return NNFW_STATUS_UNEXPECTED_NULL;
+ }
+
+ // Check training mode is enabled
+ if (!isStateFinishedTraining())
+ {
+ std::cerr << "Error during nnfw_session::train_export_circle : invalid state" << std::endl;
+ return NNFW_STATUS_INVALID_STATE;
+ }
+
+ // NYI
+ return NNFW_STATUS_ERROR;
+}
+
+bool nnfw_session::isStatePreparedTraining()
+{
+ if (_state == State::PREPARED_TRAINING)
+ {
+ assert(_nnpkg == nullptr);
+ assert(!_coptions.empty());
+ assert(_execution != nullptr);
+ return true;
+ }
+ else
+ return false;
+}
+
+bool nnfw_session::isStateFinishedTraining()
+{
+ if (_state == State::FINISHED_TRAINING)
+ {
+ assert(_nnpkg == nullptr);
+ assert(!_coptions.empty());
+ assert(_execution != nullptr);
+ return true;
+ }
+ else
+ return false;
+}
+
+bool nnfw_session::isStatePreparedOrFinishedTraining()
+{
+ return isStatePreparedTraining() || isStateFinishedTraining();
+}
+
+#endif // ONERT_TRAIN
+
+NNFW_STATUS nnfw_session::set_quantization_type(NNFW_QUANTIZE_TYPE qtype)
+{
+ try
+ {
+ if (!isStateModelLoaded())
+ {
+ std::cerr << "invalid state" << std::endl;
+ return NNFW_STATUS_INVALID_STATE;
+ }
+
+ bool is_q16 = false;
+ switch (qtype)
+ {
+ case NNFW_QUANTIZE_TYPE_U8_ASYM:
+ break;
+ case NNFW_QUANTIZE_TYPE_I16_SYM:
+ is_q16 = true;
+ break;
+ default:
+ return NNFW_STATUS_INVALID_STATE;
+ }
+ _quant_manager->quantizeType(is_q16);
+ }
+ catch (const std::exception &e)
+ {
+ std::cerr << "Error during nnfw_session::set_quantization_type : " << e.what() << std::endl;
+ return NNFW_STATUS_ERROR;
+ }
+
+ return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::set_quantized_model_path(const char *path)
+{
+ try
+ {
+ if (!isStateModelLoaded())
+ {
+ std::cerr << "invalid state" << std::endl;
+ return NNFW_STATUS_INVALID_STATE;
+ }
+
+ _quant_manager->exportModelPath(std::string(path));
+ }
+ catch (const std::exception &e)
+ {
+ std::cerr << "Error during nnfw_session::set_quantized_model_path : " << e.what() << std::endl;
+ return NNFW_STATUS_ERROR;
+ }
+
+ return NNFW_STATUS_NO_ERROR;
+}
+
+NNFW_STATUS nnfw_session::quantize()
+{
+ try
+ {
+ if (!isStateModelLoaded())
+ {
+ std::cerr << "invalid state" << std::endl;
+ return NNFW_STATUS_INVALID_STATE;
+ }
+
+ auto result = _quant_manager->quantize();
+ if (!result)
+ return NNFW_STATUS_INVALID_STATE;
+
+ // Replace model
+ // TODO Support buffer replace, not file reload
+ auto model = loadModel(_quant_manager->exportModelPath(), "circle");
+ if (model == nullptr)
+ return NNFW_STATUS_ERROR;
+ _nnpkg->replaceModel(std::move(model));
+ }
+ catch (const std::exception &e)
+ {
+ std::cerr << "Error during nnfw_session::quantize : " << e.what() << std::endl;
+ return NNFW_STATUS_ERROR;
+ }
+
+ return NNFW_STATUS_NO_ERROR;
+}