AI 计算中的数值精度与量化基础
结合chatgpt生成
在大模型训练和推理中,FP32、FP16、BF16、INT8 等格式决定了数据的表示范围、计算精度、存储空间和运行速度。本文简要梳理常见浮点格式、误差来源、Softmax 数值稳定性以及 INT8 量化。
1. FP32、FP16 与 BF16
浮点数一般由三部分组成:
其中:
- 符号位决定正负;
- 指数位决定数值范围;
- 尾数位决定有效精度。
| 格式 | 符号位 | 指数位 | 尾数位 | 特点 |
|---|---|---|---|---|
| FP32 | 1 | 8 | 23 | 范围大、精度高、开销较大 |
| FP16 | 1 | 5 | 10 | 精度较高,但数值范围较小 |
| BF16 | 1 | 8 | 7 | 范围接近 FP32,但精度低于 FP16 |
FP32
FP32,也就是通常所说的 float32,是 IEEE 754 单精度浮点数。
它大约能够提供 7 位十进制有效数字,最大有限值约为:
FP32 精度和动态范围都比较好,但每个数需要 4 字节,计算与访存开销较大。
FP16
FP16 每个数只需要 2 字节,因此能减少显存占用和内存带宽,并且可以使用 GPU Tensor Core 加速。
但 FP16 只有 5 位指数,最大有限值只有:
之所以不是 65535,是因为浮点数在这个数量级下不能逐个表示整数。
因此 FP16 在训练中容易发生:
- 上溢:数值过大,变成
inf; - 下溢:数值过小,变成 0;
- 舍入误差:较小的增量无法被保留。
BF16
BF16 也占 16 bit,但它保留了与 FP32 相同的 8 位指数,因此动态范围与 FP32 接近。
代价是 BF16 只有 7 位尾数,精度比 FP16 更低。
可以这样理解:
- FP16:尾数更多,精度更高,但范围较小;
- BF16:指数更多,范围更大,但精度较低。
大模型训练中的激活值和梯度可能变化很大,因此 BF16 通常比 FP16 更不容易溢出,也往往不需要复杂的 loss scaling。
2. 为什么 FP16 乘法常用 FP32 累加
矩阵乘法中的一个输出元素,本质上是点积:
即使 \(a_i\) 和 \(b_i\) 使用 FP16 存储,累加过程也经常使用 FP32:
原因是点积通常包含大量加法。FP16 精度有限,直接用 FP16 累加会让舍入误差不断积累。
例如,当前累加结果已经很大,而新加入的数很小时:
较小的 \(x\) 可能因为 FP16 尾数不足而完全丢失。
使用 FP32 累加可以:
- 降低累计舍入误差;
- 减少溢出风险;
- 提高矩阵乘法的数值稳定性。
很多 GPU 的混合精度矩阵乘法都支持 FP16 或 BF16 输入、FP32 累加,最后再根据需要转换为低精度格式。
类似地,INT8 矩阵乘法一般使用 INT32 进行累加。
3. 绝对误差与相对误差
设真实值为 \(x\),计算结果为 \(\hat{x}\)。
绝对误差
绝对误差表示计算结果与真实值相差多少。
例如:
则绝对误差为:
相对误差
上面的相对误差为:
相对误差反映误差相对于真实值的大小。
如果真实值接近 0,相对误差会变得非常大甚至无法定义。因此工程测试中常使用:
其中:
atol:允许的绝对误差;rtol:允许的相对误差。
这也是 NumPy、PyTorch 等框架进行数值对齐时常见的判断方式。
4. Reduction 顺序为什么会影响结果
Reduction 指把一组数据归约成一个值,例如求和、求最大值或者计算均值。
数学中的加法满足结合律:
但是浮点数精度有限,每次运算都可能发生舍入,因此浮点加法并不严格满足结合律:
例如在有限精度下:
中间的 1 可能因为精度不足而丢失,结果得到 0。而改变计算顺序:
则可以得到 1。
在并行计算中,不同线程会分别计算部分结果,最后再进行树形归约:
顺序求和:(((a+b)+c)+d)
树形归约:(a+b)+(c+d)
不同的线程数量、分块方式和执行顺序都可能改变加法顺序,从而产生微小差异。
因此:
- CPU 与 GPU 的结果可能不完全相同;
- 单线程与多线程结果可能有微小差异;
- 并行程序有时难以做到逐 bit 一致。
这通常不是逻辑错误,而是浮点运算的正常现象。数值测试应该使用合理的误差范围,而不是直接使用 == 比较浮点结果。
5. INT8 量化基础
量化是把浮点数映射到有限的整数范围。对于 INT8:
常用的仿射量化公式为:
反量化公式为:
其中:
- round 是四舍五入取整函数
- \(x\):原始浮点数;
- \(q\):量化后的整数;
- \(s\):scale,缩放因子;
- \(z\):zero point,零点;
- \(\hat{x}\):反量化后的近似值。
Scale
Scale 表示整数单位对应多少浮点数值。
对于非对称量化,可以使用:
Scale 越小,量化间隔越细,但能够覆盖的数值范围也越小。
Zero Point
Zero point 用于保证浮点数 0 可以准确映射到某个整数:
并将其截断到合法整数范围。
对称量化通常令:
它实现简单、计算高效,但如果数据分布明显不对称,可能浪费一部分 INT8 表示范围。
6. Per-Tensor 与 Per-Channel
Per-Tensor 量化
整个张量共享同一组 scale 和 zero point:
优点是实现简单、额外存储少。缺点是如果张量中不同区域的数值范围差异很大,量化误差可能较大。
Per-Channel 量化
不同通道分别使用自己的量化参数:
例如线性层权重:
通常可以为每个输出通道设置一个 scale:
Per-channel 能适应不同通道的数值分布,通常比 per-tensor 精度更好,但会增加量化参数和实现复杂度。
实际推理中常见的选择是:
- 权重:适合 per-channel 量化;
- 激活值:经常采用 per-tensor 或 per-token 量化;
- 对精度要求较高时,使用更细粒度的 group-wise 量化。
7.Group-wise 量化
Group-wise 量化(分组量化)是 per-tensor 和 per-channel 之间的一种折中方案:把张量切成若干组,每组共享一套量化参数 scale 和 zero point。
设一个权重矩阵为:
沿输入维度每 \(G\) 个元素分为一组:
第 \(g\) 组使用自己的 \(s_g,z_g\):
反量化为:
例如一行有 4096 个权重,group_size=128,那么这一行会被分为:
每组拥有独立的 scale。

浙公网安备 33010602011771号