跳到主要内容
DA DataAlgorithm
Insights Calibration 只需加减乘除 IN-04

说 70% 的时候,真的会发生 70% 吗

摘要 · 一句话结论 准确率只问“猜对没有”,校准问的是“那个百分数配不配”。 在同一批 2000 场比赛上,三个预测者的准确率是 66.90%、66.85%、66.90%, 几乎分不出高下;但期望校准误差 ECE 分别是 0.10150.02670.0775,最大校准误差 MCE 甚至差到 0.2191。 本文用可靠性图把这个差别画出来,用 Brier 分解证明它藏在哪一项里, 并展示一次分箱重映射如何把 ECE 从 0.0954 压到 0.0207。 全程只用加减乘除。
编号 IN-04 系列 通俗洞察 发布 阅读 13 min 数据 合成样本 N=2000,seed 20260808

0 一个尴尬的提问

有人给你看一份预测记录:2000 场比赛,准确率 66.90%。 看起来不错。但如果你换个问法——

核心提问 “在你说过 70% 的那些场次里,实际发生了多少次?”

这个问题准确率答不了。准确率只统计“把票投给谁”, 它把 0.51 和 0.99 都当成同一个“是”, 把 0.49 和 0.01 都当成同一个“否”。 也就是说,准确率把概率里最值钱的信息——强度——直接扔掉了

这不是学术洁癖。IN-03 已经算过: 要判断一注值不值得下,你需要的是 EV = p × d − 1 里的那个 p,而不是“猜对没有”这个 0/1 结果。 IN-02 又说过,赔率倒数归一化后能给出市场的 p。 两件事拼起来,结论就很硬:如果你的 p 是虚的, EV 算出来的每一个正号都可能是假的

本文要做的,就是给 p 本身做一次体检。三步: 先画可靠性图看形状,再用 ECE / MCE 把形状压成一个数, 最后动手修,并检查修完之后钱包有没有变化。

1 校准是什么:一句人话的定义

校准(calibration)的定义朴素到有点让人失望:

定义 把所有你说过 “70%” 的场次挑出来,数一数实际发生了几成。 如果是七成,你在这个刻度上校准良好;如果只有五成,你吹了; 如果有九成,你过于谦虚。对每个刻度都这么查一遍,就是一次完整的校准检验。

实践中没人会说得刚好是 70.00%,所以要分箱: 把 0 到 1 切成 10 段,每段里的宣称概率算一个平均值, 再和这段里的实际发生频率比。差值就是这一箱的校准误差。

注意这里出现了一个和准确率完全不同的动作: 准确率横着看每一场,校准竖着看每一箱。 单场比赛没有校准可言——事情要么发生要么没发生, 你说 70% 是对是错无从判断。只有把同一刻度的许多场聚在一起, 那个百分数才第一次变得可以被证伪。

2 三个准确率一样的预测者

为了把话说清楚,我构造了一批数据:2000 场比赛, 每场有一个真实发生概率,结果按这个概率抛硬币决定。 然后请三位预测者对同一批比赛报数——

  • 预测者 A(过度自信):把真实概率往两端拉伸,敢说 0.03,也敢说 0.97。
  • 预测者 B(校准良好):报的就是真实概率,只带一点小噪声。
  • 预测者 C(欠自信):把真实概率往 0.5 压,什么都说得含含糊糊。

数据口径 这是构造样本,不是真实赛事记录, 目的是把三种典型偏差干净地隔离出来。随机种子固定为 20260808, §7 给出完整源码,任何人跑一遍都会得到本文的每一个数字。

先看最常被拿出来汇报的那张表:

表 1 三个预测者的整体成绩(N=2000,阈值 0.5)
预测者准确率 Brier 分数ECEMCE
A 过度自信0.6690 0.21350.10150.1377
B 校准良好0.6685 0.20380.02670.0760
C 欠自信0.6690 0.21290.07750.2191

