// // liteConverter.cpp // MNNConverter // // Created by MNN on 2019/01/31. // Copyright © 2018, Alibaba Group Holding Limited // #include #include #include "logkit.h" #include "flatbuffers/idl.h" #include "flatbuffers/minireflect.h" #include "flatbuffers/util.h" #include "liteConverter.hpp" #include "liteOpConverter.hpp" class TfliteModel { public: TfliteModel() = delete; TfliteModel(const std::string fileName); ~TfliteModel(); void readModel(); inline std::unique_ptr& get(); private: const std::string _modelName; std::unique_ptr _tfliteModel; }; static MNN::DataType _dataTypeMap(tflite::TensorType type) { switch (type) { case tflite::TensorType_FLOAT32: return MNN::DataType_DT_FLOAT; break; case tflite::TensorType_INT32: return MNN::DataType_DT_INT32; break; case tflite::TensorType_UINT8: return MNN::DataType_DT_UINT8; break; default: return MNN::DataType_DT_FLOAT; break; } } bool dumpTflite2Json(const char* modelFile, const char* jsonFile) { std::ifstream inputFile(modelFile, std::ios::binary); inputFile.seekg(0, std::ios::end); auto size = inputFile.tellg(); inputFile.seekg(0, std::ios::beg); char* buffer = new char[size]; inputFile.read((char*)buffer, size); flatbuffers::Verifier verify((uint8_t*)buffer, size); if (!tflite::VerifyModelBuffer(verify)) { LOG(FATAL) << "TFlite model version ERROR!"; return false; } std::ofstream output(jsonFile); auto s = flatbuffers::FlatBufferToString((const uint8_t*)buffer, tflite::ModelTypeTable()); output << s; delete[] buffer; return true; } static void _converteConstantDataToMNNConstantNode( int tensorIndex, const std::vector>& tfliteTensors, const std::vector>& tfliteModelBuffers, std::unique_ptr& MNNNetT) { // check whether buffer data size is greater than zero, // if size > 0, then this tensor is Constant, convete this tensor to be MNN Constant node const auto& tensor = tfliteTensors[tensorIndex]; const uint32_t bufferIndex = tensor->buffer; const auto tensorBuffer = tfliteModelBuffers[bufferIndex]->data; const auto bufferSize = tensorBuffer.size(); if (bufferSize == 0) return; // this is Constant data std::unique_ptr mnnConstantOp(new MNN::OpT); mnnConstantOp->name = tensor->name; mnnConstantOp->type = MNN::OpType_Const; mnnConstantOp->main.type = MNN::OpParameter_Blob; mnnConstantOp->outputIndexes.push_back(tensorIndex); std::unique_ptr mnnBlob(new MNN::BlobT); // TODO, map tflite data type to mnn data type mnnBlob->dataType = _dataTypeMap(tensor->type); mnnBlob->dataFormat = MNN::MNN_DATA_FORMAT_NHWC; mnnBlob->dims = tensor->shape; if (mnnBlob->dataType == MNN::DataType_DT_FLOAT) { mnnBlob->float32s.resize(bufferSize / 4); memcpy(mnnBlob->float32s.data(), tensorBuffer.data(), bufferSize); } else if (mnnBlob->dataType == MNN::DataType_DT_INT32) { mnnBlob->int32s.resize(bufferSize / 4); memcpy(mnnBlob->int32s.data(), tensorBuffer.data(), bufferSize); } else { DCHECK(false) << "TODO support other data type!"; } mnnConstantOp->main.value = mnnBlob.release(); MNNNetT->tensorName.emplace_back(mnnConstantOp->name); MNNNetT->oplists.emplace_back(std::move(mnnConstantOp)); } template void convert(const SRC* s, DST* d, size_t sizeInBytes) { auto size = sizeInBytes / sizeof(SRC); for (size_t i=0; i _getConvertFunction(tflite::TensorType type) { switch (type) { case tflite::TensorType_FLOAT64: return [](const void* s, void* d, size_t size) { convert((double*)s, (float*)d, size); }; case tflite::TensorType_UINT64: return [](const void* s, void* d, size_t size) { convert((uint64_t*)s, (int32_t*)d, size); }; case tflite::TensorType_INT16: return [](const void* s, void* d, size_t size) { convert((int16_t*)s, (int32_t*)d, size); }; case tflite::TensorType_INT64: return [](const void* s, void* d, size_t size) { convert((int64_t*)s, (int32_t*)d, size); }; default: break; } return nullptr; } static MNN::DataType _convertType(tflite::TensorType type) { if (type == tflite::TensorType_FLOAT32) { return MNN::DataType_DT_FLOAT; } if (type == tflite::TensorType_FLOAT64) { return MNN::DataType_DT_FLOAT; } if (type == tflite::TensorType_INT8) { return MNN::DataType_DT_INT8; } if (type == tflite::TensorType_INT16) { return MNN::DataType_DT_INT32; } if (type == tflite::TensorType_INT32) { return MNN::DataType_DT_INT32; } if (type == tflite::TensorType_INT64) { return MNN::DataType_DT_INT32; } if (type == tflite::TensorType_UINT8) { return MNN::DataType_DT_UINT8; } if (type == tflite::TensorType_UINT64) { return MNN::DataType_DT_INT32; } if (type == tflite::TensorType_FLOAT16) { return MNN::DataType_DT_HALF; } return MNN::DataType_DT_INVALID; } static bool needExtractInput(uint32_t opCode) { #define NONEED(x) if (x == opCode) return false; NONEED(tflite::BuiltinOperator_CONV_2D); NONEED(tflite::BuiltinOperator_DEPTHWISE_CONV_2D); NONEED(tflite::BuiltinOperator_SPLIT); NONEED(tflite::BuiltinOperator_CONCATENATION); NONEED(tflite::BuiltinOperator_CONV_2D); NONEED(tflite::BuiltinOperator_RESIZE_BILINEAR); NONEED(tflite::BuiltinOperator_RESIZE_NEAREST_NEIGHBOR); NONEED(tflite::BuiltinOperator_SOFTMAX); return true; } int tflite2MNNNet(const std::string inputModel, const std::string bizCode, std::unique_ptr& MNNNetT) { const std::string model_name = inputModel; auto model = std::shared_ptr(new TfliteModel(model_name)); model->readModel(); auto& tfliteModel = model->get(); const auto& tfliteOpSet = tfliteModel->operator_codes; // const auto operatorCodesSize = tfliteOpSet.size(); const auto subGraphsSize = tfliteModel->subgraphs.size(); const auto& tfliteModelBuffer = tfliteModel->buffers; // check whether this tflite model is quantization model // use the weight's data type of Conv2D|DepthwiseConv2D to decide quantizedModel mode int quantizedModel = 0; for (int i = 0; i < subGraphsSize; ++i) { const auto& ops = tfliteModel->subgraphs[i]->operators; const auto& tensors = tfliteModel->subgraphs[i]->tensors; const int opNums = static_cast(ops.size()); for (int j = 0; j < opNums; ++j) { const int opcodeIndex = ops[j]->opcode_index; auto opCode = liteOpConverter:: getOpCode(tfliteOpSet[opcodeIndex].get()); if (opCode == tflite::BuiltinOperator_CONV_2D || opCode == tflite::BuiltinOperator_DEPTHWISE_CONV_2D || opCode == tflite::BuiltinOperator_TRANSPOSE_CONV) { const int weightIndex = ops[j]->inputs[1]; const auto& weightTensor = tensors[weightIndex]; if (weightTensor->type == tflite::TensorType_UINT8) { quantizedModel = 1; } else if (weightTensor->type == tflite::TensorType_INT8) { quantizedModel = 2; } } } } auto& buffers = tfliteModel->buffers; for (int i = 0; i < subGraphsSize; ++i) { const auto& ops = tfliteModel->subgraphs[i]->operators; const auto& tensors = tfliteModel->subgraphs[i]->tensors; // set const std::vector extractedTensors(tfliteModel->subgraphs[i]->tensors.size(), false); // set input for (const auto index : tfliteModel->subgraphs[i]->inputs) { MNN::OpT* inputOp = new MNN::OpT; const auto& inputTensor = tensors[index]; inputOp->name = inputTensor->name; inputOp->type = MNN::OpType_Input; inputOp->main.type = MNN::OpParameter_Input; auto inputParam = new MNN::InputT; inputParam->dformat = MNN::MNN_DATA_FORMAT_NHWC; inputParam->dims = inputTensor->shape; inputParam->dtype = _convertType(inputTensor->type); inputOp->main.value = inputParam; inputOp->outputIndexes.push_back(index); MNNNetT->oplists.emplace_back(inputOp); } // set output names for (int k = 0; k < tfliteModel->subgraphs[i]->outputs.size(); ++k) { MNNNetT->outputName.push_back(tensors[tfliteModel->subgraphs[i]->outputs[k]]->name); } // tensor names for (const auto& tensor : tensors) { MNNNetT->tensorName.push_back(tensor->name); } const int opNums = ops.size(); for (int j = 0; j < opNums; ++j) { const int opcodeIndex = ops[j]->opcode_index; auto opCode = liteOpConverter:: getOpCode(tfliteOpSet[opcodeIndex].get()); if (needExtractInput(opCode)) { for (auto input : ops[j]->inputs) { if (input < 0 || extractedTensors[input]) { continue; } extractedTensors[input] = true; auto& tensor = tfliteModel->subgraphs[i]->tensors[input]; auto& buffer = buffers[tensor->buffer]; if (buffer->data.empty()) { continue; } std::unique_ptr newOp(new MNN::OpT); newOp->type = MNN::OpType_Const; newOp->name = tensor->name; newOp->outputIndexes = {input}; newOp->main.type = MNN::OpParameter_Blob; newOp->main.value = new MNN::BlobT; auto blob = newOp->main.AsBlob(); blob->dims = tensor->shape; blob->dataFormat = MNN::MNN_DATA_FORMAT_NHWC; blob->dataType = _convertType(tensor->type); if (MNN::DataType_DT_INVALID == blob->dataType) { MNN_ERROR("Don't support tensor type for %s\n", tflite::EnumNameTensorType(tensor->type)); MNNNetT.reset(); return 0; } int size = 1; for (auto s : blob->dims) { size *= s; } void* dst = nullptr; switch (blob->dataType) { case MNN::DataType_DT_FLOAT: blob->float32s.resize(size); dst = blob->float32s.data(); break; case MNN::DataType_DT_INT32: blob->int32s.resize(size); dst = blob->int32s.data(); break; case MNN::DataType_DT_INT8: blob->int8s.resize(size); dst = blob->int8s.data(); break; case MNN::DataType_DT_UINT8: blob->uint8s.resize(size); dst = blob->uint8s.data(); break; case MNN::DataType_DT_HALF: blob->uint8s.resize(size * 2); dst = blob->uint8s.data(); break; default: break; } auto func = _getConvertFunction(tensor->type); if (nullptr == func) { ::memcpy(dst, buffer->data.data(), buffer->data.size()); } else { func(buffer->data.data(), dst, buffer->data.size()); } MNNNetT->oplists.emplace_back(std::move(newOp)); } } if (opCode == tflite::BuiltinOperator_CUSTOM) { const int inputSize = ops[j]->inputs.size(); for (int k = 0; k < inputSize; ++k) { _converteConstantDataToMNNConstantNode(ops[j]->inputs[k], tensors, tfliteModelBuffer, MNNNetT); } } MNN::OpT* op = new MNN::OpT; auto creator = liteOpConverterSuit::get()->search(opCode); DCHECK(creator) << "NOT_SUPPORTED_OP: [ " << tflite::EnumNameBuiltinOperator(opCode) << " ]"; if (nullptr == creator) { // Has error, reset net MNNNetT.reset(); return 0; } // tflite op to MNN op op->name = tensors[ops[j]->outputs[0]]->name; op->type = creator->opType(quantizedModel); op->main.type = creator->type(quantizedModel); // set default input output index auto insertQuantinfo = [&](int idx) { if (quantizedModel != 2) { return; } if (tensors[idx]->type != tflite::TensorType_INT8) { return; } auto quant = tensors[idx]->quantization.get(); if (!quant) { return; } std::unique_ptr tensorDescribe(new MNN::TensorDescribeT); tensorDescribe->index = idx; tensorDescribe->name = MNNNetT->tensorName[idx]; tensorDescribe->quantInfo.reset(new MNN::TensorQuantInfoT); tensorDescribe->quantInfo->type = MNN::DataType_DT_INT8; tensorDescribe->quantInfo->scale = quant->scale[0]; tensorDescribe->quantInfo->zero = quant->zero_point[0]; MNNNetT->extraTensorDescribe.emplace_back(std::move(tensorDescribe)); }; op->inputIndexes.clear(); op->outputIndexes.clear(); for (int i = 0; i < ops[j]->inputs.size(); i++) { if (ops[j]->inputs[i] >= 0) { op->inputIndexes.emplace_back(ops[j]->inputs[i]); } } for (int i = 0; i < ops[j]->outputs.size(); i++) { if (ops[j]->outputs[i] >= 0) { op->outputIndexes.emplace_back(ops[j]->outputs[i]); insertQuantinfo(ops[j]->outputs[i]); } } // Run actual conversion creator->run(op, ops[j], tensors, tfliteModelBuffer, tfliteOpSet, quantizedModel); if (op->type == MNN::OpType_MAX) { // Has error, reset net MNNNetT.reset(); return 0; } MNNNetT->oplists.emplace_back(op); } } MNNNetT->sourceType = MNN::NetSource_TFLITE; MNNNetT->bizCode = bizCode; return 0; } TfliteModel::TfliteModel(const std::string fileName) : _modelName(fileName) { } TfliteModel::~TfliteModel() { } void TfliteModel::readModel() { std::ifstream inputFile(_modelName, std::ios::binary); inputFile.seekg(0, std::ios::end); const auto size = inputFile.tellg(); inputFile.seekg(0, std::ios::beg); char* buffer = new char[size]; inputFile.read(buffer, size); inputFile.close(); // verify model flatbuffers::Verifier verify((uint8_t*)buffer, size); if (!tflite::VerifyModelBuffer(verify)) { LOG(FATAL) << "TFlite model version ERROR!"; } _tfliteModel = tflite::UnPackModel(buffer); delete[] buffer; } std::unique_ptr& TfliteModel::get() { return _tfliteModel; }