4、后训练量化:快速部署的捷径与陷阱

后训练量化,说白了就是模型训练完之后,我们再动手把它从 FP32 压缩成 INT8 甚至更低比特。这玩意儿在嵌入式部署里太常见了,我几乎每个项目都会用到它。为什么?因为它快,真的快,不需要重新训练,几行代码就能让模型跑起来。

但我要先泼一盆冷水。这玩意儿看着简单,坑却不少。我见过太多人一上来就量化,结果模型精度直接崩了,然后跑来问我怎么回事。嗯,咱们今天就把这些坑一个个填上。

4.1 量化到底在做什么?

先说说本质。量化就是把连续的浮点数映射到离散的整数上。比如一个权重是 0.1234,量化后可能就变成了 12(假设是 INT8 的某个值)。

公式其实很简单:

Q = round( (R - zero_point) / scale )

其中 R 是原始浮点数,Q 是量化后的整数,scale 和 zero_point 就是我们要找的量化参数。

我个人习惯把 scale 理解为「步长」。你想想看,FP32 能表示的范围太大了,INT8 只有 256 个值。所以每个整数代表一个区间,scale 就是这个区间的大小。

关键点:量化不是无损的。每次量化都会引入误差,关键是怎么让这个误差不影响最终结果。

4.2 常见的量化方式

后训练量化主要有两种路子:

  • 权重量化:只量化模型的权重参数。这个最简单,几乎不会掉精度。我在项目中经常用这个做快速验证。
  • 全量化:权重和激活值都量化。这个麻烦一些,但压缩效果更好。

还有一种叫「量化感知训练」,但那不属于后训练量化,咱们今天不聊。

4.3 校准数据集:被忽视的关键

做全量化的时候,你需要一个校准数据集。这个数据集用来统计激活值的分布,从而确定 scale 和 zero_point。

我曾经犯过一个错误。当时图省事,随便拿了 100 张图片做校准。结果模型部署后,精度掉了 5 个点。后来换成 500 张有代表性的图片,精度只掉了 0.3 个点。

注意:校准数据集必须能代表真实场景的数据分布。不要用训练集做校准,那会过拟合。也不要只用几张图,那统计出来的分布不准。

我建议校准集至少 200-500 张,而且要覆盖各种情况。比如做目标检测,要有不同光照、不同角度、不同大小的目标。

4.4 量化粒度:逐层还是逐通道?

量化粒度是个容易被忽略的细节。说白了就是:每个层用一个 scale,还是每个通道用一个 scale?

粒度 优点 缺点
逐层量化 实现简单,计算快 精度损失大,尤其对分布不均匀的层
逐通道量化 精度更高,能适应不同通道的分布 实现复杂,硬件支持要求高

我个人习惯:能逐通道就逐通道。虽然实现起来麻烦点,但精度确实好。不过要注意,有些嵌入式芯片不支持逐通道量化,你得先查硬件手册。

4.5 常见的陷阱与避坑指南

这里我总结几个最常见的坑:

  • 陷阱一:忽略异常值。激活值里偶尔会出现几个特别大的值,比如 100 以上。如果直接用这些值算 scale,那大部分值都会被压缩到很小的区间里,精度就崩了。我建议先做异常值裁剪,把 99.9% 分位以外的值截断。
  • 陷阱二:对称量化 vs 非对称量化。ReLU 激活后的值都是非负的,用非对称量化更合适。但有些硬件只支持对称量化,那就得注意了。
  • 陷阱三:BN 层融合。量化前一定要把 BN 层融合到卷积层里。不融合的话,量化误差会放大。这个步骤很多框架会自动做,但最好确认一下。

小技巧:量化后一定要做精度验证。我习惯在验证集上跑一遍,对比量化前后的输出差异。如果某个层的输出差异超过 5%,那这个层可能需要特殊处理。

4.6 实战:用 PyTorch 做后训练量化

来,看个实际例子。假设我们有一个训练好的模型:

import torch
import torch.quantization as quant

# 加载预训练模型
model = torch.load('model_fp32.pth')
model.eval()

# 准备量化配置
model.qconfig = quant.get_default_qconfig('fbgemm')
model = quant.prepare(model, inplace=True)

# 用校准数据集跑一遍
with torch.no_grad():
    for images in calibration_loader:
        model(images)

# 转换为量化模型
model = quant.convert(model, inplace=True)

# 保存量化模型
torch.save(model.state_dict(), 'model_int8.pth')

这段代码看着简单,但实际项目中往往没这么顺利。我遇到过一个问题:校准的时候数据量不够,导致某些层的 scale 算得不准。后来我加了个循环,每 50 张图重新统计一次分布,效果好了很多。

4.7 什么时候不该用后训练量化?

不是所有模型都适合后训练量化。我总结了几种情况:

  • 模型本身精度就不高。比如只有 80% 的准确率,量化后可能掉到 75%,那就没法用了。
  • 模型对数值非常敏感。比如一些回归任务,或者检测小目标的模型。量化后小目标的特征可能直接被抹掉了。
  • 模型结构特殊。比如用了很多自定义算子,量化工具可能不支持。

遇到这些情况,我建议要么用量化感知训练,要么干脆别量化,直接用 FP16 部署。FP16 虽然压缩比不如 INT8,但精度损失小很多。

4.8 总结

后训练量化是个好东西,但别把它当万能药。我的经验是:先做权重量化试试水,如果精度没问题,再考虑全量化。全量化的时候,校准数据集和量化粒度是关键。最后一定要做精度验证,别偷懒。

嗯,今天就聊到这儿。下一章咱们聊聊量化感知训练,那个路子更稳,但代价是得重新训练模型。