qinghuan
← 回到知识探索

AI 直觉 / 可下载小项目

随机舍入能保住平均值,能保住损失吗?

用精确分数追踪 1.3 的随机舍入、十次微小更新与二位随机数,区分无偏、方差和训练效果。

展开本文目录
  1. 下载完整练习包
  2. 向上舍入的概率来自距离
  3. 平方以后,多出来的是波动
  4. 小更新能积起来,但每条路径不同
  5. 随机数本身也有精度合同
  6. 运行完整实验并检查报告
  7. 记住这些要点
  8. 别把例子用过头
  9. 自己试一小步
  10. 查原始资料
开始前,会这些就够了
遇到不熟悉的词,先看这 2 个解释
随机舍入
根据待存值到上下可表示数的距离,随机选择其中一个。
舍入方差
舍入后的数围绕平均值波动的程度。
打开完整术语小抄 ↗

只能存整数时,1.3 总被舍成 1,会丢掉 0.3。另一种做法是七成概率存 1、三成概率存 2。平均仍是 1.3,但每次存下来的值都会有误差。

这就是本文研究的按距离随机舍入。我们用有理数精确计算所有概率,不靠一次随机运行碰巧接近答案,也不把理想概率模型冒充真实硬件精度。

把例子带到自己的电脑上

下载完整练习包

需要 Python 3.10 或更新版本,无第三方依赖;制作时实测 Windows / Python 3.14.0。使用标准库,不需要安装第三方包。下载 ZIP 后选择“全部提取”,进入能看到 README.md 的文件夹。

下载 ZIP · 5 个文件
包里有什么
  • stochastic_rounding.py:完整可运行示例
  • README.md:运行说明与独立练习
  • observed.stdout.txt:本次实际输出
  • verification.json:源码和产物验证记录
  • SHA256SUMS.txt:内容摘要

在这个文件夹打开 PowerShell,逐行运行

python --version
python -B stochastic_rounding.py

解压到独立练习文件夹,在含 stochastic_rounding.py 的目录打开终端运行。已运行 Fraction 精确概率、十步固定更新分布与有限随机位反例;未模拟 IEEE 浮点编码、评测硬件随机数或训练模型。

找不到命令或文件时,先按 README 检查环境和所在目录。文中标出的阅读时间只估算文章篇幅,安装环境和动手练习可按自己的节奏进行。

向上舍入的概率来自距离

设 x 夹在两个可表示数 a<b 之间。向上取 b 的概率 p=(x−a)/(b−a),向下取 a 的概率 1−p。a、b 是邻近可表示数,未必是整数,也未必与别的区间等距。

对 x=1.3、a=1、b=2,p=0.3。期望 E[Q]=(1−p)a+pb=1.3。期望是在所有可能结果上做概率平均,不是说每次输出都等于 1.3。若无条件各取一半,期望变成 1.5,不符合本文的无偏规则。

程序使用 Fraction 保存 13/10、7/10、3/10,以排除十进制转二进制带来的额外误差。负数 −1.3 的邻点为 −2 与 −1,也使用同一个距离公式。

平方以后,多出来的是波动

令 Q 表示随机舍入后的结果。方差 Var(Q)=p(1−p)(b−a)²,在这里是 0.3×0.7=0.21。相同的平均值,并不能把这个波动抹去。

取目标 t=1。未舍入时损失 (1.3−1)²=0.09;舍入后以七成概率损失为 0、三成概率损失为 1,所以期望损失为 0.3。两者相差 0.21,正好是方差。

一般地,E[(Q−t)²]=(E[Q]−t)²+Var(Q)。无偏只让 E[Q]=x,右边仍然有方差项。因此“平均参数没变”不能推出“平均损失没变”,更不能推出训练准确率一定相同。

小更新能积起来,但每条路径不同

从整数状态 0 出发,每次加 0.1,再存回整数。如果每次取最近整数,十次都回到 0。若每一步按距离随机舍入,给定当前整数状态,每步有 0.1 概率前进 1。

程序枚举十步后的所有状态及其概率,结果总概率为 1、最终均值为 1、方差为 0.9。它展示了微小更新在期望上没有被全部吞掉,但单次路径可能仍然停在零,也可能走到更大的整数。

这段推理的更新量始终固定为 0.1。真正训练时,下一次梯度取决于已经舍入的参数;不能把这里的线性累加结论直接推广成整条训练轨迹等于高精度轨迹。

随机数本身也有精度合同

理想公式允许任意实数概率,实际机器只有有限随机位。作为反例,令 r 均匀取 0、1、2、3,计算 floor(1.3+r/4),四个结果的平均只有 1.25。随机了,并不自动无偏。

Graphcore 的讨论进一步推导了有限随机位的影响。本文只复现这个简化反例,不实现浮点位级转换,也不评测硬件随机数或提出某个随机位数对所有输入都足够。

接入训练前应明确表示范围、上下邻点、溢出处理、概率生成方式及重复运行规则。本文的有理数实验只核对数学性质;实际低精度训练还需要单独测损失与任务指标。

运行完整实验并检查报告

保存为 stochastic_rounding.py,运行 python -B stochastic_rounding.py。预期输出来自本次实际运行;完整结果写入 report.json。

