【mnn】——模型离线量化流程代码浅析

mnn, 离线量化

1. 前言

mnn的离线量化,需要首先将其他模型转换成mnn的模型表达,再进行量化。

这里我们采用MAX_ABS进行weight权重量化,KL散度进行激活值的量化,int8对称量化。

2. Code

2.1 mnn模型读入与解析

std::unique_ptr<MNN::NetT> netT;
    {
        std::ifstream input(modelFile);
        std::ostringstream outputOs;
        outputOs << input.rdbuf();
        netT = MNN::UnPackNet(outputOs.str().c_str());
    }

    // temp build net for inference
    flatbuffers::FlatBufferBuilder builder(1024);
    auto offset = MNN::Net::Pack(builder, netT.get());
    builder.Finish(offset);
    int size      = builder.GetSize();
    auto ocontent = builder.GetBufferPointer();

    // model buffer for creating mnn Interpreter
    std::unique_ptr<uint8_t> modelForInference(new uint8_t[size]);
    memcpy(modelForInference.get(), ocontent, size);

    std::unique_ptr<uint8_t> modelOriginal(new uint8_t[size]);
    memcpy(modelOriginal.get(), ocontent, size);

    netT.reset();
    netT = MNN::UnPackNet(modelOriginal.get());

2.2 创建Calibration数据dataloader
这个Calibration是整个量化的主流程,整体流程可以归纳为:

  • fake quant weight,对原有的模型进行假量化,就是将模型的权重用MAX_ABS量化到int8,再从int8反量化到float类型。这里是为了统计的激活值的范围更精确。
  • 将假量化模型和浮点模型的tensor放入两个不同的map

2.3 离线量化
整体流程:

  • 给假量化模型传入图片,更新每个tensor的最大值,最小值
    在这里插入图片描述
  • 计算所有tensor的分布,将激活值用2048个bin进行离散,统计其直方图
    在这里插入图片描述
  • 通过KL散度为每个tensor计算一个阈值threshold,并将阈值转换为浮点和int8之间转换的scale
    在这里插入图片描述
  • 将量化参数,tensor scale,int8 weight等量化参数写回模型。 注意这里需要将浮点的weight clear掉。
for (const auto iter :  _scales) {
        std::unique_ptr<MNN::TensorDescribeT> describe(new MNN::TensorDescribeT);
        describe->index = _tensorIdx[iter.first];
        describe->quantInfo.reset(new MNN::TensorQuantInfoT);
        describe->quantInfo->scale = iter.second;
        describe->quantInfo->type = MNN::DataType_DT_INT8;
        describe->quantInfo->min = -1 * _featureClampValue;
        describe->quantInfo->max = 1 * _featureClampValue;
        _originalModel->extraTensorDescribe.emplace_back(std::move(describe));          // 1. extraTensorDescribe量化后添加的属性,在哪里使用??
    }

SymmetricQuantizeWeight(param->weight.data(), weightSize, quantizedWeight.data(), quantizedWeightScale.data(), outputChannel, _weightClampValue);

param->quanParameter = IDSTEncoder::encode(param->weight, quantizedWeightScale, weightSize/channles, channles, false, quantizedWeight.data(), -_weightClampValue);          // 3. 
param->quanParameter->scaleIn = inputScale;
param->quanParameter->scaleOut = outputScale;
if (param->common->relu6) {
    param->common->relu  = true;
    param->common->relu6 = false;
}
param->weight.clear();          // 4. 清除原有的weight
  • 重新写回到模型

总结

  • 整个代码还是很清晰的,结构也很明确。只是后续要如何使用这些量化参数需要深入到mnn的框架里,后面再补上mnn框架代码的浅析
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值