准确率那一列几乎是平的:66.90%66.85%66.90%,最大差距 0.05 个百分点—— 在 2000 场的尺度上,这点差距连噪声都算不上。 如果汇报只到这一列,三个人是同一水平。

但 ECE 那一列,A 是 B 的 3.8 倍。 MCE 更夸张,C 在某个刻度上的偏差达到 0.2191—— 也就是说,C 报的某个百分数系统性地偏离真相 21.91 个百分点。 准确率对此完全没有反应,因为它压根不看这个百分数。

3 可靠性表:把偏差摊开来看

ECE 是一个压缩后的数,压缩就会丢信息。要知道错在哪里, 得把每一箱摊开。这张摊开的表就是可靠性图的数字版本—— 完美校准的预测者,“宣称”和“实际”两列应该处处相等。

表 2 预测者 A(过度自信)的可靠性表,浅红为偏差 ≥ 0.10 的箱
宣称概率区间场次 区间内平均宣称实际发生频率 差值
[0.0, 0.1)2710.02840.1661+0.1377
[0.1, 0.2)1500.14770.2533+0.1056
[0.2, 0.3)1800.24770.3167+0.0690
[0.3, 0.4)1990.35020.4724+0.1221
[0.4, 0.5)2180.45100.4174-0.0336
[0.5, 0.6)2040.55320.4461-0.1071
[0.6, 0.7)1860.65050.5376-0.1129
[0.7, 0.8)1750.74980.6343-0.1156
[0.8, 0.9)1540.84700.7857-0.0613
[0.9, 1.0)2630.97050.8441-0.1264

A 的形状非常有辨识度:低端全是正号,高端全是负号。 最低那一箱他平均报 0.0284,实际发生了 0.1661—— 他说“几乎不可能”的事,每六次就来一次。 最高那一箱他平均报 0.9705,实际只有 0.8441—— 他说“几乎必然”的事,每六到七次就失手一次。

形状读法 低估低端 + 高估高端 = 过度自信。 在可靠性图上,这条曲线会以比对角线更平缓的斜率穿过中心, 两端各自倒向 0 和 1。直觉上就是:这个人的话要往中间打折。

表 3 预测者 B(校准良好)的可靠性表
宣称概率区间场次 区间内平均宣称实际发生频率 差值
[0.0, 0.1)610.05740.0656+0.0082
[0.1, 0.2)1390.16170.1942+0.0326
[0.2, 0.3)2110.25360.2512-0.0024
[0.3, 0.4)2850.34990.3509+0.0010
[0.4, 0.5)3170.45040.4385-0.0119
[0.5, 0.6)3240.55130.4753-0.0760
[0.6, 0.7)2670.64930.6142-0.0351
[0.7, 0.8)2080.74800.7837+0.0356
[0.8, 0.9)1420.84670.8592+0.0124
[0.9, 1.0)460.94100.9565+0.0155

B 的差值列几乎全在 ±0.04 以内,正负交替、没有方向性—— 这才是随机噪声该有的样子。唯一一处 −0.0760 出现在 [0.5, 0.6), 也是全表最大偏差,即它的 MCE。 值得强调的是:B 的准确率和 A 一模一样, 但 B 说的每个百分数都可以直接拿去算钱

4 欠自信:另一个方向的谎

过度自信容易被抓,因为它张扬。欠自信更隐蔽——它听起来谨慎、专业、有分寸, 但它同样是错的,而且错得可能更贵。看 C 的表:

表 4 预测者 C(欠自信)的可靠性表,浅红为偏差 ≥ 0.10 的箱
宣称概率区间场次 区间内平均宣称实际发生频率 差值
[0.2, 0.3)960.27380.0833-0.1905
[0.3, 0.4)3580.35600.2402-0.1158
[0.4, 0.5)5640.45200.4096-0.0425
[0.5, 0.6)5310.54860.5348-0.0137
[0.6, 0.7)3640.64320.7665+0.1233
[0.7, 0.8)870.72340.9425+0.2191

