2、量化基础回顾:浮点数表示(FP32/FP16/BF16)、定点数与整数量化、量化误差来源分析
2.1 浮点数表示:FP32、FP16 与 BF16
聊量化之前,得先把浮点数这老本行捋清楚。说白了,计算机里存小数,靠的就是浮点数标准——IEEE 754。我最早接触这玩意儿是在做图像分类模型部署的时候,那时候模型跑在 GPU 上,精度稳稳的,压根没想过底层怎么存。直到后来要压模型体积,才被迫把浮点数扒了个底朝天。
浮点数由三部分组成:符号位、指数位、尾数位。公式长这样:
值 = (-1)^符号位 × 2^(指数位 - 偏移量) × (1 + 尾数位 / 2^尾数位数)
嗯,看着有点绕。我换个说法:符号位决定正负,指数位决定范围,尾数位决定精度。你想想看,指数位越多,能表示的数越大;尾数位越多,小数能分得越细。
咱们常用的三种浮点格式,我直接列个表:
| 类型 | 总位数 | 符号位 | 指数位 | 尾数位 | 最大可表示值 | 最小正正规数 |
|---|---|---|---|---|---|---|
| FP32 | 32 | 1 | 8 | 23 | ~3.4×10³⁸ | ~1.18×10⁻³⁸ |
| FP16 | 16 | 1 | 5 | 10 | ~6.55×10⁴ | ~6.10×10⁻⁵ |
| BF16 | 16 | 1 | 8 | 7 | ~3.4×10³⁸ | ~1.18×10⁻³⁸ |
FP32 是标准精度,也叫单精度。指数位 8 位,尾数位 23 位,精度大概 7 位有效数字。我在项目中遇到过,大部分训练任务用 FP32 都不会出问题,但显存占用确实大。
FP16 是半精度。指数位只有 5 位,范围窄了很多。尾数位 10 位,精度也低。我记得第一次用 FP16 做训练,梯度直接炸了——因为数值范围不够,小梯度直接变零。后来才知道,FP16 的表示范围只有 6.55×10⁴,稍微大一点的激活值就溢出了。
BF16 是 Google 搞出来的。它保留了 FP32 的 8 位指数位,所以范围跟 FP32 一样大。但尾数位砍到了 7 位,精度只有 2-3 位有效数字。说白了,BF16 牺牲精度保范围。我个人习惯在训练大模型时用 BF16,因为梯度不容易炸,而且现代硬件对 BF16 支持很好。
核心结论:FP32 精度高但占内存;FP16 省内存但范围窄;BF16 是折中方案,范围大精度低。量化时,我们通常把 FP32 的权重映射到 INT8 或 INT4,这中间就得考虑浮点数的分布特性。
2.2 定点数与整数量化
浮点数讲完了,咱们聊聊定点数。定点数,顾名思义,小数点位置是固定的。比如 Q7.8 格式,7 位整数位,8 位小数位。定点数计算快,但范围固定,容易溢出。
整数量化,说白了就是把浮点数映射到整数。公式很简单:
q = round(r / s) + z
其中 r 是原始浮点数,q 是量化后的整数,s 是缩放因子,z 是零点偏移。反量化就是:
r ≈ (q - z) × s
我举个例子。假设权重范围是 [-1.0, 1.0],要量化到 INT8(范围 -128 到 127)。那么:
s = (1.0 - (-1.0)) / (127 - (-128)) = 2.0 / 255 ≈ 0.00784
z = round(0 - (-1.0) / 0.00784) = round(127.5) = 128
嗯,这里要注意:零点偏移 z 是为了让浮点数的 0 能精确映射到整数 0。为什么?因为很多神经网络里,0 有特殊意义(比如 padding、ReLU 的输出)。如果 0 映射不准,模型精度会掉得厉害。
我在项目中遇到过,有一次做 INT8 量化,模型精度掉了 5 个点。排查了半天,发现是零点偏移算错了,导致卷积层里大量 0 值被映射成了非零整数,特征图全乱了。从那以后,我每次做量化都会先检查零点映射对不对。
个人经验:对称量化(z=0)实现简单,但非对称量化(z≠0)能更好地匹配数据分布。如果数据分布不对称(比如 ReLU 后的激活值全是非负的),非对称量化效果更好。
2.3 量化误差来源分析
量化不是无损的。误差来源主要有三个:
- 舍入误差:浮点数映射到整数时,小数部分被截断或四舍五入。这个误差范围是 ±0.5 个量化步长。
- 截断误差:超出量化范围的数值被强制截断到边界值。比如 INT8 范围是 [-128, 127],如果权重是 200,就会被截成 127。
- 精度损失:量化后的整数精度远低于浮点数。FP32 有 23 位尾数,INT8 只有 8 位,信息量差了 15 位。
你想想看,这三种误差叠加起来,模型精度能不降吗?
我画了一张图,帮你理解量化误差的分布:
从图上能看出来,量化后的值只能落在离散的阶梯上,跟原始连续曲线之间必然有偏差。偏差大的地方,就是误差集中的区域。
避坑指南:我曾经在量化一个 NLP 模型时,发现某些层的权重分布特别不均匀——大部分值集中在 0 附近,少数值特别大。这种情况下,如果直接用均匀量化,小值区域的精度会损失殆尽。后来我改用非均匀量化(比如对数量化),才把精度拉回来。所以,量化前一定要先看数据分布,别盲目套公式。
量化误差还有一个容易被忽略的点:误差累积。单层量化误差可能只有 0.1%,但模型有几十层,误差一层层传下去,最后可能放大到 5% 甚至 10%。这就是为什么有些模型量化后直接崩了——不是某一层出了问题,而是误差在深层网络里滚雪球了。
我个人习惯的做法是:先做逐层量化误差分析,找到误差最大的几层,然后对这些层做混合精度(比如权重用 INT8,激活用 FP16)。这样既能压体积,又能保住精度。
总结一下:量化误差不可避免,但可以控制。关键是要理解误差来源——舍入、截断、精度损失——然后针对性地做优化。比如用校准数据集来选最优的缩放因子,或者用 KL 散度来匹配量化前后的分布。这些技巧,后面章节会详细讲。