Files
alibaba--mnn/tools/converter/source/common/convertToStaticModel.cpp
T
2026-07-13 13:33:03 +08:00

379 lines
16 KiB
C++

//
// convertToStaticModel.cpp
// MNNConverter
//
// Created by MNN on 2020/09/03.
// Copyright © 2018, Alibaba Group Holding Limited
//
#include <fstream>
#include <sstream>
#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 <MNN/expr/Expr.hpp>
#include <MNN/expr/ExecutorScope.hpp>
using namespace MNN;
#define SET_TYPE(TYPE, type) \
if (tensor->getType() == halide_type_of<type##_t>()) {\
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<type##_t>(), blob->type##s.size() * bytes);\
}
static bool _RemoveDupOutput(MNN::NetT* net, bool abortOpt) {
std::vector<bool> outputMask(net->tensorName.size(), false);
std::map<int, TensorDescribeT*> 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; i<op->outputIndexes.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<TensorDescribeT> newTensorDes;
flatbuffers::FlatBufferBuilder tempBuilder;
tempBuilder.Finish(TensorDescribe::Pack(tempBuilder, originDes));
newTensorDes.reset(flatbuffers::GetRoot<TensorDescribe>(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; k<subOp->inputIndexes.size(); ++k) {
if (subOp->inputIndexes[k] == index) {
subOp->inputIndexes[k] = newIndex;
}
}
}
}
}
return true;
}
static void _RemoveUnusefulNodes(std::unique_ptr<MNN::NetT>& 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<std::string, MNN::Express::VARP> 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<MNN::Express::VARP> outputVars;
std::vector<std::string> 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<MNN::Net>(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<Tensor*, std::pair<std::string, int>>& tensorNames, std::vector<std::string>&& outputNames, const Net* originNetInfo) {
MNN_PRINT("gen Static Model ... \n");
std::unique_ptr<MNN::NetT> netT = std::unique_ptr<MNN::NetT>(new MNN::NetT());
netT->outputName = std::move(outputNames);
netT->usage = Usage_INFERENCE_STATIC;
std::map<Tensor*, int> tensorMap;
// Add tensorName to new netT
netT->tensorName.resize(tensorNames.size());
std::vector<std::unique_ptr<OpT>> 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<OpT> 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<void(Tensor*)> insertTensor = [&](Tensor* t) {
if (tensorMap.find(t) == tensorMap.end()) {
int index = static_cast<int>(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<OpT> 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<float>()) {
blob->dataType = DataType_DT_FLOAT;
blob->float32s.resize(tensor->elementSize());
::memcpy(blob->float32s.data(), tensor->host<void>(), 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<MNN::TensorDescribeT>(new MNN::TensorDescribeT);
describe->index = index;
describe->blob = std::unique_ptr<MNN::BlobT>(new MNN::BlobT);
auto& blob = describe->blob;
blob->dataFormat = des->dimensionFormat;
if (tensor->getType() == halide_type_of<float>()) {
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<MNN::RegionT>(new MNN::RegionT);
regionT->src = std::unique_ptr<MNN::ViewT>(new MNN::ViewT);
regionT->dst = std::unique_ptr<MNN::ViewT>(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<std::string,std::vector<int>>& 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> runtime(runtimeCreator->onCreate(compute));
std::shared_ptr<Backend> backend(runtime->onCreate());
BackendConfig defaultConfig;
defaultConfig.flags = 4;
std::shared_ptr<Backend> defaultBackend(runtime->onCreate(&defaultConfig));
std::vector<std::shared_ptr<Tensor>> 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<Schedule::OpCacheInfo> infos;
{
std::vector<const Op*> ops;
for (int i = 0; i < net->oplists()->size(); i++) {
auto op = net->oplists()->GetAs<Op>(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<Tensor*> constTensors;
GeometryComputerUtils::buildConstantTensors(infos);
GeometryComputerUtils::shapeComputeAndGeometryTransform(runtime.get(), nullptr, infos, ctx, defaultBackend, runtime->onGetCompilerType());
std::map<Tensor*, std::pair<std::string, int>> 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<std::string> outputNames;
if (net->outputName() != nullptr) {
for (int i=0; i<net->outputName()->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);
}