第一件该注意的事:这张表只有 6 行, 而 A 和 B 都有 10 行。C 的预测从来没有低于 0.2,也从来没有高于 0.8—— 有 4 个箱是空的。他把所有判断都塞进了中间那几档。

第二件:差值的符号排列和 A 正好相反。 低端是负号(说 0.2738,实际只有 0.0833),高端是正号(说 0.7234,实际高达 0.9425)。 翻译一下:C 说“可能性不大”的时候,实际上是“基本不会”; C 说“比较可能”的时候,实际上是“几乎肯定”。

形状读法 高估低端 + 低估高端 = 欠自信。 可靠性曲线以比对角线更陡的斜率穿过中心,两端伸不出去。 直觉上:这个人的话要往两边放大。

这里有个反直觉的结果值得停一下。C 的最大校准误差 0.2191 是三人中最高的,比 A 的 0.1377 还高出 8 个百分点; 但 C 的 ECE(0.0775)反而低于 A(0.1015)。 原因是 ECE 按场次加权,而 C 那个 0.2191 的巨大偏差只发生在 87 场里, 权重只有 87/2000 = 4.35%。

这正是必须同时看 ECE 和 MCE 的理由。 ECE 告诉你平均而言这个人偏多少,MCE 告诉你他最糟的时候能偏多少。 如果你只在高置信区间下注,那么 C 对你来说是三人中最危险的—— 你专门去用的那个刻度,恰好是他错得最狠的地方。

5 ECE 与 MCE:两个数怎么来的

把可靠性表压成一个数,只需要加权平均。设一共 B 个箱, 第 i 箱有 ni 场, 平均宣称 ci,实际频率 ai, 总场次 N

(1)
ECE= i=1B niN | aici |

期望校准误差:各箱偏差的绝对值,按该箱样本占比加权求和。

(2)
MCE= maxi | aici |

最大校准误差:所有箱里最坏的那一个偏差,不加权。

公式里没有任何超越加减乘除的运算。C 只有 6 个非空箱,索性整个算完:

手算 96/2000 × 0.1905 + 358/2000 × 0.1158 + 564/2000 × 0.0425 + 531/2000 × 0.0137 + 364/2000 × 0.1233 + 87/2000 × 0.2191 = 0.0775, 与表 1 中 C 的 ECE 0.0775 完全一致。 MCE 则不必加权,直接取差值列绝对值最大的那一格:0.2191, 来自 [0.7, 0.8) 这一箱。

有两个细节会影响结果,报数时必须一起说明。 第一是箱数:箱越多,每箱样本越少,实际频率的抖动越大,ECE 会被噪声推高; 箱越少,箱内不同的宣称概率被平均掉,偏差会被掩盖。本文统一用 10 箱, 这是文献里的常规选择,但它是一个约定,不是定理第二是等宽还是等频:本文用等宽分箱(每箱跨度 0.1), 所以 C 才会出现空箱;若改用等频分箱(每箱样本数相同), C 的空箱会消失,ECE 数值也会变。

可比性提醒 不同箱数、不同分箱方式算出的 ECE 不能直接比较。 看到别人报 ECE 而没说箱数和分箱方式,这个数字的信息量接近于零。

6 Brier 分解:校准误差藏在哪一项

表 1 里还有一列没解释:Brier 分数。它的定义比 ECE 还简单—— 每场把宣称概率和结果(0 或 1)相减、平方,最后取平均:

(3)
BS= 1N t=1N (pt yt) 2

Brier 分数:越小越好,完美预测为 0,全押反面为 1。

Brier 分数好用,因为它同时惩罚校准和分辨能力—— 但也正因如此,单看一个 Brier 分数说不清问题出在哪。 Murphy 在 1973 年给出了一个漂亮的拆法:把 Brier 分成三块。

(4)
BS= iB niN (ci ai)2 可靠性 iB niN (ai y¯)2 分辨度 + y¯ (1y¯) 不确定度

