量化数据类型:INT8、INT4、FP16、BF16 的区别与选择
做 NPU 部署,选量化数据类型是第一步。选错了,后面全白干。
我见过不少团队,一上来就上 INT4,结果精度崩了,回头查才发现模型对动态范围特别敏感。也有项目死守 FP16,功耗压不下去,NPU 带宽跑满,帧率就是上不来。
说白了,数据类型的选择就是一场「精度 vs 效率」的博弈。你得知道每种数据类型的脾气,才能用好它。
主流量化数据类型一览
目前 NPU 部署中,常见的量化数据类型就四种:INT8、INT4、FP16、BF16。它们的核心差异在三个维度:位宽、动态范围、精度。
| 数据类型 | 位宽 | 动态范围 | 精度(尾数位) | 典型场景 |
|---|---|---|---|---|
| INT8 | 8 bit | [-128, 127] | 均匀量化,无指数 | 卷积层、全连接层 |
| INT4 | 4 bit | [-8, 7] | 均匀量化,无指数 | 激活值、权重压缩 |
| FP16 | 16 bit | ±65,504 | 10 bit 尾数 | 梯度、敏感层 |
| BF16 | 16 bit | ±3.4×10³⁸ | 7 bit 尾数 | 大动态范围场景 |
嗯,这里要注意:INT8 和 INT4 是定点数,FP16 和 BF16 是浮点数。定点数计算快、功耗低,但动态范围窄;浮点数动态范围大,但硬件开销也大。
INT8:部署的黄金标准
INT8 是目前 NPU 部署的主力。为什么?因为大部分模型在 INT8 下精度损失可以控制在 1% 以内,而推理速度能翻倍。
我在项目中遇到过最典型的案例:一个 ResNet-50 分类模型,FP32 精度 76.3%,INT8 量化后精度 75.8%,只掉了 0.5%。但推理延迟从 12ms 降到了 6ms,吞吐量直接翻倍。
INT8 量化核心公式:
scale = (max_val - min_val) / 255
zero_point = round(-min_val / scale)
quantized_val = round(float_val / scale) + zero_point
其中 scale 是缩放因子,zero_point 是零点偏移。这两个参数决定了量化精度。
INT8 的坑在哪?我踩过。当模型某些层的激活值分布极度不均匀时,比如大部分值集中在 0 附近,少数离群值很大,这时候均匀量化会严重损失精度。
避坑指南:我曾经在一个检测模型上直接做 INT8 量化,结果 mAP 掉了 5%。排查后发现是某个特征层的激活值有长尾分布。解决办法是:对该层做 per-channel 量化,或者改用 KL 散度校准算法,而不是默认的 min-max 校准。
INT4:极限压缩的利器
INT4 是最近两年才火起来的。它能把模型体积压缩到 INT8 的一半,对带宽敏感的场景特别友好。
但 INT4 的精度损失明显更大。你想想看,INT8 有 256 个量化等级,INT4 只有 16 个。信息量差了 16 倍。
我个人习惯只在两种情况下用 INT4:
- 权重量化:权重的分布通常比较规整,INT4 可以接受
- 非敏感层:比如网络浅层的激活值,对精度影响较小
千万别在 attention 层或分类头用 INT4。我试过,精度直接跳水。
FP16 vs BF16:浮点数的两个流派
FP16 和 BF16 都是 16 位浮点数,但设计哲学完全不同。
| 特性 | FP16 | BF16 |
|---|---|---|
| 指数位 | 5 bit | 8 bit |
| 尾数位 | 10 bit | 7 bit |
| 动态范围 | ±65,504 | ±3.4×10³⁸ |
| 精度 | 高(尾数多) | 低(尾数少) |
| 适用场景 | 梯度、小数值 | 大动态范围 |
FP16 的尾数位多,精度高,但动态范围窄。训练时梯度容易下溢或上溢。BF16 正好相反,它保留了和 FP32 一样的 8 位指数,动态范围极大,但尾数位少,精度低。
我记得有一次做 BERT 部署,用 FP16 推理时某些层的激活值直接变成 NaN。排查后发现是 softmax 后的值太小,FP16 表示不了。换成 BF16 就没事了,因为它的动态范围大得多。
实战建议:如果你的 NPU 支持 BF16,优先用 BF16 做浮点推理。它的动态范围接近 FP32,基本不会出现溢出问题。精度损失通常可以忽略。
混合精度量化策略
单一数据类型很难满足所有需求。所以就有了混合精度量化——不同层用不同的数据类型。
我常用的混合精度策略分三步:
- 敏感度分析:逐层量化到 INT8,看精度损失。损失大的层标记为「敏感层」
- 分层配置:敏感层用 FP16 或 BF16,非敏感层用 INT8,极端压缩场景用 INT4
- 迭代调优:跑一遍验证集,调整分层策略,直到精度和性能都达标
下面这张图展示了混合精度量化的完整流程:
实战中的选择建议
说了这么多,到底怎么选?我总结了一个快速决策表:
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 精度优先,带宽充足 | 全 FP16 或 BF16 | 几乎无损,部署简单 |
| 性能优先,精度容忍 | 全 INT8 | 速度翻倍,精度损失可控 |
| 极致压缩,边缘设备 | INT8 + INT4 混合 | 体积减半,带宽友好 |
| 大模型,动态范围大 | BF16 + INT8 混合 | BF16 兜底敏感层 |
| 训练后量化(PTQ) | INT8 为主 | 工具链成熟,校准方便 |
最后提醒一句:别信任何「万能量化方案」。每个模型、每颗 NPU 都有自己的脾气。我习惯的做法是:先跑一遍全 INT8,看精度;精度不行就做敏感度分析,上混合精度;还不行就换 BF16 兜底。迭代三次,基本能找到一个最优解。