4. 模型量化技术
模型量化,说白了就是给模型“减减肥、降降压”。
FP32 的模型跑在边缘设备上,又慢又吃内存。我刚开始做嵌入式部署时,一个 100MB 的模型塞进开发板,直接 OOM(内存溢出)。后来才意识到——不量化,根本没法落地。
4.1 为什么要量化?——从 FP32 到 INT8 的跨越
FP32 每个数占 4 字节,INT8 只占 1 字节。理论上,模型体积直接缩到 1/4,推理速度也能翻倍(取决于硬件对 INT8 的加速支持)。
但代价呢?精度损失。嗯,这里要注意——不是所有模型都能无损量化。我踩过的坑是:轻量模型(如 MobileNet)量化后精度掉得比大模型还厉害。
- 模型体积减少 75%
- 推理速度提升 2~4 倍(边缘端 NPU 加持下更明显)
- 内存带宽占用降低,多路并发更稳
4.2 量化原理:映射与校准
量化本质就是做映射:把 FP32 的浮点范围“压缩”到 INT8 的整数范围。
公式其实不复杂:
scale = (max_val - min_val) / 255
zero_point = round(-min_val / scale)
# 量化
q = round(x / scale) + zero_point
# 反量化
x_hat = (q - zero_point) * scale
这里有两个关键参数:scale(缩放因子)和 zero_point(零点偏移)。
我个人习惯用 对称量化(zero_point=0),因为计算更简单,硬件实现也友好。但遇到 ReLU 输出全是正数的情况,非对称量化反而能保留更多信息。
4.3 三种量化路线:PTQ、QAT、混合精度
目前业界主流就三条路。我按落地难度排个序:
| 方法 | 需要重训? | 精度损失 | 部署难度 | 适用场景 |
|---|---|---|---|---|
| 后训练量化 (PTQ) | 否 | 中~高 | 低 | 快速验证、大模型 |
| 量化感知训练 (QAT) | 是 | 低 | 中 | 精度敏感场景 |
| 混合精度量化 | 可选 | 可控 | 高 | 关键层保护 |
4.3.1 后训练量化(PTQ)
PTQ 是最省事的方法。模型训练完,拿一小批校准数据跑一遍,统计出每层的激活值范围,然后直接转 INT8。
步骤很简单:
- 准备 100~500 张校准图片(别用训练集,用验证集)
- 跑一遍前向推理,记录每层的 min/max
- 计算 scale 和 zero_point
- 转换权重和激活值为 INT8
但 PTQ 有个致命问题—— outlier(离群值)。如果某层有个别值特别大,整个量化范围被拉宽,大部分值反而精度受损。
4.3.2 量化感知训练(QAT)
QAT 的思路是:在训练过程中就模拟量化误差,让模型自己学会“抗噪”。
具体做法是在前向传播时插入 伪量化节点(FakeQuant):
# 伪量化操作(PyTorch 风格)
def fake_quantize(x, scale, zero_point, qmin=-128, qmax=127):
x = torch.clamp(x, min_val, max_val)
x = torch.round(x / scale) + zero_point
x = torch.clamp(x, qmin, qmax)
x = (x - zero_point) * scale
return x
反向传播时,round 操作不可导,怎么办?用 直通估计器(STE)——直接把梯度传过去,忽略 round 的梯度。
QAT 的精度通常比 PTQ 高 1~3 个点。代价是训练时间增加 20%~50%。
4.3.3 混合精度量化
不是所有层都适合 INT8。有些层对精度极其敏感——比如第一层(输入层)、最后一层(输出层)、注意力机制中的 softmax。
混合精度量化的思路就是:敏感层保留 FP16 或 FP32,其他层用 INT8。
怎么判断哪些层敏感?我一般用 逐层精度损失分析:
- 先全量量化,记录每层输出的 SNR(信噪比)
- SNR 低于阈值的层,标记为“敏感层”
- 这些层回退到 FP16 或 FP32
这样做的好处是:模型体积只增加 5%~10%,但精度几乎无损。
4.4 量化工具链与实战对比
不同框架的量化支持差异很大。我整理了一张对比表:
| 框架 | PTQ | QAT | 混合精度 | 边缘端支持 |
|---|---|---|---|---|
| TensorRT | ✅ 成熟 | ✅ 需插件 | ✅ 自动 | Jetson 系列 |
| ONNX Runtime | ✅ 成熟 | ⚠️ 实验性 | ✅ 手动 | 通用 |
| TFLite | ✅ 成熟 | ✅ 成熟 | ⚠️ 有限 | Android/ARM |
| OpenVINO | ✅ 成熟 | ⚠️ 实验性 | ✅ 自动 | Intel 系列 |
| NCNN | ✅ 成熟 | ❌ 不支持 | ⚠️ 手动 | ARM/手机 |
我个人在 Jetson 上用得最多的是 TensorRT。它的 INT8 校准器 支持三种模式:
- EntropyCalibratorV2:基于 KL 散度,通用性好
- MinMaxCalibrator:简单粗暴,适合分布均匀的层
- LegacyCalibrator:老版本兼容,不推荐
4.5 量化全流程框架图
下面这张图总结了从 FP32 模型到 INT8 部署的完整流程:
4.6 量化后的验证与调优
量化完别急着部署。我一般会做三件事:
- 逐层对比输出:跑 10 张图,对比 FP32 和 INT8 每层的输出余弦相似度。低于 0.99 的层重点关注。
- 端到端精度测试:在完整验证集上跑一遍,看 mAP/ACC 掉了多少。
- 性能基准测试:测延迟、吞吐量、内存占用。有时候 INT8 反而比 FP32 慢——因为某些硬件对 INT8 支持不好。
4.7 总结:量化选型建议
最后给个实用建议:
- 快速原型验证:PTQ + EntropyCalibratorV2
- 精度敏感场景:QAT + 混合精度(保护输入输出层)
- 资源极度受限:全 INT8 + 对称量化(计算更简单)
- 多平台部署:ONNX Runtime + 混合精度(兼容性最好)
量化不是银弹,但它是边缘端部署的必经之路。你想想看,一个 100MB 的模型量化到 25MB,推理速度翻倍——这笔账怎么算都划算。