Murphy 分解:可靠性越小越好,分辨度越大越好,不确定度由数据本身决定、与预测者无关。

三块各自的含义,用人话说是这样:

  • 可靠性(reliability):就是校准误差,只不过用平方代替绝对值。 它衡量“你报的数准不准”。越小越好。
  • 分辨度(resolution):你的不同刻度之间,实际发生率能拉开多大差距。 如果你对每一场都报同一个数,分辨度为 0——你的预测毫无信息量。 越大越好。
  • 不确定度(uncertainty):这批数据本身有多难猜,等于基准发生率乘以它的补数。 它不由预测者决定,换谁来预测这个数都一样。

这批数据的基准发生率是 0.4850, 所以不确定度固定为 0.4850 × 0.5150 = 0.2498。 三个人的这一项完全相同,可以直接约掉——差别只在前两项。

表 5 Brier 分数的 Murphy 三项分解(10 等宽箱)
预测者可靠性 ↓ 分辨度 ↑不确定度 三项合计直接算 Brier
A 过度自信 0.01140.0472 0.24980.21400.2135
B 校准良好 0.00130.0465 0.24980.20460.2038
C 欠自信 0.00960.0443 0.24980.21510.2129

这张表把话说透了。分辨度那一列三个人几乎相同: 0.0472、0.0465、0.0443,最大差距只有 0.0029。 这不奇怪——三个人的预测都是同一个真实概率的单调变换, 排序能力没有任何损失。这也解释了为什么他们准确率一样: 准确率只依赖排序和阈值,而排序是三人共有的那部分本事。

真正的差别全在可靠性列:A 是 0.0114, C 是 0.0096,而 B 只有 0.0013—— A 是 B 的 8.4 倍。 Brier 分数的高低(0.2135 vs 0.2038)几乎全部由这一项贡献

关键结论 A、B、C 拥有同样的排序能力,差别只在报数的诚实程度。 准确率只看排序,所以它看三个人一样好; 校准指标只看报数,所以它看出了 3.8 倍的差距。 这两个指标测的根本不是同一件事,谁也替代不了谁。

还有一个能说明问题的细节:三项合计与直接计算的 Brier 有微小差异 (A 是 0.2140 vs 0.2135)。这不是算错了—— Murphy 分解在分箱之后才成立,箱内把不同的宣称概率平均成了一个 ci, 这一步丢掉的信息就是这点残差。箱数越多,残差越小。 我把两列都列出来,正是为了让这个近似公开可见, 而不是悄悄用其中一个冒充另一个。

7 校准误差怎么变成亏损

到这里全是指标。指标不花钱,所以必须回答最后一个问题: 校准差,到底亏在哪里?

IN-03 的规则接过来: 只有当 EV = p × d − 1 超过某个门槛时才下注, 每注固定 100 元。这里的 p 是预测者报的概率, d 是赔率。赔率按 IN-02 的办法构造: 在真实概率上扣掉 5% 抽水,再取倒数。

口径说明 赔率由真实概率加抽水反推,属构造盘口, 不代表任何真实市场报价。这样做的好处是“市场”本身是校准的, 亏损只能来自预测者自己的校准误差,因果关系干净。 抽水固定 5%,唯一变动的是 EV 门槛。

门槛就是“要求多大的账面优势才动手”。 门槛 0% 意味着只要 EV 是正的就下,门槛 8% 意味着要看到 8% 以上的优势才下。 直觉上门槛越高越安全。看真实结果:

表 6 不同 EV 门槛下的下注数与收益率(抽水固定 5%,单位注 100 元)
EV 门槛 A 过度自信 B 校准良好 C 欠自信
注数收益率 注数收益率 注数收益率
0%802-6.79%455-5.71%836-3.20%
2%710-5.03%335-2.67%778-3.25%
5%579-5.00%205-5.13%703-5.48%
8%420-4.46%134+7.70%648-5.24%

