2. 数值表示基础

在混合精度部署工程中,所有优化最终都要落到“一个数用什么格式存、用什么格式算”上。INT8 与 FP16 之所以成为主流,并不是因为它们“更小”,而是因为它们在位宽、动态范围、精度和硬件支持之间取得了可工程化的平衡。理解数值表示,是判断“哪里能压、哪里不能压、误差从哪来”的前提。

一、定点与浮点:两种完全不同的“小数点约定”

定点数把小数点位置固定下来,整数部分和小数部分的位数在编译期或量化参数中确定。例如 Q7 表示 1 位符号 + 7 位小数,Q15 表示 1 位符号 + 15 位小数。定点数的优点是运算简单、功耗低、延迟稳定;缺点是动态范围窄,一旦数值超出表示范围就会饱和或溢出。

浮点数则把“有效数字”和“数量级”分开存储:符号位决定正负,指数位决定数量级,尾数位决定有效精度。FP32 是 1+8+23,FP16 是 1+5+10,BF16 是 1+8+7。浮点数的动态范围远大于定点数,但尾数位越少,能保留的有效数字就越少,舍入误差也越明显。

一句话区分:定点数“精度均匀但范围窄”,浮点数“范围宽但精度随数量级变化”。

二、INT8 与 FP16 的位宽、动态范围与精度对照

INT8 是 8 位整数,通常表示 -128~127 或 0~255。它本身没有小数,必须配合 scale 和 zero point 才能映射到真实浮点区间。FP16 是 16 位半精度浮点,符号 1 位、指数 5 位、尾数 10 位,可表示约 ±65504 的有限值,最小正规数约 6.1e-5。

格式 总位宽 组成 典型动态范围 有效精度 典型用途
INT8 8 bit 1 符号 + 7 数值(或 8 位无符号) 约 2^8 个离散档位 取决于 scale,通常 1/127 量级 权重、激活量化,卷积/矩阵乘加速
FP16 16 bit 1 符号 + 5 指数 + 10 尾数 约 1e-5 ~ 6.5e4 约 3~4 位十进制有效数字 激活、归一化、注意力、累加中间值
FP32 32 bit 1 符号 + 8 指数 + 23 尾数 约 1e-38 ~ 3.4e38 约 7 位十进制有效数字 训练、基准、误差对照

从表中可以看出:INT8 的优势是位宽小、算力密度高,但它把“范围”和“精度”都交给了量化参数;FP16 的优势是动态范围足够覆盖大多数推理中间值,同时位宽只有 FP32 的一半,适合做累加和归一化等对范围敏感的操作。

INT8 与 FP16 的位宽结构与数值映射 INT8:8 位整数 + 量化参数 S 7 位数值(-128~127) 真实值 ≈ scale × (q - zero_point) 动态范围由 scale 决定,精度均匀但范围窄 FP16:1 + 5 + 10 半精度浮点 S 指数 5 尾数 10 真实值 = (-1)^S × 1.M × 2^(E-15) 动态范围宽,但尾数少,大数附近精度下降 数值大小 INT8 离散档位 间隔 = scale FP16 小数量级 间隔小、精度高 FP16 大数量级 间隔大、精度低 误差来源:量化舍入、截断、饱和、累加溢出、指数下溢

三、误差来源:从“表示误差”到“累积误差”

数值格式带来的误差不是单一来源,而是多层叠加的。第一层是表示误差:INT8 只能表示有限个离散值,真实值必须四舍五入到最近的档位;FP16 的尾数只有 10 位,超过有效位数的部分会被舍入。第二层是量化误差:scale 选得太大,小数值会被压成同一个整数;scale 选得太小,大数值会饱和截断。第三层是累加误差:卷积和矩阵乘中大量乘加运算如果一直用低精度累加,误差会随累加次数增长。

在工程中,最常见的误差表现是:模型整体精度下降不大,但某些层输出出现明显偏移,或者分类置信度分布变平。这通常不是“格式选错了”,而是量化参数没有覆盖真实激活分布,或者累加路径没有用 FP16/FP32 保护。

不要只看单层误差。低精度部署的误差是逐层传播的,前层的小偏差可能在后层被放大。必须用端到端指标验证,而不是只看某一层的余弦相似度。

四、方法流程:如何为每一层选择数值格式

  1. 统计每层权重和激活的数值分布,记录 min、max、均值、方差和离群值比例。
  2. 对权重优先尝试 INT8 对称量化;对激活根据分布选择对称或非对称量化。
  3. 对归一化、Softmax、注意力分数等对动态范围敏感的操作,保留 FP16 或 FP32。
  4. 累加器统一使用 FP16 或 FP32,避免 INT8 累加溢出。
  5. 用校准集重新估计 scale,再跑端到端精度对比,定位误差最大的层。
# 典型量化配置片段(示意)
weight:
  dtype: int8
  scheme: symmetric
  granularity: per_channel
activation:
  dtype: int8
  scheme: asymmetric
  granularity: per_tensor
  calibration: minmax
accumulator:
  dtype: fp16
sensitive_ops:
  - softmax
  - layernorm
  - attention_score
  dtype: fp16

五、案例与复盘:同一模型,不同格式的误差表现

假设一个视觉分类模型,骨干网络全部 INT8,分类头保留 FP16。校准集只有 200 张图,且亮度偏暗。部署后发现整体 Top-1 只下降 0.3%,但暗光样本下降 4%。复盘发现:激活的 min/max 被亮样本拉大,暗光样本的激活被压到很少的几个整数档位,信息损失严重。解决办法是把激活量化改为 per-channel 或使用移动平均校准,并对第一层和最后一层保留 FP16。

另一个常见误区是“INT8 一定比 FP16 快”。在支持 INT8 张量核心的硬件上,INT8 吞吐确实更高;但如果某层因为精度问题必须回退到 FP16,频繁的格式转换反而会引入额外开销。因此,混合精度部署的关键不是“全部换成 INT8”,而是让每一层都落在“精度可接受且硬件高效”的格式上。

记住三个判断:范围够不够用看动态范围,精度够不够用看有效位数,误差能不能接受看端到端指标。三者缺一不可。