已运行 Fraction 精确概率、十步固定更新分布与有限随机位反例;未模拟 IEEE 浮点编码、评测硬件随机数或训练模型。

展开收起代码 · 小实验python · 67 行

长行可在代码区域内左右滚动,按 Tab 可把焦点移入代码区。

小实验 · python
"""Exact rational stochastic rounding and update accumulation."""
from fractions import Fraction as F
import json
from pathlib import Path


def choices(x,low,high):
    if high <= low or not low <= x <= high:
        raise ValueError('ordered enclosing neighbors required')
    up=(x-low)/(high-low)
    return [(low,1-up),(high,up)]


def moments(rows):
    mean=sum(value*p for value,p in rows)
    variance=sum(p*(value-mean)**2 for value,p in rows)
    return mean,variance


def main():
    x,low,high=F(13,10),F(1),F(2)
    rows=choices(x,low,high)
    mean,variance=moments(rows)
    loss=sum(p*(value-F(1))**2 for value,p in rows)
    ideal_loss=(x-F(1))**2
    assert mean==x and variance==F(21,100)
    assert loss==ideal_loss+variance==F(3,10)
    # Ten independent conditional rounds of integer state + 1/10.
    states={F(0):F(1)}
    for _ in range(10):
        next_states={}
        for value,prob in states.items():
            for rounded,weight in choices(value+F(1,10),value,value+1):
                next_states[rounded]=next_states.get(rounded,F(0))+prob*weight
        states=next_states
    accumulated_mean,accumulated_var=moments(list(states.items()))
    assert sum(states.values())==1 and accumulated_mean==1 and accumulated_var==F(9,10)
    # Round-to-nearest stagnates at 0 on each +0.1 update.
    nearest=0
    for _ in range(10): nearest=round(nearest+F(1,10))
    assert nearest==0
    negative=choices(F(-13,10),F(-2),F(-1))
    assert moments(negative)[0]==F(-13,10)
    # A deliberately crude 2-bit random grid with floor(x + r/4).
    coarse=[(x+F(r,4)).numerator//(x+F(r,4)).denominator for r in range(4)]
    coarse_mean=sum(map(F,coarse))/4
    assert coarse_mean==F(5,4) and coarse_mean != x
    rejected=0
    for args in [(F(1),F(1),F(1)),(F(3),F(1),F(2))]:
        try: choices(*args)
        except ValueError: rejected+=1
    assert rejected==2
    report=dict(x=str(x),probabilities=[(str(a),str(p)) for a,p in rows],mean=str(mean),
        variance=str(variance),expected_squared_loss=str(loss),ideal_squared_loss=str(ideal_loss),
        accumulation_distribution={str(k):str(v) for k,v in states.items()},
        accumulation_mean=str(accumulated_mean),accumulation_variance=str(accumulated_var),
        nearest_accumulation=nearest,coarse_grid_mean=str(coarse_mean),checks_passed=True)
    Path('report.json').write_text(json.dumps(report,indent=2)+'\n',encoding='utf-8')
    print('round 1.3: P(1)=0.7; P(2)=0.3')
    print(f'exact mean={float(mean):.6f}; variance={float(variance):.6f}')
    print(f'squared loss to target 1: expected={float(loss):.6f}; unrounded={float(ideal_loss):.6f}')
    print(f'ten +0.1 updates: nearest={nearest}; SR expectation={float(accumulated_mean):.6f}; variance={float(accumulated_var):.6f}')
    print(f'2-bit floor-grid expectation={float(coarse_mean):.6f}')
    print('checks=PASS; report.json written')


if __name__=='__main__': main()

怎样运行:在独立练习文件夹中执行 python -B stochastic_rounding.py。2026-10-18 已在 Windows、Python 3.14.0 运行此份源码并通过自带检查。已运行 Fraction 精确概率、十步固定更新分布与有限随机位反例;未模拟 IEEE 浮点编码、评测硬件随机数或训练模型。

运行后对照结果
round 1.3: P(1)=0.7; P(2)=0.3
exact mean=1.300000; variance=0.210000
squared loss to target 1: expected=0.300000; unrounded=0.090000
ten +0.1 updates: nearest=0; SR expectation=1.000000; variance=0.900000
2-bit floor-grid expectation=1.250000
checks=PASS; report.json written

记住这些要点

  • 按距离分配上下舍入概率,才能得到本文的无偏性质。
  • 平方损失的期望还包含舍入方差。
  • 固定小更新可在期望上积累,不代表每条路径相同。
  • 有限随机位可能改变概率与偏差。

别把例子用过头

  • 把上下各一半误认为按距离随机舍入。
  • 从参数期望不变推导任务损失或准确率不变。
  • 把固定更新的累加实验写成真实训练收益。

自己试一小步

把格点间距从 1 改成 0.5,平方损失中的额外项会怎样变?

  1. 给 1.3 选择新的上下邻点,并手算概率。
  2. 比较均值、方差与目标为 1 时的期望平方损失。
  3. 把粗随机网格改为 3 位和 4 位,保留每个结果及其概率。

做到这些,就算完成

  • 概率和为 1,所有输出都在格点上。
  • 区分理想概率与有限随机网格。
  • 用方差解释损失差异,不只报告一个均值。

查原始资料

想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。