先看注数怎么随门槛缩水。B 从 455 注一路砍到 134 注, 只剩下 29%;而 C 从 836 只降到 648, 还留着 78%这是个危险信号。

为什么门槛对 C 几乎无效?因为 C 在低端系统性高报 (说 0.2738,实际只有 0.0833)。高报直接把 EV 算大, 这些虚高的正号轻松越过任何门槛。 门槛是一道对数值大小的筛子, 而系统性偏差恰恰是把数值整体推大——筛子越细,漏过来的越是偏差本身。 真正被门槛筛掉的,只有那些没有偏差、优势本来就薄的诚实判断。

再看收益率。最值得看的是 B 的那一列: -5.71% → -2.67% → -5.13% → +7.70%。 门槛提到 8% 时,B 转正了,而且是全表唯一的正号。 这正是校准良好该有的行为:它报的 p 可信, 所以“只挑优势最大的注”这个策略真的有效—— 哪怕代价是 2000 场里只出手 134 次。

对照 A 和 C,同样的门槛策略完全失灵。 A 从 -6.79% 到 -4.46%,在负区间里横盘, 抬门槛既没帮他也没害他——因为他的偏差在高低两端同时存在, 筛掉一批坏注的同时也筛掉了一批好注。 C 更彻底,-3.20% 到 -5.24%,越筛越差。

最贵的一课 三个人准确率都是 66.9%。8% 门槛下,收益率是 -4.46%+7.70%-5.24%准确率相同,钱包相差 12.95 个百分点。 差额全部由那个“看起来只是小数点后两位”的校准误差贡献。

还有一层更实用的推论。“抬高门槛能不能改善收益” 本身就是一个校准诊断工具——不需要标签,不需要画可靠性图。 如果你的策略在提高选注标准后收益反而变差, 那几乎可以断定:你的 p 有系统性偏差, 你正在用一个虚假的数字去挑“最好的机会”, 结果专挑出了偏差最大的那些。

8 怎么修:把可靠性表反过来用

诊断完了该谈治疗。好消息是:校准问题往往是最容易修的那一类模型缺陷, 因为它不要求你改模型、加特征、重训练。

思路直接来自可靠性表。表 2 告诉我们,A 说 0.9705 的时候实际只发生 0.8441。 那么下次 A 再说 0.97,我们就把它当成 0.84。 这就是分箱重映射(histogram binning): 把可靠性表的“实际频率”那一列,直接当成新的输出。

方法 第一步,在一批带标签的历史数据上分箱,算出每箱的实际发生频率,得到一张查表; 第二步,对新的预测,先看它落在哪个箱,再用查表里的频率替换它的输出。 整个过程只用到加法和除法。

但这里有个绝对不能省的动作:训练和检验必须用不同的数据。 如果在同一批数据上学映射又在同一批上评估, ECE 会被压到接近 0——那不是校准变好了,那是把答案抄了一遍。 所以我把 2000 场对半切开:前 1000 场学映射,后 1000 场检验, 映射表在检验集上是完全没见过的。

表 7 分箱重映射的效果(前 1000 场学映射,后 1000 场检验)
预测者阶段 准确率Brier ECEMCE ECE 降幅
A 过度自信修正前0.66600.21070.09540.1400−78%
修正后0.68400.20190.02070.0483
B 校准良好修正前0.67300.20160.03980.0850−12%
修正后0.68400.20280.03490.0787
C 欠自信修正前0.66600.21250.08390.1928−66%
修正后0.66600.20550.02820.0677

结果值得逐行读。A 的 ECE 从 0.0954 降到 0.0207, 砍掉 78%;MCE 从 0.1400 降到 0.0483。 C 的 ECE 从 0.0839 降到 0.0282,降幅 66%。 而 B 本来就校准良好,修正后 ECE 只从 0.0398 微降到 0.0349—— 对已经诚实的预测者,这道工序几乎不做任何事,也不会把它弄坏。 这正是一个好的后处理该有的性质。

