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 的计算本身就有误差,这个误差会在多层网络中累积。

那么,怎么权衡?我一般遵循这几个原则:

我的量化决策树:

  1. 先看精度要求:如果任务对精度极其敏感(比如医疗影像),可以考虑 FP16 量化或者混合精度(部分层用 INT8,敏感层用 FP16)。
  2. 再看硬件支持:有些芯片的 INT8 计算单元有特殊优化,比如支持 per-channel 量化(每个通道独立 Scale),精度会比 per-tensor 量化好很多。
  3. 最后做校准:用一小部分真实数据跑一遍模型,统计出每层激活值的 min/max,这个步骤叫“校准”。校准数据集的质量直接影响量化精度。我见过有人随便拿几张图做校准,结果模型直接崩了。

下面这张图展示了量化过程中精度与性能的典型关系:

量化精度与性能权衡 量化程度(从 FP32 → INT8) FP32 INT8 性能 / 精度 精度 性能(FPS) 最佳权衡区 精度损失 < 1% 性能提升 > 2x 注:实际曲线因模型结构和硬件而异,但趋势一致——量化越深,性能越高,精度越低。

从图上可以清楚看到,在 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 或者混合精度。我一般把这个叫做“量化三明治法”——先做最激进的,再根据结果往回调整。