模型量化基础:从FP32到INT8的数学原理与精度损失分析
模型量化,说白了就是给神经网络做「瘦身手术」。把原本用32位浮点数(FP32)表示的权重和激活值,换成8位整数(INT8)来存。我刚开始接触这概念时也觉得不可思议——精度降了这么多,模型还能用吗?
后来在边缘设备上跑模型时发现,FP32模型动不动就几百兆,嵌入式芯片根本塞不下。量化不是选择题,而是必答题。今天我们就把它彻底讲透。
为什么是FP32?为什么又要换成INT8?
FP32用32位二进制表示一个数,1位符号、8位指数、23位尾数。它能表示的数值范围大约是 ±3.4×10³⁸,精度约7位有效数字。训练时我们需要这个精度来稳定梯度传播。
但推理时不一样。推理只需要前向传播,对精度的容忍度其实很高。INT8只有256个离散值,但计算速度能快2-4倍,模型体积直接缩到1/4。在边缘设备上,这差距就是「能跑」和「跑不动」的区别。
核心洞察:量化不是「降级」,而是「重新编码」。我们用更少的比特,尽可能保留原始信息。关键在于找到FP32到INT8的映射关系。
量化的数学本质:仿射变换
FP32到INT8的转换,本质上是一个线性映射。公式很简单:
Q = round( (R - zero_point) / scale )
反过来,反量化:
R = Q * scale + zero_point
其中:
- R — 原始FP32值
- Q — 量化后的INT8值
- scale — 缩放因子(步长)
- zero_point — 零点偏移
scale 和 zero_point 就是我们要找的两个参数。它们决定了整个映射的质量。
对称量化 vs 非对称量化
这里有个关键选择。我个人习惯先看权重分布再决定用哪种。
| 类型 | zero_point | 适用场景 | 优点 | 缺点 |
|---|---|---|---|---|
| 对称量化 | 固定为0 | 权重(通常对称分布) | 计算简单,硬件友好 | 浪费非对称分布的表示能力 |
| 非对称量化 | 根据数据计算 | 激活值(ReLU后全正数) | 充分利用INT8的256个值 | 计算稍复杂 |
我在项目中遇到过一个问题:用对称量化去量化ReLU后的激活值,结果精度掉了3个点。因为激活值全是正数,对称量化把一半的表示范围浪费在了负数区域。换成非对称量化后,精度损失降到了0.5%以内。
经验之谈:权重用对称量化,激活值用非对称量化。这是大多数工业级部署的默认配置。
量化粒度:逐层 vs 逐通道
scale 和 zero_point 是每个张量一套,还是每个通道一套?这决定了量化粒度。
- 逐层量化(Per-Tensor):整个层共享一组参数。计算量小,但精度损失大。
- 逐通道量化(Per-Channel):每个输出通道独立参数。精度高,但计算稍复杂。
我建议:卷积层的权重用逐通道量化,全连接层用逐层量化就够了。卷积层不同通道的权重分布差异很大,逐通道能显著减少精度损失。
精度损失从哪来?三个罪魁祸首
量化一定会丢精度,但我们要知道丢在哪。我总结了三个主要来源:
- 截断误差:FP32的值超出INT8表示范围时被截断。比如scale设得太小,大数值直接溢出。
- 舍入误差:round()操作把连续值映射到离散整数点。这个误差最大不超过半个scale步长。
- 零点偏移误差:zero_point取整带来的额外偏差。非对称量化中尤其明显。
注意:量化误差会逐层累积。前面层丢1%,后面可能放大到5%。所以输入层的量化要格外小心。
量化感知训练(QAT) vs 训练后量化(PTQ)
这是两个主流方案。我直接说结论:
| 方法 | 精度 | 成本 | 适用场景 |
|---|---|---|---|
| PTQ | 中等(通常损失<1%) | 低(只需少量校准数据) | 快速部署、模型已训练好 |
| QAT | 高(损失<0.1%) | 高(需要重新训练) | 精度敏感、有训练资源 |
PTQ 适合快速验证。QAT 适合生产环境。我曾经在一个目标检测项目里,PTQ 后 mAP 掉了2.3%,换成 QAT 后只掉了0.4%。多花两天训练时间,换回2个点的精度,值。
量化误差的评估方法
怎么知道量化后模型还能不能用?我一般看三个指标:
- 余弦相似度:比较量化前后每层输出的向量夹角。低于0.99就要警惕。
- 信噪比(SNR):信号功率与噪声功率的比值。越高越好。
- 最终任务指标:比如分类的Top-1准确率、检测的mAP。这是最终裁判。
嗯,这里要注意:余弦相似度高不代表最终指标好。我见过余弦相似度0.995但准确率掉了1.5%的情况。最终还是得看任务指标。
知识结构图:量化全流程
下面这张图总结了从FP32到INT8的完整流程和关键决策点:
实战中的量化参数选择
最后分享几个我在项目中总结的经验:
- 校准数据集:PTQ 时用500-1000张代表性图片就够了。别用训练集,用验证集。
- scale 的初始化:用最大值法(max)还是 KL散度法?我推荐 KL 散度。它自动找到截断阈值,保留信息最多。
- 特殊层处理:BatchNorm 层在量化前要融合进卷积层。不然量化误差会翻倍。
- 量化后微调:QAT 时学习率设为原来的1/10,训练3-5个epoch就够了。多了反而过拟合。
避坑指南:我曾经在量化一个轻量级模型(MobileNetV3)时,PTQ 后精度直接崩了。后来发现是激活值分布太集中,scale 被 outlier 拉得太大。改用 percentile 方法(取99.9%分位数)后,问题解决。
量化不是玄学,是数学。理解了 scale 和 zero_point 的物理意义,你就能精准控制精度损失。下次遇到量化后模型不准,别慌,先看分布,再调参数。