再看准确率列。A 修正后准确率反而从 0.6660 升到 0.6840, C 则一动不动(0.6660 → 0.6660)。 C 不变是必然的:分箱重映射是单调变换,不改变任何两场之间的排序, 而准确率只依赖排序和阈值。A 会变,是因为重映射把某些箱推过了 0.5 这条线—— 换了阈值两侧的归属,不是换了排序。

务实的结论 校准是后处理能解决的问题,排序能力不是。 如果你的模型排序能力尚可但报数虚假, 你离一个可用的模型只差一张查表; 反过来,如果排序能力本身不行, 再精致的校准手段也只是把一堆没有信息量的数字调得好看一点。 先看分辨度,再修可靠性——顺序不能颠倒。

分箱重映射不是唯一选择,只是最容易手算的那个。 实践中更常见的是 Platt scaling(拿一维逻辑回归拟合) 和 isotonic regression(拟合一条单调阶梯)。 前者假设偏差有固定的 S 形,参数只有两个,小样本下更稳; 后者不作形状假设,但需要更多数据才不过拟合。 分箱重映射介于两者之间,代价是箱边界处会有跳变。 三者的共同点是:都只动输出,不动模型。

9 复算脚本

本文所有数字都来自下面这段代码。它只用 Python 标准库, 没有 numpy、没有 sklearn,可以直接复制运行。 随机种子固定为 20260808,因此你在自己机器上应当得到逐位相同的结果。

数据性质 这批 2000 场是构造数据, 不是真实赛事记录。构造它的理由很实际: 只有先知道每场的真实概率,才能明确定义“过度自信”和“欠自信”, 并把三个预测者的排序能力控制成完全相同。 真实数据里做不到这种对照——那里的真实概率永远不可观测。

代码 1 可靠性表、ECE、MCE、Brier 的完整实现
import random

SEED, N, BINS = 20260808, 2000, 10

def clip(x, lo=0.01, hi=0.99):
    return max(lo, min(hi, x))

def make_data():
    """构造 N 场比赛:真实概率 p_true,结果 y,以及三个预测者的宣称概率。
    A 过度自信(把 p 往两端推),B 校准良好(只加小噪声),C 欠自信(把 p 往中间压)。
    """
    rnd = random.Random(SEED)
    rows = []
    for _ in range(N):
        p = rnd.betavariate(2.0, 2.0)          # 真实概率
        y = 1 if rnd.random() < p else 0       # 按真实概率掷出结果
        pA = clip(p + (p - 0.5) * 0.60)        # 远离 0.5 -> 过度自信
        pB = clip(p + rnd.gauss(0, 0.03))      # 轻微噪声 -> 校准良好
        pC = clip(0.5 + (p - 0.5) * 0.55)      # 靠近 0.5 -> 欠自信
        rows.append((p, y, pA, pB, pC))
    return rows

def reliability(ps, ys, bins=BINS):
    """等宽分箱,返回每箱 (下界, 上界, 场次, 平均宣称, 实际频率, 差值)。"""
    acc = [[0, 0.0, 0] for _ in range(bins)]   # [场次, 宣称之和, 命中数]
    for p, y in zip(ps, ys):
        k = min(int(p * bins), bins - 1)
        acc[k][0] += 1
        acc[k][1] += p
        acc[k][2] += y
    out = []
    for k, (n, sp, sy) in enumerate(acc):
        if n:
            c, a = sp / n, sy / n
            out.append((k / bins, (k + 1) / bins, n, c, a, a - c))
        else:
            out.append((k / bins, (k + 1) / bins, 0, 0.0, 0.0, 0.0))
    return out

def ece_mce(table, total):
    """ECE = 按场次加权的平均绝对偏差;MCE = 最大绝对偏差。"""
    ece = sum(n / total * abs(d) for _, _, n, _, _, d in table if n)
    mce = max((abs(d) for _, _, n, _, _, d in table if n), default=0.0)
    return ece, mce

