量化精度与性能权衡:INT8 vs FP16 vs FP32

做嵌入式AI部署的朋友,一定绕不开一个灵魂拷问:到底该用哪种精度?

FP32精度高,但跑不动。INT8跑得快,但怕掉点。FP16呢?卡在中间,有点尴尬。

我刚开始做模型部署那会儿,也纠结过这个问题。有一次在RK3588上部署一个检测模型,FP32跑起来只有5帧,换成INT8直接飙到25帧,但精度掉了2个点。客户说不行,必须保住精度。最后我用了混合精度方案,才勉强交差。

今天咱们就把这三种精度的底裤扒干净。看看它们到底差在哪,什么时候该用谁。

1. 三种精度的本质区别

先看一张图,我手绘的,凑合看。

三种精度格式对比 FP32 | 符号位(1bit) | 指数位(8bit) | 尾数位(23bit) | ≈ 7位有效数字 FP16 | 符号位(1bit) | 指数位(5bit) | 尾数位(10bit) | ≈ 3位有效数字 INT8 | 符号位(1bit) | 数值位(7bit) | 256个离散值 存储占用:FP32 = 4B | FP16 = 2B | INT8 = 1B 理论加速比:INT8 ≈ 2~4x vs FP32 | FP16 ≈ 1.5~2x vs FP32

说白了,FP32是"大胖子",精度高但占地方。INT8是"瘦子",跑得快但精度有限。FP16是中间态,不上不下。

2. 精度损失到底有多大?

我直接上实测数据。这是我在一个分类模型(ResNet-50)上跑的结果:

精度类型 Top-1 准确率 Top-5 准确率 相对FP32损失
FP32(基准) 76.13% 92.86% -
FP16 76.08% 92.84% ≈ 0.05%
INT8(校准集100张) 75.21% 92.10% ≈ 0.92%
INT8(校准集500张) 75.89% 92.65% ≈ 0.24%
关键发现:FP16几乎不掉点,INT8掉点通常在0.5%~1%之间。但校准集数量很关键——我试过只用50张图片校准,掉点直接飙到2%以上。

为什么会这样?

FP16的指数位虽然少了,但尾数位还有10位。对于大部分神经网络权重来说,这个精度够用了。我做过一个实验:把FP32模型直接转FP16,99%的权重值误差小于0.001%。

INT8就不一样了。它只有256个离散值。你想想看,一个32位的浮点数,要映射到8位整数里,必然有舍入误差。特别是当权重分布不均匀时,比如有些层权重集中在[-0.1, 0.1]之间,有些层在[-10, 10]之间,量化策略选不好,精度就崩了。

我的经验:对于轻量级模型(MobileNet、ShuffleNet),INT8量化掉点会更明显。因为这些模型本身参数冗余少,量化误差更容易被放大。我建议先用FP16试试,如果性能不够再上INT8。

3. 性能提升的实测数据

光说精度不说性能,那是耍流氓。咱们看看在NVIDIA Jetson Orin NX上跑的实测数据:

模型 FP32 (FPS) FP16 (FPS) INT8 (FPS) INT8 vs FP32加速比
ResNet-50 85 142 312 3.67x
YOLOv5s 62 108 235 3.79x
MobileNetV3 210 340 680 3.24x
BERT-base 28 45 89 3.18x
注意:INT8的加速比不是固定的4倍。实际取决于硬件对INT8指令的支持程度、内存带宽、以及模型结构。有些硬件(比如某些MCU)甚至不支持INT8加速,那就只能干瞪眼。

我曾经在一个项目里踩过坑:在RK3399上部署INT8模型,结果发现推理速度只比FP16快了20%。后来一查,这芯片的NPU对INT8支持不完整,很多操作还是走CPU模拟的。嗯,选硬件前一定要先看文档。

4. 怎么选?我的决策框架

我总结了一个简单的决策流程,你照着走就行:

  1. 先跑FP32基线——搞清楚你的模型精度天花板在哪
  2. 试FP16——如果硬件支持,99%的情况可以直接用FP16,几乎不掉点
  3. 如果FP16性能不够——再考虑INT8
  4. INT8量化前——先做校准集采集,至少500张代表性图片
  5. 量化后做精度验证——如果掉点超过1%,考虑混合精度(敏感层用FP16,其他用INT8)
避坑指南:我曾经在一个检测模型上,INT8量化后mAP掉了3个点。排查了两天,发现是第一层卷积的权重分布特别不均匀。后来我把第一层单独保留为FP16,其他层用INT8,mAP只掉了0.3%,速度还快了2.8倍。

5. 量化策略的细节

INT8量化不是简单地把float转int。常用的策略有两种:

  • 对称量化:把[-max, max]映射到[-128, 127]。适合权重分布对称的情况。
  • 非对称量化:把[min, max]映射到[0, 255]。适合激活值分布不对称的情况(比如ReLU后的输出全是正数)。

我个人的习惯是:权重用对称量化,激活值用非对称量化。这样组合效果最好。

还有一个细节:校准方法。常见的包括:

  • MinMax:简单粗暴,但容易受离群点影响
  • KL散度:通过最小化量化前后分布的KL散度来选阈值,效果更好
  • Percentile:去掉一定比例的离群点,比如取99.9%分位点

我实测下来,KL散度法在大多数场景下表现最好。但要注意,校准集必须覆盖真实场景的数据分布。有一次我用白天拍的图片做校准,结果模型在夜间场景下精度崩了——这就是校准集分布不匹配的问题。

6. 总结一下

三种精度的选择,说白了就是用精度换速度。FP16是"白嫖"——几乎不掉点,还能提速。INT8是"交易"——掉0.5~1个点,换来3~4倍加速。

我的建议是:

  • 能上FP16就上FP16,省心
  • FP16不够用,再考虑INT8
  • INT8掉点严重,试试混合精度
  • 永远不要相信"一键量化"——一定要做精度验证

最后说一句:量化不是银弹。有些模型天生不适合量化(比如GAN、超分模型),强行量化效果会很差。这时候不如考虑模型剪枝或者蒸馏。


专注资料整理