// // convertToStaticModel.cpp // MNNConverter // // Created by MNN on 2020/09/03. // Copyright © 2018, Alibaba Group Holding Limited // #include #include #include "MNN_generated.h" #include "core/TensorUtils.hpp" #include "core/FileLoader.hpp" #include "utils/InitNet.hpp" #include "core/Command.hpp" #include "shape/SizeComputer.hpp" #include "geometry/GeometryComputer.hpp" #include "geometry/GeometryComputerUtils.hpp" #include "CommonUtils.hpp" #include #include using namespace MNN; #define SET_TYPE(TYPE, type) \ if (tensor->getType() == halide_type_of()) {\ blob->dataType = DataType_DT_##TYPE; #define CONSTANT_COPY(TYPE, type, bytes) \ SET_TYPE(TYPE, type)\ blob->type##s.resize(tensor->elementSize());\ ::memcpy(blob->type##s.data(), tensor->host(), blob->type##s.size() * bytes);\ } static bool _RemoveDupOutput(MNN::NetT* net, bool abortOpt) { std::vector outputMask(net->tensorName.size(), false); std::map describes; for (auto& des : net->extraTensorDescribe) { describes.insert(std::make_pair(des->index, des.get())); } for (auto iter = net->oplists.begin(); iter != net->oplists.end(); iter++) { auto& op = *iter; for (int i=0; ioutputIndexes.size(); ++i) { auto index = op->outputIndexes[i]; if (!outputMask[index]) { outputMask[index] = true; continue; } if (abortOpt) { return false; } // Dup output, rename it int newIndex = (int)net->tensorName.size(); outputMask.push_back(true); std::ostringstream tempOs; tempOs << "_" << net->tensorName[index] << "_" << newIndex; auto newName = tempOs.str(); MNN_PRINT("Convert: Dup output %s, replace by %s\n", net->tensorName[index].c_str(), newName.c_str()); net->tensorName.emplace_back(newName); op->outputIndexes[i] = newIndex; if (describes.find(index) != describes.end()) { auto originDes = describes.find(index)->second; std::unique_ptr newTensorDes; flatbuffers::FlatBufferBuilder tempBuilder; tempBuilder.Finish(TensorDescribe::Pack(tempBuilder, originDes)); newTensorDes.reset(flatbuffers::GetRoot(tempBuilder.GetBufferPointer())->UnPack()); newTensorDes->index = newIndex; net->extraTensorDescribe.emplace_back(std::move(newTensorDes)); } for (auto subIter = iter; subIter != net->oplists.end(); ++subIter) { auto& subOp = *subIter; for (int k=0; kinputIndexes.size(); ++k) { if (subOp->inputIndexes[k] == index) { subOp->inputIndexes[k] = newIndex; } } } } } return true; } static void _RemoveUnusefulNodes(std::unique_ptr& net) { if (!_RemoveDupOutput(net.get(), true)) { MNN_PRINT("Can't optimize static model because has loop\n"); return; } auto originMode = MNN::Express::ExecutorScope::Current()->getLazyMode(); MNN::Express::ExecutorScope::Current()->setLazyComputeMode(MNN::Express::Executor::LAZY_CONTENT); std::map varMap; auto outputs = std::move(net->outputName); { flatbuffers::FlatBufferBuilder builder; builder.Finish(MNN::Net::Pack(builder, net.get())); net.reset(); varMap = MNN::Express::Variable::loadMap(builder.GetBufferPointer(), builder.GetSize()); } std::vector outputVars; std::vector validOutputs; for (auto& name : outputs) { auto iter = varMap.find(name); if (iter == varMap.end()) { MNN_ERROR("Convert Static Model: Can't find %s output, skip\n", name.c_str()); continue; } validOutputs.emplace_back(name); outputVars.emplace_back(iter->second); } auto buffer = MNN::Express::Variable::save(outputVars); outputVars.clear(); varMap.clear(); net.reset(flatbuffers::GetRoot(buffer.data())->UnPack()); buffer.clear(); net->outputName = validOutputs; MNN::Express::ExecutorScope::Current()->setLazyComputeMode(originMode); } static void genStaticModel(CommandBuffer buffer, const std::string& modelName, std::map>& tensorNames, std::vector&& outputNames, const Net* originNetInfo) { MNN_PRINT("gen Static Model ... \n"); std::unique_ptr netT = std::unique_ptr(new MNN::NetT()); netT->outputName = std::move(outputNames); netT->usage = Usage_INFERENCE_STATIC; std::map tensorMap; // Add tensorName to new netT netT->tensorName.resize(tensorNames.size()); std::vector> inputOps; for (auto& iter : tensorNames) { netT->tensorName[iter.second.second] = iter.second.first; tensorMap.insert(std::make_pair(iter.first, iter.second.second)); if (TensorUtils::getDescribe(iter.first)->usage == MNN::Tensor::InsideDescribe::INPUT) { std::unique_ptr input(new OpT); input->type = OpType_Input; input->name = iter.second.first; input->outputIndexes = {iter.second.second}; input->main.value = new InputT; input->main.type = OpParameter_Input; input->main.AsInput()->dims = iter.first->shape(); input->main.AsInput()->dformat = TensorUtils::getDescribe(iter.first)->dimensionFormat; auto type = iter.first->getType(); if (type.code == halide_type_float) { if (type.bits == 32) { input->main.AsInput()->dtype = DataType_DT_FLOAT; } else if (type.bits == 16) { input->main.AsInput()->dtype = DataType_DT_HALF; } } else if (type.code == halide_type_int) { if (type.bits == 32) { input->main.AsInput()->dtype = DataType_DT_INT32; } else if (type.bits == 16) { input->main.AsInput()->dtype = DataType_DT_INT16; } else if (type.bits == 8) { input->main.AsInput()->dtype = DataType_DT_INT8; } } else if (type.code == halide_type_uint) { if (type.bits == 16) { input->main.AsInput()->dtype = DataType_DT_UINT16; } else if (type.bits == 8) { input->main.AsInput()->dtype = DataType_DT_UINT8; } } inputOps.emplace_back(std::move(input)); } } // add Tensors to netT for (auto& iterP : buffer.command) { auto& iter = *iterP; std::function insertTensor = [&](Tensor* t) { if (tensorMap.find(t) == tensorMap.end()) { int index = static_cast(tensorMap.size()); tensorMap.insert(std::make_pair(t, index)); std::string tensorName = "ExtraTensor_" + std::to_string(index); netT->tensorName.push_back(tensorName); } }; for (auto& t : iter.inputs) { insertTensor(t); } for (auto& t : iter.outputs) { insertTensor(t); } } // add tensors' describe to netT for (auto tensorPair : tensorMap) { auto tensor = tensorPair.first; auto index = tensorPair.second; //FUNC_PRINT(index); auto des = TensorUtils::getDescribe(tensor); if (des->usage == Tensor::InsideDescribe::CONSTANT || des->usage == MNN::Tensor::InsideDescribe::TRAINABLE) { std::unique_ptr op(new OpT); if (des->usage == Tensor::InsideDescribe::CONSTANT) { op->type = OpType_Const; } else { op->type = OpType_TrainableParam; } auto blob = new BlobT; op->main.type = OpParameter_Blob; op->main.value = blob; blob->dataFormat = des->dimensionFormat; for (int d = 0; d < tensor->dimensions();d++) { blob->dims.push_back(tensor->buffer().dim[d].extent); } if (tensor->getType() == halide_type_of()) { blob->dataType = DataType_DT_FLOAT; blob->float32s.resize(tensor->elementSize()); ::memcpy(blob->float32s.data(), tensor->host(), blob->float32s.size() * sizeof(float)); } else { CONSTANT_COPY(INT8, int8, 1); CONSTANT_COPY(UINT8, uint8, 1); CONSTANT_COPY(INT32, int32, 4) CONSTANT_COPY(INT64, int64, 8); } op->outputIndexes.push_back(index); netT->oplists.emplace_back(std::move(op)); } auto describe = std::unique_ptr(new MNN::TensorDescribeT); describe->index = index; describe->blob = std::unique_ptr(new MNN::BlobT); auto& blob = describe->blob; blob->dataFormat = des->dimensionFormat; if (tensor->getType() == halide_type_of()) { blob->dataType = DataType_DT_FLOAT; } else { SET_TYPE(INT8, int8)} SET_TYPE(UINT8, uint8)} SET_TYPE(INT32, int32)} SET_TYPE(INT64, int64)} } for (int d = 0; d < tensor->dimensions();d++) { describe->blob->dims.push_back(tensor->buffer().dim[d].extent); } auto tensorDes = TensorUtils::getDescribe(tensor); if (nullptr != tensorDes->quantAttr) { describe->quantInfo.reset(new TensorQuantInfoT); describe->quantInfo->max = tensorDes->quantAttr->max; describe->quantInfo->min = tensorDes->quantAttr->min; describe->quantInfo->zero = tensorDes->quantAttr->zero; describe->quantInfo->scale = tensorDes->quantAttr->scale; } for (auto& reg : des->regions) { auto regionT = std::unique_ptr(new MNN::RegionT); regionT->src = std::unique_ptr(new MNN::ViewT); regionT->dst = std::unique_ptr(new MNN::ViewT); regionT->src->offset = reg.src.offset; regionT->dst->offset = reg.dst.offset; for (int s = 0; s < 3; s++) { regionT->src->stride.push_back(reg.src.stride[s]); regionT->dst->stride.push_back(reg.dst.stride[s]); regionT->size.push_back(reg.size[s]); } describe->regions.emplace_back(std::move(regionT)); } netT->extraTensorDescribe.emplace_back(std::move(describe)); } // add op to netT for (auto&& iter : inputOps) { netT->oplists.emplace_back(std::move(iter)); } int idx = 0; for (auto& iterP : buffer.command) { auto& iter = *iterP; auto opt = iter.op->UnPack(); if (opt->name.size() <= 0) { opt->name = std::string("Geometry_") + MNN::EnumNameOpType(opt->type) + std::to_string(idx++); } opt->inputIndexes.resize(iter.inputs.size()); opt->outputIndexes.resize(iter.outputs.size()); for (int i = 0; i < iter.outputs.size(); i++) { opt->outputIndexes[i] = tensorMap[iter.outputs[i]]; } for (int i = 0; i < iter.inputs.size(); i++) { opt->inputIndexes[i] = tensorMap[iter.inputs[i]]; } netT->oplists.emplace_back(std::move(opt)); } _RemoveUnusefulNodes(netT); netT->usage = Usage_INFERENCE_STATIC; netT->sourceType = originNetInfo->sourceType(); if (nullptr != originNetInfo->bizCode()) { netT->bizCode = originNetInfo->bizCode()->str(); } if (nullptr != originNetInfo->mnn_uuid()) { netT->mnn_uuid = originNetInfo->mnn_uuid()->str(); } netT->extraInfo.reset(new ExtraInfoT); netT->extraInfo->version = MNN_VERSION; // write netT to file flatbuffers::FlatBufferBuilder builderOutput(1024); auto len = MNN::Net::Pack(builderOutput, netT.get()); builderOutput.Finish(len); int sizeOutput = builderOutput.GetSize(); auto bufferOutput = builderOutput.GetBufferPointer(); std::ofstream output(modelName, std::ofstream::binary); output.write((const char*)bufferOutput, sizeOutput); } void converToStaticModel(const Net* net, std::map>& inputConfig, std::string mnnFile) { // set a backend and context to run resize ScheduleConfig config; config.type = MNN_FORWARD_CPU; BackendConfig backendConfig; backendConfig.precision = BackendConfig::Precision_High; config.backendConfig = &backendConfig; Backend::Info compute; compute.type = config.type; compute.numThread = config.numThread; compute.user = config.backendConfig; const RuntimeCreator* runtimeCreator(MNNGetExtraRuntimeCreator(compute.type)); std::unique_ptr runtime(runtimeCreator->onCreate(compute)); std::shared_ptr backend(runtime->onCreate()); BackendConfig defaultConfig; defaultConfig.flags = 4; std::shared_ptr defaultBackend(runtime->onCreate(&defaultConfig)); std::vector> allTensors; allTensors.resize(net->tensorName()->size()); ErrorCode code = NO_ERROR; initConstTensors(allTensors, net, defaultBackend.get(), code, nullptr); if (NO_ERROR != code) { MNN_ERROR("Init tensor error code = %d\n", code); return; } bool valid = initTensors(allTensors, net); // set tensors' shape by inputConfig for (int i = 0; i < allTensors.size(); i++) { auto name = net->tensorName()->GetAsString(i)->str(); if (inputConfig.find(name) != inputConfig.end()) { auto& dims = inputConfig[name]; allTensors[i]->buffer().dimensions = dims.size(); for (int j = 0; j < dims.size(); j++) { allTensors[i]->setLength(j, dims[j]); } } } std::vector infos; { std::vector ops; for (int i = 0; i < net->oplists()->size(); i++) { auto op = net->oplists()->GetAs(i); if (needComputeOp(op)) { ops.push_back(op); } } initPipelineInfosFromOps(infos, ops, allTensors); setInputOutputForOps(allTensors, ops); } GeometryComputer::Context ctx(Interpreter::GeometryComputeMask::GEOMETRCOMPUTEMASK_ALL, defaultBackend); // resize the session's info and store to buffer std::vector constTensors; GeometryComputerUtils::buildConstantTensors(infos); GeometryComputerUtils::shapeComputeAndGeometryTransform(runtime.get(), nullptr, infos, ctx, defaultBackend, runtime->onGetCompilerType()); std::map> tensorName; for (int i = 0; i < net->tensorName()->size(); i++) { tensorName[allTensors[i].get()] = std::make_pair(net->tensorName()->GetAsString(i)->str(), i); } std::vector outputNames; if (net->outputName() != nullptr) { for (int i=0; ioutputName()->size(); ++i) { outputNames.emplace_back(net->outputName()->GetAsString(i)->str()); } } else { for (int i = 0; i < net->tensorName()->size(); i++) { if (TensorUtils::getDescribe(allTensors[i].get())->usage == MNN::Tensor::InsideDescribe::OUTPUT) { outputNames.emplace_back(net->tensorName()->GetAsString(i)->str()); } } } CommandBuffer newBuffer; for (auto& info : infos) { if (info.type == MNN::Schedule::CONSTANT) { continue; } // TODO: Remove inside constant op in future auto& buf = info.executeBuffer; newBuffer.command.insert(newBuffer.command.end(), buf.command.begin(), buf.command.end()); newBuffer.extras.insert(newBuffer.extras.end(), buf.extras.begin(), buf.extras.end()); } // store buffer to STATIC model file genStaticModel(newBuffer, mnnFile, tensorName, std::move(outputNames), net); }