Skip to content

约 3 分钟阅读

03. 舍入、溢出与计算误差 ​

知道刻度有疏密之后,就能回答“精度降低,到底丢了什么”。这一篇先观察 1.0351 怎样舍入,再用一连串加法看误差怎样累积。所有数字都来自明确的表示规则,可以手算核对。

保存时,找最近的刻度 ​

BF16 在 1 附近相邻刻度是 1.03125 和 1.0390625。1.0351 离前者更近,因此保存成 1.03125。绝对误差是两者距离:|1.0351 − 1.03125| = 0.00385。

切换 FP32、FP16、BF16,观察同一个输入落点的变化。再选 FP8 E4M3,刻度会更疏;这个名字中的 E4 和 M3 表示 4 位指数、3 位小数部分,第五篇会继续解释。

BF16:1.0351 → 1.03125;绝对误差 0.00385;[1, 2) 内间距 0.0078125

把观察范围切到“1024 附近”,同一种格式的刻度也会变疏:FP16 的间距从 1/1024 变成 1,BF16 则从 1/128 变成 8。输入 1024.25,就能看到这四分之一落在两个刻度之间。

实验采用就近舍入。输入恰好位于两个刻度中间时,选择保留的二进制最低位为 0 的一边,称为“取偶”。回到“1 附近”,FP8 的间距是 0.125,两个候选是 1.000₂ = 1 和 1.001₂ = 1.125。1.0625 正好居中,会舍入到最低位为 0 的前者。这能减少反复遇到中点时总往同一方向偏的倾向。

小数保存不准,计算还会继续舍入 ​

先做一个刻意简化的实验:两边都从 1024 开始,连续加八次 0.25。一边每次加完立即保存成 FP16,另一边用 FP32 累加,到最后再转 FP16。把次数逐次增加,观察两边的总数:

每步存 FP161024FP32 累加102410241026横轴:计算结果的位置

两边都从 1024 开始。

在这一段范围,FP16 的刻度间隔是 1,1024.25 更接近 1024,每一小步都被舍回原处。FP32 能暂时留住四分之一,八次加完得到 1026,最后转回 FP16 时也能准确表示。

这就是高精度累加有价值的一个直观原因。模型里大量乘积要相加,计算设备可以用较低精度做乘法,同时给累加留更多位。实际矩阵算子如何分块、何时舍入,由后端实现决定;这个例子演示的是每一步保存格式对结果的影响。

数值异常有三种常见来路 ​

舍入:结果仍在范围内,但落不到现有刻度上,于是靠到附近。例如 1.0351 → 1.03125。

溢出:数值太大,超出格式的有限范围。例如 FP16 保存 70000 得到无穷大,记作 Inf。Inf − Inf 会得到无效数,记作 NaN,随后沿计算传下去。

下溢:结果太靠近零,进入次正规区域后,刻度间距固定为该格式的最小正数,无法再跟着数值缩小。FP16 此时相邻刻度始终相差 2^-24,数越靠近零,这段间距占它的比例越大,能保留的有效信息越少。进一步变小后会舍为零,例如 10^-8 转成 FP16 为 0。训练中如果一个很小的调整量这样消失,参数就可能收不到这次调整。

数字差一点,结果会不会变 ​

要看后面用这个数字做什么。若一个检测程序规定分数严格大于 0.5 才通过,基线分数 0.5001 经过 BF16 舍入变为 0.5,判断就会翻转。换成大于等于,则这个例子仍然通过。误差的影响需要连同决策规则一起观察。

生成模型也会比较许多候选分数。两个候选接近时,小差异可能改变先后顺序,进而让后续内容走向另一条路径。因此既要检查数值偏差,也要检查任务结果,例如识别对不对、输出格式是否有效。

精度损失发生在表示和计算的具体位置。下一篇混合精度怎样计算把“乘法省一些、累加留细些”放回模型,解释同一次运行为什么可以使用多种格式。

别急,先让缓存热一下。