3. 模型量化基础:为什么需要量化、量化的数学原理、精度与性能的权衡
好,咱们进入第三章。说实话,量化这个话题,是端侧推理里最绕不开的一个坎。你模型训练得再漂亮,精度再高,到了手机、摄像头、AI ISP 芯片上跑不动,一切都是白搭。我最早接触量化是在一个智能门锁项目上,模型在 GPU 上跑得飞起,一放到海思的芯片上,直接内存溢出。嗯,从那以后,我才真正开始认真研究量化。
3.1 为什么需要量化?—— 从“胖”模型到“瘦”模型
先问个问题:为什么好好的 FP32(32位浮点数)不用,非要折腾成 INT8(8位整数)?
说白了,就三个字:资源不够。
你想想看,一个典型的 AI ISP 芯片,比如安霸、地平线或者瑞芯微的芯片,它的算力、带宽、内存都是有限的。FP32 模型就像个“胖子”,占地方、吃得还多。
核心痛点:
- 存储压力:FP32 模型参数占 4 字节,INT8 只占 1 字节。一个 10MB 的模型,量化后直接变成 2.5MB。对于 Flash 只有几十 MB 的端侧设备,这差距是致命的。
- 带宽瓶颈:每次从 DDR 读取数据,INT8 的读取量是 FP32 的 1/4。带宽在端侧是稀缺资源,省下来的带宽可以留给图像预处理或者后处理。
- 计算效率:绝大多数端侧 NPU(神经网络处理器)对 INT8 的乘累加运算有专门的硬件加速单元,速度比 FP32 快 2-4 倍。我在一个项目里实测过,INT8 推理延迟比 FP32 降低了 60%。
所以,量化不是锦上添花,而是端侧部署的刚需。没有量化,很多模型根本跑不起来。
3.2 量化的数学原理 —— 对称 vs 非对称
量化听起来玄乎,其实核心就是一个数学映射:把 FP32 的数值范围,映射到 INT8 的数值范围(通常是 -128 到 127,或者 0 到 255)。
这里面有两个关键参数:缩放因子(Scale) 和 零点(Zero Point)。
公式很简单:
量化值 (INT8) = round( 原始值 (FP32) / Scale + Zero Point )
反量化回去就是:
原始值 (FP32) = ( 量化值 (INT8) - Zero Point ) * Scale
Scale 决定了映射的“步长”,Zero Point 决定了映射的“偏移”。根据 Zero Point 的不同,量化分为两种主流方式。
3.2.1 对称量化(Symmetric Quantization)
对称量化,Zero Point 固定为 0。也就是说,FP32 的 0 映射到 INT8 的 0。
它的映射范围是对称的:[-max_abs, max_abs] 映射到 [-127, 127](或者 -128 到 127,但通常为了对称性会牺牲 -128)。
我个人习惯:在大部分卷积层和全连接层,我倾向于用对称量化。因为权重通常关于 0 对称分布,用对称量化不会浪费 INT8 的表示范围。而且计算时不用考虑 Zero Point 的偏移,硬件实现更简单。
3.2.2 非对称量化(Asymmetric Quantization)
非对称量化,Zero Point 可以不为 0。它把 FP32 的 [min, max] 映射到 INT8 的 [0, 255](或者 [-128, 127])。
这种方式的优势是:能充分利用 INT8 的整个表示范围。特别是对于激活值(比如 ReLU 后的输出),它们往往都是正数,用非对称量化可以把所有 256 个 bin 都用上,精度损失更小。
避坑指南:我曾经在一个语义分割模型上,对激活值用了对称量化,结果精度掉了 3 个点。后来换成非对称量化,精度只掉了 0.5 个点。原因就是激活值全是正数,对称量化浪费了一半的表示范围。所以,激活值推荐用非对称,权重推荐用对称,这基本是行业共识。
下面这个表格可以帮你快速对比:
| 特性 | 对称量化 | 非对称量化 |
|---|---|---|
| Zero Point | 固定为 0 | 不固定,需计算 |
| 映射范围 | [-max_abs, max_abs] → [-127, 127] | [min, max] → [0, 255] |
| 适用场景 | 权重(分布对称) | 激活值(分布偏斜) |
| 计算复杂度 | 低(无偏移计算) | 高(需处理偏移) |
| 精度表现 | 对对称分布友好 | 对非对称分布更优 |
3.3 精度与性能的权衡 —— 量化不是免费的午餐
量化能带来性能提升,但代价是精度损失。这个损失从哪来?
核心原因就一个:信息丢失。FP32 有约 4.2e9 个离散值,INT8 只有 256 个。把 40 亿个值塞进 256 个桶里,必然会有舍入误差。
我把它总结为三个主要来源:
- 截断误差:当 FP32 的数值范围超出 INT8 能表示的范围时,超出部分会被直接截断。比如一个很大的权重值,量化后可能被截断到 127。
- 舍入误差:映射后的值需要四舍五入到最近的整数,这个舍入过程会引入误差。
- 零点漂移:非对称量化中,Zero Point 的计算本身就有误差,这个误差会在多层网络中累积。
那么,怎么权衡?我一般遵循这几个原则:
我的量化决策树:
- 先看精度要求:如果任务对精度极其敏感(比如医疗影像),可以考虑 FP16 量化或者混合精度(部分层用 INT8,敏感层用 FP16)。
- 再看硬件支持:有些芯片的 INT8 计算单元有特殊优化,比如支持 per-channel 量化(每个通道独立 Scale),精度会比 per-tensor 量化好很多。
- 最后做校准:用一小部分真实数据跑一遍模型,统计出每层激活值的 min/max,这个步骤叫“校准”。校准数据集的质量直接影响量化精度。我见过有人随便拿几张图做校准,结果模型直接崩了。
下面这张图展示了量化过程中精度与性能的典型关系:
从图上可以清楚看到,在 FP32 到 INT8 的过渡区间,存在一个“最佳权衡区”。在这个区域内,你只需要牺牲不到 1% 的精度,就能换来 2 倍以上的性能提升。这也是为什么 INT8 量化成为端侧部署的主流选择。
3.4 实战中的量化策略选择
说了这么多理论,最后给点实战建议。我根据项目经验,把常见的量化策略整理成了下面这个表格:
| 策略 | 精度损失 | 性能提升 | 适用场景 |
|---|---|---|---|
| FP32 → FP16 | 几乎无损失(<0.1%) | 1.5x - 2x | 对精度极度敏感,硬件支持 FP16 |
| FP32 → INT8(per-tensor) | 0.5% - 2% | 2x - 4x | 大部分通用场景,快速部署 |
| FP32 → INT8(per-channel) | 0.1% - 1% | 2x - 4x | 对精度要求较高,硬件支持 per-channel |
| 混合精度(FP16 + INT8) | 0.1% - 0.5% | 1.5x - 3x | 模型中有敏感层(如检测头),需要精细控制 |
一个小技巧:如果你不确定用哪种策略,可以先跑一个 per-tensor 的 INT8 量化,看看精度。如果精度掉得不多(<1%),那就直接用。如果掉得厉害,再换成 per-channel 或者混合精度。我一般把这个叫做“量化三明治法”——先做最激进的,再根据结果往回调整。