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

182 lines
7.5 KiB
C++

//
// FullQuantAndCoding.cpp
// MNNConverter
//
// Created by MNN on 2021/08/11.
// Copyright © 2018, Alibaba Group Holding Limited
//
#include <fstream>
#include "CommonUtils.hpp"
#include "MNN/expr/ExprCreator.hpp"
#include "core/IDSTEncoder.hpp"
using namespace MNN;
using namespace MNN::Express;
void FullQuantAndCoding(std::unique_ptr<MNN::NetT>& netT, std::unique_ptr<MNN::OpT>& op, Compression::Pipeline& proto, SubGraphProtoT* subgraph) {
std::string outputTensorName = subgraph ? subgraph->tensors[op->outputIndexes[0]] : netT->tensorName[op->outputIndexes[0]];;
auto opType = op->type;
if (opType != MNN::OpType_Convolution && opType != MNN::OpType_ConvolutionDepthwise) {
return;
}
if (op->inputIndexes.size() != 1) {
return;
}
auto findQuantParameters = [&](Compression::Pipeline& proto, std::string outputTensorName) {
for (const auto& algo : proto.algo()) {
if (algo.type() == Compression::CompressionAlgo::QUANTIZE) {
auto quantParams = algo.quant_params();
for (const auto& layerProto : quantParams.layer()) {
if (layerProto.output_size() <= 0) {
continue;
}
const std::string& outputName = layerProto.output(0).name();
if ((outputName == outputTensorName) || (outputTensorName == outputName+"__matmul_converted")) {
return layerProto;
}
}
}
}
MNN::Compression::LayerQuantizeParams empty;
return empty;
};
auto inputIndex = op->inputIndexes[0];
int outputIndex = op->outputIndexes[0];
auto quantParams = findQuantParameters(proto, outputTensorName);
if (quantParams.weight_size() == 0) {
return;
}
auto inputParams = quantParams.input(0);
auto outputParams = quantParams.output(0);
auto weightParams = quantParams.weight(0);
auto& tensorDescribe = subgraph ? subgraph->extraTensorDescribe : netT->extraTensorDescribe;
auto findInDescribe = [&] (int index) {
for (int i = 0; i < tensorDescribe.size(); i++) {
if (tensorDescribe[i]->index == index) {
return true;
}
}
return false;
};
if (!findInDescribe(inputIndex)) {
std::unique_ptr<MNN::TensorDescribeT> inDescribe(new MNN::TensorDescribeT);
inDescribe->index = inputIndex;
std::unique_ptr<MNN::TensorQuantInfoT> inputQuantInfo(new MNN::TensorQuantInfoT);
inputQuantInfo->zero = inputParams.zero_point();
inputQuantInfo->scale = inputParams.scales(0);
inputQuantInfo->min = inputParams.clamp_min();
inputQuantInfo->max = inputParams.clamp_max();
inputQuantInfo->type = MNN::DataType_DT_INT8;
inDescribe->quantInfo = std::move(inputQuantInfo);
tensorDescribe.emplace_back(std::move(inDescribe));
}
if (!findInDescribe(outputIndex)) {
std::unique_ptr<MNN::TensorDescribeT> outDescribe(new MNN::TensorDescribeT);
outDescribe->index = outputIndex;
std::unique_ptr<MNN::TensorQuantInfoT> outputQuantInfo(new MNN::TensorQuantInfoT);
outputQuantInfo->zero = outputParams.zero_point();
outputQuantInfo->scale = outputParams.scales(0);
outputQuantInfo->min = outputParams.clamp_min();
outputQuantInfo->max = outputParams.clamp_max();
outputQuantInfo->type = MNN::DataType_DT_INT8;
outDescribe->quantInfo = std::move(outputQuantInfo);
tensorDescribe.emplace_back(std::move(outDescribe));
}
auto convParams = op->main.AsConvolution2D();
auto weightFloat = convParams->weight;
auto biasFloat = convParams->bias;
auto& common = convParams->common;
const int ko = common->outputCount;
const int ki = common->inputCount / common->group;
const int kh = common->kernelY;
const int kw = common->kernelX;
const int kernelNum = common->outputCount;
int kernelSize = weightFloat.size() / kernelNum;
VARP weightVar = _Const(weightFloat.data(), {ko, ki, kh, kw}, NCHW);
VARP biasVar = _Const(biasFloat.data(), {ko, 1, 1, 1}, NCHW);
VARP inputScaleVar = _Const(inputParams.scales(0), {}, NCHW);
VARP outputScaleVar = _Const(outputParams.scales(0), {}, NCHW);
float wClampMin = weightParams.clamp_min();
float wClampMax = weightParams.clamp_max();
std::vector<float> weightScaleVector(weightParams.scales().begin(), weightParams.scales().end());
VARP weightScale = _Const(weightScaleVector.data(), {(int)weightScaleVector.size(), 1, 1, 1}, NCHW, halide_type_of<float>());
auto quanWeightTemp = _Round(weightVar * _Reciprocal(weightScale));
auto quanWeightClamp = MNN::Express::_Maximum(_Minimum(quanWeightTemp, _Scalar<float>(wClampMax)), _Scalar<float>(wClampMin));
auto quanWeight = _Cast<int8_t>(quanWeightClamp);
auto convScale = _Reshape(_Reciprocal(outputScaleVar), {-1, 1, 1, 1}) * weightScale * inputScaleVar;
std::vector<int8_t> quantWeights;
std::vector<float> biasData;
std::vector<float> scale;
{
auto info = quanWeight->getInfo();
quantWeights.resize(info->size);
auto ptr = quanWeight->readMap<int8_t>();
for (int i = 0; i < quantWeights.size(); i++) {
quantWeights[i] = ptr[i];
}
}
{
auto biasinfo = biasVar->getInfo();
biasData.resize(biasinfo->size);
auto ptr = biasVar->readMap<float>();
::memcpy(biasData.data(), ptr, biasData.size() * sizeof(int32_t));
auto info = weightScale->getInfo();
scale.resize(info->size);
MNN_ASSERT(scale.size() == biasData.size());
auto ptrScale = weightScale->readMap<float>();
::memcpy(scale.data(), ptrScale, scale.size() * sizeof(float));
}
bool asymmetricQuantFlag = false;
std::vector<float> fakeScales(kernelNum, 1.0f);
convParams->quanParameter = IDSTEncoder::encode(nullptr, fakeScales, kernelSize, kernelNum, asymmetricQuantFlag, quantWeights.data(), wClampMin);
convParams->weight.clear();
convParams->quanParameter->alpha = std::move(scale);
convParams->quanParameter->scaleIn = inputParams.scales(0);
convParams->quanParameter->scaleOut = outputParams.scales(0);
convParams->symmetricQuan.reset(new MNN::QuantizedFloatParamT);
convParams->symmetricQuan->method = MNN::QuantizeAlgo(int(quantParams.method()));
convParams->symmetricQuan->nbits = outputParams.bits();
convParams->symmetricQuan->zeroPoint = inputParams.zero_point();
convParams->symmetricQuan->outputZeroPoint = outputParams.zero_point();
convParams->symmetricQuan->clampMin = outputParams.clamp_min();
convParams->symmetricQuan->clampMax = outputParams.clamp_max();
convParams->bias = std::move(biasData);
// winogradAttr store:
// 1. transformed weight and input scale
// 2. winograd config (F(2,3)/F(4,3)/F(6,3)/...)
if (quantParams.method() == MNN::Compression::LayerQuantizeParams::WinogradAware) {
const auto& attr = quantParams.wino_params().units_attr();
convParams->symmetricQuan->winogradAttr.assign(attr.begin(), attr.end());
}
};
void fullQuantAndCoding(std::unique_ptr<MNN::NetT>& netT, MNN::Compression::Pipeline proto) {
for (auto& op : netT->oplists) {
FullQuantAndCoding(netT, op, proto, nullptr);
}
for (auto& subgraph : netT->subgraphs) {
for (auto& op : subgraph->nodes) {
FullQuantAndCoding(netT, op, proto, subgraph.get());
}
}
}