5D Library › 概率判断、布莱尔分数校准与群体智慧的统计理论 · English
第二章 严格恰当评分规则:布莱尔分数与墨菲分解
一位气象学家的评分规则
1950 年,美国气象局的格伦·布莱尔在《每月天气评论》上发表了一篇只有三页的短文,提出用一个简单的数来检验概率预报 (Brier 1950)。设一共有 次预报,每次有 种可能的结局,预报者给第 种结局的概率为 ,实际发生的结局记为 ,其余为 0:
这是布莱尔的原始形式,取值在 0 到 2 之间。今天更常用的是二元事件的形式:只看「发生」这一种结局,
取值在 0 到 1 之间,等于原始形式的一半。第一章的三选一问题用的是原始形式,本章其余部分用二元形式。这个分数有一个后来被系统研究的好性质:预报者不能靠报出自己并不相信的概率来改善期望分数 (Gneiting and Raftery 2007)。本章后面一节证明这一点。
墨菲分解:校准、区分度与不确定性
1973 年,气象学家阿伦·墨菲把布莱尔分数拆成三部分 (Murphy 1973)。把预报按数值分成 组(例如所有说 30% 的预报为一组),第 组有 次预报,平均预报为 ,其中事件实际发生的比例为 ;全部预报中事件发生的比例为 。则
- 可靠性(REL,又称校准误差):说 30% 的事,是否大约三成发生。越小越好,完美校准时为 0。
- 区分度(RES,又称分辨率):不同组的实际发生率离总体基础率有多远,也就是预报者能否把「较可能」与「较不可能」的事分开。越大越好。
- 不确定性(UNC):只取决于事件本身的基础率,与预报者无关。
推导
推导。 在第 组内,若每个预报都等于 ,则 ,后一项等于 ;再利用 ,即得上式。若组内的预报并不相同(例如把连续的预报分箱),还会多出两项:组内预报的方差 WBV 与组内预报和结果的协方差 WBC, (Stephenson, Coelho, and Jolliffe 2008)。
下面的程序生成 5,000 次合成的预报,先让预报者只说 0%、10%……100%,再让他说连续的数字、事后分成 11 组,分别验证这两个恒等式。
推导
# Brier score and Murphy's decomposition BS = REL - RES + UNC, computed by binning;
# exact when all forecasts in a bin are equal. With binned continuous forecasts two
# within-bin terms appear (Stephenson et al. 2008): BS = REL - RES + UNC + WBV - 2 WBC.
import random
rng = random.Random(1950)
def murphy(f, o, nbins=11):
n = len(f)
obar = sum(o) / n
bins = {}
for fi, oi in zip(f, o):
k = min(int(fi * (nbins - 1) + 0.5), nbins - 1) # nearest of 0, 0.1, ..., 1
bins.setdefault(k, []).append((fi, oi))
rel = res = wbv = wbc = 0.0
for pts in bins.values():
nk = len(pts)
fk = sum(p[0] for p in pts) / nk
ok = sum(p[1] for p in pts) / nk
rel += nk * (fk - ok) ** 2
res += nk * (ok - obar) ** 2
wbv += sum((p[0] - fk) ** 2 for p in pts)
wbc += sum((p[0] - fk) * (p[1] - ok) for p in pts)
bs = sum((fi - oi) ** 2 for fi, oi in zip(f, o)) / n
return bs, rel / n, res / n, obar * (1 - obar), wbv / n, wbc / n
def forecaster(n, rounded):
f, o = [], []
for _ in range(n):
p = rng.betavariate(2, 3) # the true chance of this event
q = min(max(p + rng.gauss(0, 0.08), 0.0), 1.0) # the forecaster's noisy judgement
if rounded:
q = round(q * 10) / 10 # says only 0 %, 10 %, ..., 100 %
f.append(q)
o.append(1 if rng.random() < p else 0)
return f, o
if __name__ == "__main__":
for rounded in (True, False):
f, o = forecaster(5000, rounded)
bs, rel, res, unc, wbv, wbc = murphy(f, o)
print("forecasts in steps of 10 %" if rounded else "continuous forecasts, 11 bins")
print(f" BS = {bs:.5f} REL = {rel:.5f} RES = {res:.5f} UNC = {unc:.5f}")
tot = rel - res + unc
print(f" REL - RES + UNC = {tot:.5f} gap = {bs - tot:+.2e}")
if not rounded:
print(f" WBV = {wbv:.5f} WBC = {wbc:.5f} REL - RES + UNC + WBV - 2 WBC = "
f"{rel - res + unc + wbv - 2 * wbc:.5f}")
# forecasts in steps of 10 %
# BS = 0.20955 REL = 0.00146 RES = 0.03396 UNC = 0.24204
# REL - RES + UNC = 0.20955 gap = +2.78e-17
# continuous forecasts, 11 bins
# BS = 0.20418 REL = 0.00089 RES = 0.03614 UNC = 0.23988
# REL - RES + UNC = 0.20464 gap = -4.51e-04
# WBV = 0.00079 WBC = 0.00062 REL - RES + UNC + WBV - 2 WBC = 0.20418当预报只取 11 个离散值时, 与布莱尔分数相差约 ,也就是只有计算机的舍入误差:分解是精确的恒等式。当预报是连续数字、事后分箱时,三项之和比布莱尔分数大约 0.00045;把组内方差与协方差两项加回去,就又完全相等。实际应用中常用分箱后的三项分解,这时应当知道,它是一个近似。
谁是「校准完美」的人
一个常见的说法是:「总是报 50% 的人校准完美,因为他从不过度自信。」这只在事件的基础率恰好是 50% 时成立。
推导
20,000 个事件,基础率约 30%。比较五位预报者:总是报 50%;总是报基础率 30%;说出每个事件真实概率的「校准者」;把同样的判断向两端推的「过度自信者」(对数几率乘 2);以及向中间拉的「信心不足者」(对数几率乘 0.5,并以基础率为中心)。最后一部分计算:一个完全校准的人如果只做了 20 次预报,用同样的分组方法算出的 REL 平均是多少。
推导
# Five forecasters, 20,000 events with base rate 30 %: always 50 %, always 30 %,
# calibrated (states the true chance), overconfident (2 x logit), underconfident (0.5 x
# logit about 30 %). Last part: REL from only 20 forecasts of a perfectly calibrated
# person.
import math, random
rng = random.Random(1973)
def logit(p):
return math.log(p / (1 - p))
def inv(x):
return 1 / (1 + math.exp(-x))
def decompose(f, o, nb=11):
n, obar = len(f), sum(o) / len(o)
bins = {}
for fi, oi in zip(f, o):
bins.setdefault(round(fi * (nb - 1)), []).append((fi, oi))
rel = res = 0.0
table = []
for k in sorted(bins):
pts = bins[k]
fk = sum(p[0] for p in pts) / len(pts)
ok = sum(p[1] for p in pts) / len(pts)
rel += len(pts) * (fk - ok) ** 2
res += len(pts) * (ok - obar) ** 2
table.append((fk, ok, len(pts)))
bs = sum((a - b) ** 2 for a, b in zip(f, o)) / n
return bs, rel / n, res / n, obar * (1 - obar), table
def cases(n=20000):
ps = [min(max(rng.betavariate(1.2, 2.8), 0.005), 0.995) for _ in range(n)]
o = [1 if rng.random() < p else 0 for p in ps]
fc = {"always 50 %": [0.5] * n,
"always base rate": [0.3] * n,
"calibrated": ps,
"overconfident": [inv(2 * logit(p)) for p in ps],
"underconfident": [inv(0.5 * logit(p) + 0.5 * logit(0.3)) for p in ps]}
return {k: decompose(v, o) for k, v in fc.items()}
def small_sample_rel(m=20, reps=4000):
vals = []
for _ in range(reps):
ps = [rng.betavariate(1.2, 2.8) for _ in range(m)]
f = [round(p * 10) / 10 for p in ps] # states its chance, rounded
o = [1 if rng.random() < q else 0 for q in f] # events happen at that rate
vals.append(decompose(f, o)[1])
vals.sort()
return sum(vals) / reps, vals[reps // 2]
if __name__ == "__main__":
res = cases()
print("forecaster | BS REL RES UNC")
for k, (bs, rel, rs, unc, _) in res.items():
print(f"{k:17s} | {bs:.4f} {rel:.4f} {rs:.4f} {unc:.4f}")
print("overconfident: mean forecast -> observed frequency (bins with n >= 200):")
cells = [f"{f:.2f}->{ob:.2f}" for f, ob, n in res["overconfident"][4] if n >= 200]
print(" " + " ".join(cells[:6]))
print(" " + " ".join(cells[6:]))
mean, med = small_sample_rel()
print(f"perfectly calibrated, only 20 forecasts: REL mean {mean:.4f},"
f" median {med:.4f} (truth 0)")
# forecaster | BS REL RES UNC
# always 50 % | 0.2500 0.0403 0.0000 0.2097
# always base rate | 0.2097 0.0000 0.0000 0.2097
# calibrated | 0.1677 0.0001 0.0413 0.2097
# overconfident | 0.1797 0.0115 0.0407 0.2097
# underconfident | 0.1757 0.0075 0.0394 0.2097
# overconfident: mean forecast -> observed frequency (bins with n >= 200):
# 0.02->0.09 0.09->0.25 0.20->0.33 0.30->0.37 0.40->0.44 0.50->0.49
# 0.60->0.55 0.70->0.59 0.80->0.67 0.90->0.74 0.97->0.88
# perfectly calibrated, only 20 forecasts: REL mean 0.0631, median 0.0583 (truth 0)总是报 50% 的人,REL 为 0.0403,约等于 :他系统地高估了这类事件。总是报基础率的人,REL 为 0,校准完美,但 RES 也是 0,他的布莱尔分数正好等于不确定性 0.2097:这是什么也不懂也能达到的分数。说出真实概率的校准者,REL 几乎为 0,RES 为 0.0413,布莱尔分数降到 0.1677。
过度自信者与校准者拥有相同的信息,RES 几乎一样(0.0407),但 REL 升到 0.0115,布莱尔分数因此变差到 0.1797。图一显示了过度自信的典型形状:他说 90% 的事只有约 74% 发生,说 97% 的事约 88% 发生;在另一端,他说 9% 的事其实有约 25% 发生。信心不足者的问题方向相反,REL 为 0.0075。所以,校准与区分度是两种不同的能力:校准好不等于有用,区分度高也不等于说得准。
最后一行提醒一个测量问题:一个完全校准的人,如果只做 20 次预报,按 11 组分箱算出的 REL 平均却是 0.063,中位数 0.058,远离真实值 0。原因是每组只有一两次预报,观察到的发生比例在 0 与 1 之间剧烈跳动。所以,用很少的预报给个人算墨菲分解,会系统地高估校准误差。第八章的试验因此没有用个人的 REL 作主要结局。
恰当评分规则
一条评分规则如果能让预报者只有说出自己真实相信的概率,期望分数才最好,就称为严格恰当的。这一点很重要:一个奖励「说得更极端」的规则,会把诚实的预报者训练成过度自信的人。
推导
推导。 设你相信事件发生的概率是 ,报出 。布莱尔分数的期望(越小越好)为 ,求导得 ,只在 时为零,且二阶导数为正:诚实是唯一的最优报告。对数分数 (事件发生)或 (未发生)的期望为 ,导数为 ,同样只在 时为零 (Good 1952; Gneiting and Raftery 2007)。看起来同样自然的绝对误差 ,期望为 ,是 的线性函数:只要 ,报得越接近 1 越好。
推导
# Expected penalty for reporting q when you believe p = 0.70 (lower is better):
# quadratic (Brier), logarithmic, absolute. Strictly proper = honest q = p is the unique
# best.
import math
P = 0.70
def e_quad(q, p=P):
return p * (1 - q) ** 2 + (1 - p) * q ** 2
def e_log(q, p=P):
return -p * math.log(q) - (1 - p) * math.log(1 - q)
def e_abs(q, p=P):
return p * (1 - q) + (1 - p) * q
if __name__ == "__main__":
grid = [i / 100 for i in range(1, 100)]
print(" report q | quadratic | logarithmic | absolute")
for q in (0.50, 0.60, 0.70, 0.80, 0.90, 0.99):
print(f" {q:.2f} | {e_quad(q):.4f} | {e_log(q):.4f} | {e_abs(q):.4f}")
for name, fn in (("quadratic", e_quad), ("logarithmic", e_log), ("absolute", e_abs)):
best = min(grid, key=fn)
print(f"{name:11s}: best report on the grid 0.01..0.99 is q = {best:.2f}")
print(f"log score if you said 0.99 and it did not happen: {-math.log(0.01):.2f}"
f" (vs {-math.log(0.30):.2f} after saying 0.70)")
# report q | quadratic | logarithmic | absolute
# 0.50 | 0.2500 | 0.6931 | 0.5000
# 0.60 | 0.2200 | 0.6325 | 0.4600
# 0.70 | 0.2100 | 0.6109 | 0.4200
# 0.80 | 0.2200 | 0.6390 | 0.3800
# 0.90 | 0.2500 | 0.7645 | 0.3400
# 0.99 | 0.2941 | 1.3886 | 0.3040
# quadratic : best report on the grid 0.01..0.99 is q = 0.70
# logarithmic: best report on the grid 0.01..0.99 is q = 0.70
# absolute : best report on the grid 0.01..0.99 is q = 0.99
# log score if you said 0.99 and it did not happen: 4.61 (vs 1.20 after saying 0.70)当你相信 70% 时,布莱尔分数与对数分数都在报 0.70 时最好;绝对误差却在报 0.99 时最好,它不是恰当的评分规则。两种恰当规则对「说得太满」的惩罚程度不同:说 99% 而事情没有发生,对数分数的惩罚是 4.61,而如果当初说 70%,惩罚只有 1.20。对数分数对极端的错误极其严厉(说 100% 而错了,惩罚为无穷大),布莱尔分数则有上限。预测锦标赛大多用布莱尔分数,部分原因就是它不会因一次失误让一个人「永远翻不了身」。
过度自信有几种
证据
校准研究的基本发现。 早期的校准研究发现,在一般知识题上,人们说「我有 90% 把握」时,正确率往往明显低于 90%;在很难的题目上过度自信,在很容易的题目上反而可能信心不足(「难易效应」)(Lichtenstein, Fischhoff, and Phillips 1982)。阿尔珀特与赖法让学生为未知数量给出 98% 的置信区间,真实值落在区间之外的比例远高于名义上的 2% (Alpert and Raiffa 1982)。摩尔与希利区分了三种常被混为一谈的「过度自信」:高估自己的表现、认为自己比别人好,以及对自己的估计过于精确(区间太窄),并指出第三种比前两种更为持久 (Moore and Healy 2008)。在哈兰、摩尔与莫尔韦奇的三个实验中,人们给出的 90% 置信区间只在 29% 到 74% 的情况下包含真实值 (Haran, Moore, and Morewedge 2010)。
本章小结
布莱尔分数是预报与结果之差的平方的平均,墨菲把它分解为可靠性(校准)、区分度与不确定性三项;当组内预报相同时分解是精确的,分箱后则多出组内方差与协方差两项。总是报 50% 的人只在基础率为 50% 时校准完美;一般而言,总是报基础率的人校准完美而区分度为零。过度自信者可以拥有和校准者同样的区分度,却因为说得太满而得分更差。布莱尔分数与对数分数是严格恰当的,绝对误差不是。用很少的预报给个人算校准误差,会系统地偏高。下一章讨论概率判断中最著名的一种错误:忽略基础率。
参考文献
Fan, Y. (2026). Probabilistic Judgement, Brier-Score Calibration and the Statistics of Collective Intelligence: From Base-Rate Neglect and Signal Detection Theory to Superforecasting, Read Beside Classical Chinese Strategic Calculation [概率判断、布莱尔分数校准与群体智慧的统计理论] (Version 1.0.0). 5D Library. https://doi.org/10.5281/zenodo.23764998 · Licensed under Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0).