Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

统计与实验中的不确定性

你给打印机换了一种调度方法,平均耗时从 60 秒降到 59 秒。看起来快了一点,可是换个时间再测,旧方法也可能跑到 58 秒。

这一秒到底是改进,还是恰巧遇到了几次比较顺利的任务?

上一章从已知分布出发,计算可能的结果。这一章反过来:我们手上只有有限的观测,希望借它们了解更大的世界。统计方法能帮助我们表达这种推断中的不确定性,也提醒我们别让一个漂亮的小数承担太多结论。

1. 手上的记录,不是整个世界

假设我们关心“同一台设备、相同设置下,20 页文档的打印耗时”。如果可以在目标条件下不断重复任务,所有可能产生的耗时共同构成我们要研究的总体。

实际只做了六次,记录为:

56, 58, 59, 61, 62, 64 56,\ 58,\ 59,\ 61,\ 62,\ 64

这六个数是一个样本。为了方便学习,它们是构造的教学数据,不是真实实验。

总体的平均耗时记为 μ\mu,我们通常不知道它;样本的平均耗时则能直接算出:

yˉ=56+58+59+61+62+646=60 \bar y=\frac{56+58+59+61+62+64}{6}=60

yˉ\bar y 读作“yy 平均”。它是用样本估计总体均值的一种办法。能够算到 60,不等于已经证明总体均值恰好是 60。

先问抽到了什么,再问算得准不准

如果只在机器空闲时记录,却想预测高峰时段的体验,问题首先出在样本代表性。再精细的公式,也不会自动把没有见过的高峰任务补进来。

同样,10000 条记录也可能只来自一台特殊设备,或者包含大量重复副本。数量大,不等于覆盖面广;少报、漏报和选择性收集,都会影响结论。

统计推断始终需要一句背景说明:我们想把结果推广到哪些任务,而这些记录又是怎样来的?

2. 平均值也会随着抽样摇摆

假设确实存在一个总体,均值为 60 秒、标准差为 6 秒,并且耗时服从正态分布。这个设定只用于模拟,让我们知道“真答案”在哪里。

现在每次抽六条记录,算一个平均值;重复 1000 次,就得到 1000 个不同的样本均值。再把每次的样本量改成 60:

图 1:真均值没有变化,变化的是每次抽到的样本。样本量增加后,平均值通常更稳定。

样本均值本身也有一个分布,叫它的抽样分布。这句话不需要神秘的想象:重复抽样,每次算一次平均,再把这些平均值画出来,就是图中的分布。

在独立同分布且方差有限的条件下,若单次结果的总体标准差为 σ\sigma,样本均值的标准差为:

SD⁡(Yˉ)=σn \operatorname{SD}(\bar Y)=\frac{\sigma}{\sqrt n}

它叫均值的标准误。nn 是每次抽到的样本数量。

这意味着,要让标准误大致减半,样本量需要变成四倍,而不是两倍。也意味着:如果记录彼此相关,不能不加检查地套用这个 1/n1/\sqrt n 的缩小规律。

3. 标准差与标准误,名字相像,任务不同

真实项目通常不知道 σ\sigma,于是先从样本估计单次结果的波动。

样本方差常用:

s2=1n−1∑i=1n(yi−yˉ)2 s^2=\frac{1}{n-1}\sum_{i=1}^{n}(y_i-\bar y)^2

分母是 n−1n-1。我们先用同一批数据估计了均值,围绕这个样本均值计算的平方偏差会有偏小倾向;在独立同分布条件下,用 n−1n-1 可以使这里的样本方差成为总体方差的无偏估计。

先记住用途即可,不必立刻证明。它与上一章已知整个分布时计算方差的方式不同,也不是说平方根 ss 本身就一定是标准差的无偏估计。

对于六条教学记录,离均值的偏差为 −4,−2,−1,1,2,4-4,-2,-1,1,2,4,平方和为 42,所以:

s2=425=8.4,s≈2.90 s^2=\frac{42}{5}=8.4,\qquad s\approx2.90

据此估计样本均值的标准误:

SE⁡^=s6≈1.18 \widehat{\operatorname{SE}}=\frac{s}{\sqrt6}\approx1.18

两个数都以秒为单位,却回答不同问题:

量本例数值描述什么
样本标准差 ss约 2.90 秒单次耗时的波动
估计标准误 s/ns/\sqrt n约 1.18 秒用这批样本估计均值的不确定性

增加样本量,通常能让均值估得更稳,却不会自动让打印机每次运行都更稳定。即使我们把平均耗时估得非常准确,某一次任务仍然可能明显偏离它。

4. 与其只给一个点,不如给一段区间

如果这些数据可以看作独立的正态样本,而总体方差未知,可以用 t 区间估计总体均值:

yˉ±t0.975,n−1sn \bar y\pm t_{0.975,n-1}\frac{s}{\sqrt n}

这表示“以样本均值为中心,向两侧各延伸一个半径”。

