统计与实验中的不确定性
你给打印机换了一种调度方法,平均耗时从 60 秒降到 59 秒。看起来快了一点,可是换个时间再测,旧方法也可能跑到 58 秒。
这一秒到底是改进,还是恰巧遇到了几次比较顺利的任务?
上一章从已知分布出发,计算可能的结果。这一章反过来:我们手上只有有限的观测,希望借它们了解更大的世界。统计方法能帮助我们表达这种推断中的不确定性,也提醒我们别让一个漂亮的小数承担太多结论。
1. 手上的记录,不是整个世界
假设我们关心“同一台设备、相同设置下,20 页文档的打印耗时”。如果可以在目标条件下不断重复任务,所有可能产生的耗时共同构成我们要研究的总体。
实际只做了六次,记录为:
这六个数是一个样本。为了方便学习,它们是构造的教学数据,不是真实实验。
总体的平均耗时记为 ,我们通常不知道它;样本的平均耗时则能直接算出:
读作“ 平均”。它是用样本估计总体均值的一种办法。能够算到 60,不等于已经证明总体均值恰好是 60。
先问抽到了什么,再问算得准不准
如果只在机器空闲时记录,却想预测高峰时段的体验,问题首先出在样本代表性。再精细的公式,也不会自动把没有见过的高峰任务补进来。
同样,10000 条记录也可能只来自一台特殊设备,或者包含大量重复副本。数量大,不等于覆盖面广;少报、漏报和选择性收集,都会影响结论。
统计推断始终需要一句背景说明:我们想把结果推广到哪些任务,而这些记录又是怎样来的?
2. 平均值也会随着抽样摇摆
假设确实存在一个总体,均值为 60 秒、标准差为 6 秒,并且耗时服从正态分布。这个设定只用于模拟,让我们知道“真答案”在哪里。
现在每次抽六条记录,算一个平均值;重复 1000 次,就得到 1000 个不同的样本均值。再把每次的样本量改成 60:
图 1:真均值没有变化,变化的是每次抽到的样本。样本量增加后,平均值通常更稳定。
样本均值本身也有一个分布,叫它的抽样分布。这句话不需要神秘的想象:重复抽样,每次算一次平均,再把这些平均值画出来,就是图中的分布。
在独立同分布且方差有限的条件下,若单次结果的总体标准差为 ,样本均值的标准差为:
它叫均值的标准误。 是每次抽到的样本数量。
这意味着,要让标准误大致减半,样本量需要变成四倍,而不是两倍。也意味着:如果记录彼此相关,不能不加检查地套用这个 的缩小规律。
3. 标准差与标准误,名字相像,任务不同
真实项目通常不知道 ,于是先从样本估计单次结果的波动。
样本方差常用:
分母是 。我们先用同一批数据估计了均值,围绕这个样本均值计算的平方偏差会有偏小倾向;在独立同分布条件下,用 可以使这里的样本方差成为总体方差的无偏估计。
先记住用途即可,不必立刻证明。它与上一章已知整个分布时计算方差的方式不同,也不是说平方根 本身就一定是标准差的无偏估计。
对于六条教学记录,离均值的偏差为 ,平方和为 42,所以:
据此估计样本均值的标准误:
两个数都以秒为单位,却回答不同问题:
| 量 | 本例数值 | 描述什么 |
|---|---|---|
| 样本标准差 | 约 2.90 秒 | 单次耗时的波动 |
| 估计标准误 | 约 1.18 秒 | 用这批样本估计均值的不确定性 |
增加样本量,通常能让均值估得更稳,却不会自动让打印机每次运行都更稳定。即使我们把平均耗时估得非常准确,某一次任务仍然可能明显偏离它。
4. 与其只给一个点,不如给一段区间
如果这些数据可以看作独立的正态样本,而总体方差未知,可以用 t 区间估计总体均值:
这表示“以样本均值为中心,向两侧各延伸一个半径”。
来自 t 分布,是用于构造双侧 95% 区间的临界值;0.975 对应左侧累计概率 97.5%,另外 2.5% 留在右尾,左尾也留 2.5%。 称为自由度。
本例 ,临界值约为 2.571。于是:
得到约 [56.96, 63.04] 秒。
这段区间表达的是总体均值的不确定性,不是“未来 95% 的打印任务都在这个范围内”。若想预测一次新任务,还必须把单次任务本身的波动考虑进去,通常会得到更宽的区间。
也不能只因公式能算,就认为条件已经满足。六条数据太少,难以仅靠它们检验分布形状;如果有严重偏斜、极端值或相关性,这个小样本 t 区间的覆盖性质可能不再成立。
95% 到底在说什么
沿用刚才的正态总体模拟,每次抽六条,按相同方法构造区间。把前 30 个画出来:
图 2:每条横线都来自一批新样本。区间在变,作为模拟真值的 60 秒没有变化。
95% 说的是这套构造区间的方法,在相应假设下长期重复使用时的覆盖比例。 大约 95% 的区间会包含固定的真均值,不是说每一批 100 个区间都必须刚好有 95 个覆盖。
在这种频率学解释中,已经算出的区间要么包含真均值,要么不包含;我们不知道是哪一种。不能直接把它解释为“固定真均值有 95% 的概率落在这条已算出的区间内”。若想对未知参数本身建立概率分布,需要另一套明确的建模方式。
名字有些绕,但图能帮我们抓住要点:用重复抽样的方法,衡量一段区间有多经得起反复使用。
5. 两个方法差一点,怎样比较更公平
假设方法 A、B 在同样六个任务上的绝对预测误差如下:
| 任务 | A 的误差(秒) | B 的误差(秒) | B−A(秒) |
|---|---|---|---|
| 1 | 4 | 3 | |
| 2 | 5 | 4 | |
| 3 | 3 | 2 | |
| 4 | 6 | 5 | |
| 5 | 4 | 5 | 1 |
| 6 | 5 | 3 |
先逐任务比较,再对差值取平均:
负号表示 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:每次多抽一些数据,样本均值的波动明显缩小。理论上的标准误分别是 和 ,约为 2.449 和 0.775;有限次模拟得到的标准差会在附近波动。
mean_interval_six 故意只接受六个数,因为代码里写死了自由度为 5 的临界值。把样本量换成 60 时,不能继续偷偷用同一个临界值。正式分析可以使用统计库计算相应分位点;这里保留这个限制,是为了让每一步的条件都看得见。
在相同 Python 环境与种子下,模拟可以重复。即使某些小数因环境变化不同,均值区间的手算结果和统计解释也应一致。
8. 自测与小实验
- 10000 条日志都来自同一台设备的空闲时段,能否直接用来说明所有设备在高峰时的表现?
- 单次结果的标准差约为 8 秒,若 64 条观测独立同分布,均值标准误约为多少?要让它减半,需要多少条?
- 均值的 95% 置信区间为 [57,63],能否说“未来 95% 的任务会落在这里”?
- 同样构造 100 次 95% 区间,是否必须恰好有 95 次覆盖真均值?
- 为什么比较模型时,应尽量让 A、B 预测同一批任务,再看成对差值?
- 固定同一个测试集,改变训练种子跑 20 次,就已经测到了测试集抽样的不确定性吗?
- 把
repeated_samples的种子换掉,区间覆盖率会怎样?把均值从 60 改成 80 时,还应该用 60 作为覆盖检查目标吗?
展开参考答案
- 不能直接推广。设备和时段的选择可能造成偏差,样本数量不能修复缺失的场景。
- 为 秒;要降到 0.5 秒,需要约 256 条,前提仍是独立同分布等条件成立。
- 不能。它针对总体均值,不针对单次任务的取值范围。
- 不必。长期覆盖率是方法的性质,有限批次中覆盖次数仍会波动。
- 相同任务保留了配对关系,减少任务难易不同带来的干扰。之后还应检查这些任务是否代表目标场景。
- 没有。它主要展示固定数据条件下训练过程的变化。
- 覆盖比例会波动,足够多次重复时通常在设定覆盖率附近。改变模拟总体均值后,也必须同步修改覆盖检查使用的真值,不能拿旧的 60 继续判定。
进阶选做:把模拟中的独立采样改成“每六条共同增加一个随机偏移”。想想这些观测为何不再独立,再观察仍套用原区间公式时,覆盖率发生什么变化。
下一章从概率到学习目标会把这些数学工具接回训练:为什么平方误差、对数和交叉熵会成为常用目标?
有关均值置信区间的条件与公式,可对照 NIST 的均值置信区间说明。阅读时尤其留意它区分的样本条件和适用范围。