def brier(ps, ys):
    return sum((p - y) ** 2 for p, y in zip(ps, ys)) / len(ys)

def accuracy(ps, ys, thr=0.5):
    return sum(1 for p, y in zip(ps, ys) if (1 if p > thr else 0) == y) / len(ys)

DATA = make_data()
ys = [r[1] for r in DATA]

print("N = %d   base rate = %.4f" % (len(ys), sum(ys) / len(ys)))
print()
print("%-14s %8s %8s %8s %8s" % ("predictor", "acc", "brier", "ECE", "MCE"))
for name, i in (("A overconf", 2), ("B calibrated", 3), ("C underconf", 4)):
    ps = [r[i] for r in DATA]
    t = reliability(ps, ys)
    e, m = ece_mce(t, len(ys))
    print("%-14s %8.4f %8.4f %8.4f %8.4f"
          % (name, accuracy(ps, ys), brier(ps, ys), e, m))
print()
print("reliability table of A (10 equal-width bins)")
print("%-12s %6s %9s %9s %9s" % ("bin", "n", "claimed", "actual", "gap"))
for lo, hi, n, c, a, d in reliability([r[2] for r in DATA], ys):
    if n:
        print("[%.1f, %.1f) %6d %9.4f %9.4f %+9.4f" % (lo, hi, n, c, a, d))

运行它,得到下面这段输出。这是脚本的真实 stdout,未经任何编辑

代码 2 代码 1 的真实运行输出
N = 2000   base rate = 0.4850

predictor           acc    brier      ECE      MCE
A overconf       0.6690   0.2135   0.1015   0.1377
B calibrated     0.6685   0.2038   0.0267   0.0760
C underconf      0.6690   0.2129   0.0775   0.2191

reliability table of A (10 equal-width bins)
bin               n   claimed    actual       gap
[0.0, 0.1)    271    0.0284    0.1661   +0.1377
[0.1, 0.2)    150    0.1477    0.2533   +0.1056
[0.2, 0.3)    180    0.2477    0.3167   +0.0690
[0.3, 0.4)    199    0.3502    0.4724   +0.1221
[0.4, 0.5)    218    0.4510    0.4174   -0.0336
[0.5, 0.6)    204    0.5532    0.4461   -0.1071
[0.6, 0.7)    186    0.6505    0.5376   -0.1129
[0.7, 0.8)    175    0.7498    0.6343   -0.1156
[0.8, 0.9)    154    0.8470    0.7857   -0.0613
[0.9, 1.0)    263    0.9705    0.8441   -0.1264

对照一下就能确认:三人准确率 0.6690 / 0.6685 / 0.6690, ECE 0.1015 / 0.0267 / 0.0775——与表 1 完全一致。 A 的可靠性表最后一行 [0.9, 1.0) 263 0.9705 0.8441 -0.1264, 也正是本文反复引用的那个“说 0.97 只发生 0.84”。

§6 的 Murphy 分解、§7 的下注回测和 §8 的分箱重映射需要额外几十行, 为了让代码 1 保持可读,没有全部贴进来。 完整脚本(含这三部分)就是本站生成本文所用的 _verify_calib.py,实现方式与上面完全同构。

10 写在最后

如果这篇文章只能留下一句话,那就是: 准确率检验你的排序,校准检验你的诚实。

三个预测者在同一批 2000 场上给出了几乎相同的准确率 (66.90% / 66.85% / 66.90%,最大差 0.05 个百分点), Brier 分解显示他们的分辨度几乎完全相同(相差 0.0029)—— 因为他们的预测本来就是同一个真实概率的单调变换,排序能力一模一样。 但可靠性一项差了 8.4 倍, ECE 差了 3.8 倍, 在 8% 门槛的同一套下注规则下收益率相差 12.95 个百分点

这就是为什么“我的模型准确率 67%”这句话, 信息量远比说的人以为的要少。它没有告诉你: 这个模型说 90% 的时候是不是真的会发生 90%; 它有没有把“差不多”的场次说成“十拿九稳”; 以及最要紧的——当你按它的数字去做决策时, 你承担的风险是不是它宣称的那个数。