t0.975,n−1t_{0.975,n-1} 来自 t 分布,是用于构造双侧 95% 区间的临界值;0.975 对应左侧累计概率 97.5%,另外 2.5% 留在右尾,左尾也留 2.5%。n−1n-1 称为自由度。

本例 n=6n=6,临界值约为 2.571。于是:

60±2.571×1.183 60\pm2.571\times1.183

得到约 [56.96, 63.04] 秒。

这段区间表达的是总体均值的不确定性,不是“未来 95% 的打印任务都在这个范围内”。若想预测一次新任务,还必须把单次任务本身的波动考虑进去,通常会得到更宽的区间。

也不能只因公式能算,就认为条件已经满足。六条数据太少,难以仅靠它们检验分布形状;如果有严重偏斜、极端值或相关性,这个小样本 t 区间的覆盖性质可能不再成立。

95% 到底在说什么

沿用刚才的正态总体模拟,每次抽六条,按相同方法构造区间。把前 30 个画出来:

图 2:每条横线都来自一批新样本。区间在变,作为模拟真值的 60 秒没有变化。

95% 说的是这套构造区间的方法,在相应假设下长期重复使用时的覆盖比例。 大约 95% 的区间会包含固定的真均值,不是说每一批 100 个区间都必须刚好有 95 个覆盖。

在这种频率学解释中,已经算出的区间要么包含真均值,要么不包含;我们不知道是哪一种。不能直接把它解释为“固定真均值有 95% 的概率落在这条已算出的区间内”。若想对未知参数本身建立概率分布,需要另一套明确的建模方式。

名字有些绕,但图能帮我们抓住要点:用重复抽样的方法,衡量一段区间有多经得起反复使用。

5. 两个方法差一点,怎样比较更公平

假设方法 A、B 在同样六个任务上的绝对预测误差如下:

任务A 的误差(秒)B 的误差(秒)B−A(秒)
143−1-1
254−1-1
332−1-1
465−1-1
5451
653−2-2

先逐任务比较,再对差值取平均:

dˉ=−1−1−1−1+1−26≈−0.833 \bar d=\frac{-1-1-1-1+1-2}{6}\approx-0.833

负号表示 B 的平均误差更小。这样成对比较,能利用“两个方法面对的是同一道题”的关系,避免把任务难易不同混进方法差异。

如果各任务独立抽取,且差值可用正态模型描述,就可以把刚才的均值区间应用到差值上。本例得到约 [−1.865, 0.198] 秒。

区间跨过了 0,说明仅凭这六个构造任务和这些假设,还不能把平均改善的方向判断得很稳。它不证明“两个方法完全一样”,更不说明任何改善都不存在;信息也可能只是太少。

反过来,即使区间很窄、明确落在负值一侧,也还要问改善是否值得。平均少 0.001 秒,却要多耗十倍计算资源,未必符合应用目标。统计证据与实际价值,需要一起考虑。

6. 为什么同一段训练代码会跑出不同结果

前面的随机性来自抽样。机器学习实验还可能受到多种因素影响:

  • 这次分到训练集的样本不同。
  • 参数的随机初值不同。
  • 训练中抽到的小批次或样本顺序不同。
  • 某些计算实现没有保证完全确定的执行结果。

把种子固定,能帮助定位问题,却不能消除数据本身带来的不确定性。只挑“最好看的那个种子”,也会让报告显得过于乐观。

比较两种方法时,尽量让它们使用相同的数据划分和对应的种子,记录多次结果及波动,再说明这些重复具体改变了什么。固定测试集上的多次训练,只能主要反映训练过程的波动,不能代替重新抽取测试任务带来的不确定性。

同样,把一百条高度相似的任务当作一百个独立证据,也可能低估波动。如果数据按设备、用户或日期成组,评估时就需要考虑这种分组。

第八章会进一步讨论划分和交叉验证。此刻先养成一个习惯:看到“提升了 1%”,同时看看基于多少任务、重复了多少次,以及是否只报告了其中最好的一次。

7. 亲手重复一千次实验

脚本只使用 Python 标准库,Python 3.9+ 即可运行:

python3 src/examples/uncertainty.py

也可以下载脚本,在保存目录运行 python3 uncertainty.py。

"""第 4 章:合成观测的均值区间与重复抽样;只使用标准库。"""

from math import sqrt
from random import Random
from statistics import mean, stdev


OBSERVATIONS = [56, 58, 59, 61, 62, 64]
ERROR_A = [4, 5, 3, 6, 4, 5]
ERROR_B = [3, 4, 2, 5, 5, 3]
T_975_DF5 = 2.570581835636314  # t 分布,自由度 5,97.5% 分位点


def mean_interval_six(values):
    if len(values) != 6:
        raise ValueError("本例的临界值只适用于 6 个独立观测")
    center = mean(values)
    standard_error = stdev(values) / sqrt(6)
    radius = T_975_DF5 * standard_error
    return center, standard_error, center - radius, center + radius


