4. 模型量化基础:FP32到INT8的转换原理、对称与非对称量化

各位同学,今天我们来聊聊模型量化。说实话,这是我在部署项目里最常打交道的一个环节。你想想看,一个模型在GPU上跑得好好的,FP32精度,一放到嵌入式设备上,内存带宽直接爆掉。怎么办?量化就是那个救火队员。

我个人习惯把量化理解成「压缩」。把32位浮点数压缩成8位整数,内存占用直接降到四分之一。但代价呢?精度会掉一点。怎么让精度掉得少?这就是我们今天要讲的核心。

4.1 为什么非要从FP32到INT8?

先问个问题:FP32不是挺好的吗?精度高,范围大。但你看现在的AI芯片,尤其是边缘端,内存带宽就那么点。一个ResNet-50,FP32版本大概98MB,INT8版本直接变成25MB。这还不算完,INT8的乘加运算比FP32快好几倍。

我在项目中遇到过一件事:一个客户要求在树莓派上跑人脸识别。FP32版本,帧率只有3帧,卡得不行。量化到INT8后,直接飙到15帧。客户当场就笑了。所以,量化不是「能不能做」的问题,而是「怎么做得更好」的问题。

4.2 转换原理:从浮点到整数的映射

说白了,量化就是找一个映射关系。把FP32的数值范围映射到INT8的数值范围。INT8能表示256个值(-128到127,或者0到255)。FP32呢?范围太大了,从-3.4e38到3.4e38。但我们关心的不是整个范围,而是模型权重和激活值的实际分布。

映射公式很简单:

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

其中:

  • R 是原始的FP32值
  • Q 是量化后的INT8值
  • scale 是缩放因子(步长)
  • zero_point 是零点偏移

反量化就是反过来:

R = (Q - zero_point) * scale

嗯,这里要注意:round操作会引入量化误差。这个误差没法消除,只能尽量减小。我刚开始做量化时,总觉得round一下没什么,结果模型精度掉了5个点。后来才明白,误差是会累积的。

4.3 对称量化 vs 非对称量化

这是量化里最核心的选择之一。两种方式各有千秋,我分别说说。

4.3.1 对称量化

对称量化,顾名思义,零点在0的位置。也就是说,FP32的0映射到INT8的0。公式简化成:

Q = round( R / scale )

这里zero_point = 0。scale的计算方式:

scale = max(|R|) / 127

为什么是127?因为INT8有符号范围是-128到127。我们取绝对值最大的那个值,然后映射到127。负值就映射到-128到-1。

优点:计算简单,硬件友好。很多AI芯片的卷积加速器直接支持对称量化,不需要额外的零点偏移计算。

缺点:如果数据分布不对称,比如ReLU后的激活值全是非负的,那对称量化会浪费一半的表示范围(负值部分全浪费了)。

我记得有一次部署一个MobileNetV2,激活值全是正的。用对称量化,scale算出来很大,精度掉得厉害。后来换成非对称,立马好了。

4.3.2 非对称量化

非对称量化允许零点偏移。也就是说,FP32的0不一定映射到INT8的0。公式就是最前面那个:

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

scale和zero_point的计算:

scale = (R_max - R_min) / 255
zero_point = round( -R_min / scale )

这里INT8用无符号范围0到255。所以分母是255。

优点:能充分利用INT8的整个表示范围。对于ReLU后的激活值(全是非负),非对称量化效果更好。

缺点:计算时多了零点偏移,硬件实现稍微复杂一点。不过现在大部分芯片都支持了。

4.4 两种方式的对比

特性 对称量化 非对称量化
零点位置 固定为0 可偏移
INT8范围 -128 ~ 127 0 ~ 255
计算复杂度
硬件友好度
适用场景 权重(分布对称) 激活值(分布不对称)

你可能会问:那到底选哪个?我的经验是:权重用对称,激活值用非对称。权重通常分布比较对称,用对称量化不会浪费范围。激活值经过ReLU后全是非负,非对称量化更合适。

4.5 量化误差的来源

量化不是无损的。误差主要来自三个方面:

  1. 舍入误差:round操作会丢失精度。这个没法避免。
  2. 截断误差:超出量化范围的数值会被截断。比如FP32的值大于scale*127,就会被截成127。
  3. 零点偏移误差:非对称量化中,zero_point的round也会引入误差。

避坑指南:我曾经在一个项目中,量化后模型精度掉了8个点。排查了半天,发现是激活值里有几个异常大的值,导致scale算得很大,大部分正常值都被压缩到很小的范围。后来加了「异常值裁剪」,把最大的1%的值截掉,精度只掉了1个点。所以,量化前一定要做数据分布分析。

4.6 实战建议

最后,给几个我在项目中总结的实用建议:

  • 先做校准:用一小批数据跑一遍模型,统计激活值的分布。不要直接用训练数据算scale。
  • 逐层量化:不要整个模型用一个scale。每层单独算,精度会好很多。
  • 混合精度:敏感层(比如第一层和最后一层)可以保留FP16或FP32,其他层用INT8。我试过,精度几乎不掉。
  • 量化感知训练:如果量化后精度掉得厉害,可以考虑在训练时加入伪量化操作(fake quantization),让模型适应量化误差。

好了,这一节的内容就到这里。量化是个实践性很强的东西,光看理论不够。我建议你找个模型,用TensorRT或ONNX Runtime跑一遍量化,看看精度变化。下一节我们会讲量化参数的计算方法,包括MinMax、KL散度等校准算法。到时候再细聊。