该报什么 报概率的模型,至少要同时给出 ECE、MCE 和分箱方式, 最好附一张可靠性表。只报准确率或只报 AUC, 等于只证明了排序能力,对报数的可信度一个字都没说。 而在按概率下注的场景里,报数的可信度就是全部

好在结论并不悲观。§8 表明校准是后处理能修的—— 一张查表、几十行代码,A 的 ECE 就从 0.0954 降到 0.0207。 真正修不了的是分辨度: 如果模型根本分不清哪场更可能发生, 再漂亮的校准也只是把一串没有信息量的数字排整齐。 所以顺序永远是:先确认有分辨度,再去修可靠性。

最后留一个可以自己动手的检查。拿出你自己的模型, 把历史预测按 0.1 分箱,数一数每箱里实际发生了多少次, 和你报的数比一比。这件事不需要任何库, 一张纸和一支笔就够—— 而它往往比你调过的所有超参数都更能说明问题出在哪。

参考文献

  1. Murphy, A. H. A New Vector Partition of the Probability Score. Journal of Applied Meteorology, 1973, 12(4), 595–600. (Brier 分数的可靠性–分辨度–不确定度三项分解,本文 §6 与表 5 的依据)
  2. Brier, G. W. Verification of Forecasts Expressed in Terms of Probability. Monthly Weather Review, 1950, 78(1), 1–3. (Brier 分数原始定义,本文式 (3))
  3. Guo, C.; Pleiss, G.; Sun, Y.; Weinberger, K. Q. On Calibration of Modern Neural Networks. ICML, 2017, 1321–1330. (ECE 的分箱定义与现代模型普遍过度自信的实证,本文 §3、§5 的方法来源)
  4. Naeini, M. P.; Cooper, G. F.; Hauskrecht, M. Obtaining Well Calibrated Probabilities Using Bayesian Binning. AAAI, 2015, 2901–2907. (MCE 定义与分箱校准方法,对应本文 §5 与 §8)
  5. Zadrozny, B.; Elkan, C. Transforming Classifier Scores into Accurate Multiclass Probability Estimates. KDD, 2002, 694–699. (分箱重映射与 isotonic regression 校准,本文 §8 表 7 的方法出处)
  6. Platt, J. C. Probabilistic Outputs for Support Vector Machines and Comparisons to Regularized Likelihood Methods. In Advances in Large Margin Classifiers, 1999, 61–74. (Platt scaling,本文 §8 末尾提及的参数化校准)
  7. Gneiting, T.; Raftery, A. E. Strictly Proper Scoring Rules, Prediction, and Estimation. Journal of the American Statistical Association, 2007, 102(477), 359–378. (严格恰当评分规则的理论框架,说明为何 Brier 而非准确率适合评估概率预测)
  8. 本站 IN-02:《赔率 2.10 是不是就等于 47.6% 的概率?》 odds-implied-probability.html (隐含概率与过轮的算术,本文 §7 构造盘口的方法与之同源)
  9. 本站 IN-03:《命中 70% 却亏钱,命中 30% 却赚钱》 accuracy-vs-profit.html (EV = p × d − 1 的下注判据,本文 §7 直接沿用)

本文使用的 2000 场比赛为构造数据 (随机种子 20260808,生成代码见 §9),不对应任何真实赛事、平台报价或账户记录。 §7 的赔率由真实概率扣除 5% 抽水反推,属示例盘口。 表 1 至表 7 的全部数字由 _verify_calib.py 实际运行产生, 代码 2 的输出为该脚本真实 stdout,读者可按 §9 自行复算逐位核对。

本文是一篇模型评估方法论说明, 目的在于讲清准确率与概率校准的区别,不构成任何投注建议。 足球比赛结果具有高度不确定性,任何预测模型都无法保证盈利。