def repeated_samples(size, repeats=1000, seed=2026):
    rng = Random(seed)
    # 人为设定总体为 N(60, 6²),真实项目通常不知道总体参数。
    return [[rng.gauss(60, 6) for _ in range(size)] for _ in range(repeats)]


def main():
    center, se, low, high = mean_interval_six(OBSERVATIONS)
    print(f"样本均值:{center:.2f} 秒,样本标准差:{stdev(OBSERVATIONS):.2f} 秒")
    print(f"估计标准误:{se:.2f} 秒,95% 均值区间:[{low:.2f}, {high:.2f}]")
    difference = [b - a for a, b in zip(ERROR_A, ERROR_B)]
    center, se, low, high = mean_interval_six(difference)
    print(f"B−A 平均误差差:{center:.3f} 秒,区间:[{low:.3f}, {high:.3f}]")

    samples = repeated_samples(6)
    intervals = [mean_interval_six(row) for row in samples]
    coverage = sum(low <= 60 <= high for _, _, low, high in intervals) / len(intervals)
    print(f"1000 次模拟中,均值区间覆盖 60 秒的比例:{coverage:.1%}")
    for size in [6, 60]:
        averages = [mean(row) for row in repeated_samples(size)]
        print(f"每次抽 {size} 条,1000 个样本均值的标准差:{stdev(averages):.3f} 秒")


if __name__ == "__main__":
    main()

输出为:

样本均值:60.00 秒,样本标准差:2.90 秒
估计标准误:1.18 秒,95% 均值区间:[56.96, 63.04]
B−A 平均误差差:-0.833 秒,区间:[-1.865, 0.198]
1000 次模拟中,均值区间覆盖 60 秒的比例:94.3%
每次抽 6 条,1000 个样本均值的标准差:2.514 秒
每次抽 60 条,1000 个样本均值的标准差:0.771 秒

1000 次中的覆盖比例是 94.3%,没有恰好等于 95%,这并不奇怪:覆盖次数本身也会随机波动。换种子或增加模拟次数,可以继续观察。

最后两行对应图 1:每次多抽一些数据,样本均值的波动明显缩小。理论上的标准误分别是 6/66/\sqrt6 和 6/606/\sqrt{60},约为 2.449 和 0.775;有限次模拟得到的标准差会在附近波动。

mean_interval_six 故意只接受六个数,因为代码里写死了自由度为 5 的临界值。把样本量换成 60 时,不能继续偷偷用同一个临界值。正式分析可以使用统计库计算相应分位点;这里保留这个限制,是为了让每一步的条件都看得见。

在相同 Python 环境与种子下,模拟可以重复。即使某些小数因环境变化不同,均值区间的手算结果和统计解释也应一致。

8. 自测与小实验

  1. 10000 条日志都来自同一台设备的空闲时段,能否直接用来说明所有设备在高峰时的表现?
  2. 单次结果的标准差约为 8 秒,若 64 条观测独立同分布,均值标准误约为多少?要让它减半,需要多少条?
  3. 均值的 95% 置信区间为 [57,63],能否说“未来 95% 的任务会落在这里”?
  4. 同样构造 100 次 95% 区间,是否必须恰好有 95 次覆盖真均值?
  5. 为什么比较模型时,应尽量让 A、B 预测同一批任务,再看成对差值?
  6. 固定同一个测试集,改变训练种子跑 20 次,就已经测到了测试集抽样的不确定性吗?
  7. 把 repeated_samples 的种子换掉,区间覆盖率会怎样?把均值从 60 改成 80 时,还应该用 60 作为覆盖检查目标吗?
展开参考答案
  1. 不能直接推广。设备和时段的选择可能造成偏差,样本数量不能修复缺失的场景。
  2. 为 8/64=18/\sqrt{64}=1 秒;要降到 0.5 秒,需要约 256 条,前提仍是独立同分布等条件成立。
  3. 不能。它针对总体均值,不针对单次任务的取值范围。
  4. 不必。长期覆盖率是方法的性质,有限批次中覆盖次数仍会波动。
  5. 相同任务保留了配对关系,减少任务难易不同带来的干扰。之后还应检查这些任务是否代表目标场景。
  6. 没有。它主要展示固定数据条件下训练过程的变化。
  7. 覆盖比例会波动,足够多次重复时通常在设定覆盖率附近。改变模拟总体均值后,也必须同步修改覆盖检查使用的真值,不能拿旧的 60 继续判定。

进阶选做:把模拟中的独立采样改成“每六条共同增加一个随机偏移”。想想这些观测为何不再独立,再观察仍套用原区间公式时,覆盖率发生什么变化。

下一章从概率到学习目标会把这些数学工具接回训练:为什么平方误差、对数和交叉熵会成为常用目标?

有关均值置信区间的条件与公式,可对照 NIST 的均值置信区间说明。阅读时尤其留意它区分的样本条件和适用范围。