去年在做 Cambricon-Q:量化训练架构 的工作时,我发现许多同学不会做误差估计,也没有意识到随机实验中需要进行误差估计。体系结构领域似乎并不重视这一点,同行做实验时常常在真实系统上测试跑一个几十毫秒的程序,然后直接用time命令截取用时,再汇报模拟实验结果的加速比。这样的测量是有误差的,结果常常在一个范围内波动;如果加速比达到上百倍,一点误差可能会导致最终报告出完全不同的数值。 小初高通识教育中,实验课会教我们“多次测量取平均值”,但“多次”应该是多少次?5次够吗?10次呢?100次呢?还是直到工作截稿之前,一直重复做下去呢?我们需要了解科学的实验方法:重复随机实验中的误差估计。我发现并非所有本科概率论课程都教授这些,但要从事科学研究工作,这却是一项必备技能。

我不是统计学家,本科时我也是全校挂科王,肯定算不上概率论专家。但我曾经作为业余爱好钻研过蒙特卡洛模拟,我来展示我自己是如何完成这个过程的。如果有误,还请概率论专家们不吝纠正。

高斯估计

我们有一组实验测量值,现在假设它们独立同分布,服从于高斯分布

虽然分布参数中有,这是我们想得到的,但不会直接体现在测量值中。为了逼近,我们可以取测量值的算术平均值。我们首先先求和,得到 ,然后除以。这时算术平均值 。注意一个高斯分布随机变量除以后,方差会相应缩减倍。

虽然以上推演过程首先假设了高斯分布,但是根据列维-林德伯格中心极限定理(Levy-Lindeberg CLT),任意无限个随机变量叠加后,其和都趋近于服从高斯分布,只要这些随机变量满足以下两个条件:

  • 方差是有限的;
  • 独立。

这样,无论实验测量值实际服从的分布是什么,我们都可以直接从CLT导出最终的分布,结果是一样的。 只要实验次数足够多,就趋近于

高斯分布告诉我们,我们有99.7%的把握说,与我们想要获取的真实值之间的差距不超过三倍的标准差,即 。我们说,的99.7%置信度的置信区间是。 随着实验次数增多,的标准差还会持续缩小。原来我们只知道多次测量取平均值会奏效,现在我们还知道收敛速度了:每增加4倍样本,误差将缩小2倍。

我们用到的常数3和99.7%是查表得出的高斯分布累积分布函数(CDF)上的一个点,类似常用值还有1.96和95%。从确定的置信区间推算置信度,我们使用CDF: ,但CDF只留了右边尾分布不计,但我们估计的区间是双边的,因此置信度还要扣除左边尾分布,即 。如果要用目标置信度来推算置信区间,则需要使用CDF的逆函数ICDF,也叫PPF。

编程实现时,高斯分布的CDF可以比较直接地用数学库函数erf实现。它的逆函数erfinv并不常见,在C语言和C++的标准库里就不存在,不过因为CDF都是连续、单调、可导的,其导函数即概率密度函数(PDF),因此用牛顿法、割线法、二分法等数值方法都可以比较高效地求逆。

然而,以上估计完全依赖于CLT,CLT毕竟只描述了一个极限情况。实际实验时我们取5次、10次实验结果,离无限次实验差得远,难道也强行应用CLT吗?这显然不科学。

“学生” t-分布估计

描述有限多次随机实验叠加值,有一个更专用的概率分布,即“学生” t-分布。除了以上高斯估计推算的过程,t-分布还额外考虑了和真实误差之间的差异。

t-分布与自由度直接相关,在重复随机实验里自由度可以理解为 。自由度低时,t-分布的两端更长,可以导出一个更保守的误差估计;当自由度趋近于无穷时,t-分布也趋近于高斯分布。例如5次重复实验,自由度为4,如果用高斯估计99.7%置信度的误差结果为3倍标准差,用“学生” t-分布则估计为6.43倍,显著地扩大了。

当试验次数不多时,我们直接用t-分布代替高斯分布来描述。特别是在依靠误差估计来判定重复实验的提前结束时,使用高斯估计更容易发生过早的结束(特别是在前两、三个样本凑巧相差不大时)。

实现

在Python中,直接调包:高斯估计使用scipy.stats.norm.cdfscipy.stats.norm.ppf;“学生” t-分布估计使用scipy.stats.t.cdfscipy.stats.t.ppf

C++里比较难做,STL库功能不全,而且引入外部库不方便。下面的代码给出了两种误差估计的C++实现。两条注释条之间的内容可以抽出组成一个头文件供直接使用,更下面的部分则展示了一个样例程序:从分布的随机变量中重复实验,直到误差估计认为有95%的把握将真实值囊括在估计值的正负10%的范围内。

代码