为什么要关心这件事
假设你手里有五个独立的预测系统,都在判断同一件事:"下个月A公司的股价会涨吗?"
五个模型分别给出:0.85、0.82、0.88、0.79、0.91。你该怎么把它们合并成一个最终的概率?
直接取算术平均?听起来合理,但问题来了------如果其中一个模型突然抽风,给出0.5(相当于没说),算术平均会把整体拉低很多。更本质的问题是:概率是有界的(0到1之间),边界附近的数值差异其实代表着巨大的置信度差别。0.99和0.999在概率上只差0.009,但在赔率意义上差了整整一个数量级。
再想另一个场景:模型说"80%概率会发生",但历史上它说80%的时候,实际只发生了60%。这个"80%"不是真实的频率,而是模型的"口头概率"。你需要一个翻译器,把模型语言翻译成真实世界的发生频率。
前者叫多模型预测分数融合 ,后者叫普拉特校准(Platt Scaling)。这两件事做好了,你的预测系统才能从"大概靠谱"变成"真正可用"。
一、多模型预测分数融合
1.1 最简单的办法:算术平均
最直觉的做法是把K个预测值加起来除以K:
pˉ=1K∑k=1Kpk \bar{p} = \frac{1}{K}\sum_{k=1}^{K} p_k pˉ=K1k=1∑Kpk
这个方法的好处是简单,而且对于Brier Score(平方误差)这类凸损失函数,根据Jensen不等式,取平均一定不会比单个预测差------至少在期望意义上。
但它有两个明显的问题。
1.2 问题一:边界信息被压缩------0.99与0.999的差距远超你的直觉
我们来算一笔账。假设有两个模型,一个说0.99,一个说0.999。
算术平均的结果:
0.99+0.9992=0.9945 \frac{0.99 + 0.999}{2} = 0.9945 20.99+0.999=0.9945
看起来差别不大,0.99到0.9945,好像只是微调了一下。
但换一个角度看------赔率。概率p对应的赔率是 p/(1-p),也就是"发生的可能性"比"不发生的可能性"大多少倍。
- 0.99 对应的赔率:0.99/0.01 = 99倍
- 0.999 对应的赔率:0.999/0.001 = 999倍
一个是99倍置信,一个是999倍置信,差了整整一个数量级。算术平均把这种巨大的置信差异压缩成了0.0045的数值差,信息损失非常严重。
1.3 更好的空间:logit空间平均
logit函数把概率从(0,1)映射到整个实数轴,本质上就是赔率取对数:
logit(p)=lnp1−p \text{logit}(p) = \ln\frac{p}{1-p} logit(p)=ln1−pp
它的逆函数是sigmoid,把实数映射回概率:
σ(x)=11+e−x \sigma(x) = \frac{1}{1+e^{-x}} σ(x)=1+e−x1
我们来算0.99和0.999的logit值:
logit(0.99)=ln0.990.01=ln(99)≈4.595 \text{logit}(0.99) = \ln\frac{0.99}{0.01} = \ln(99) \approx 4.595 logit(0.99)=ln0.010.99=ln(99)≈4.595
logit(0.999)=ln0.9990.001=ln(999)≈6.907 \text{logit}(0.999) = \ln\frac{0.999}{0.001} = \ln(999) \approx 6.907 logit(0.999)=ln0.0010.999=ln(999)≈6.907
在logit空间里,两者相差 6.907 - 4.595 = 2.312,这个差距清晰地反映了置信度的数量级差异。
logit空间取平均再映射回去:
平均logit=4.595+6.9072=5.751 \text{平均logit} = \frac{4.595 + 6.907}{2} = 5.751 平均logit=24.595+6.907=5.751
pˉ=σ(5.751)=11+e−5.751≈0.9968 \bar{p} = \sigma(5.751) = \frac{1}{1+e^{-5.751}} \approx 0.9968 pˉ=σ(5.751)=1+e−5.7511≈0.9968
这个结果有什么特殊含义?它等价于赔率的几何平均:
几何平均赔率=99×999=98901≈314.5 \text{几何平均赔率} = \sqrt{99 \times 999} = \sqrt{98901} \approx 314.5 几何平均赔率=99×999 =98901 ≈314.5
对应概率=314.51+314.5≈0.9968 \text{对应概率} = \frac{314.5}{1+314.5} \approx 0.9968 对应概率=1+314.5314.5≈0.9968
和logit平均的结果完全一致。
对比两种平均:
- 算术平均:0.9945(赔率约181倍)
- logit平均:0.9968(赔率约314倍)
算术平均把99倍和999倍的置信"中和"成了181倍,而logit平均保留了几何意义------两个独立证据的置信度应该是相乘的关系,取对数后变成相加,这正是贝叶斯更新的直觉。
1.4 问题二:失败运行的干扰太大
如果某次运行因为某种原因返回0.5(相当于弃权、没给出有效信息),算术平均会把高置信度的结果严重拉低。
举个例子:三次预测分别是 0.85、0.5、0.80。
算术平均:
0.85+0.5+0.803=0.717 \frac{0.85 + 0.5 + 0.80}{3} = 0.717 30.85+0.5+0.80=0.717
logit平均:
logit(0.85)≈1.735,logit(0.5)=0,logit(0.80)≈1.386 \text{logit}(0.85) \approx 1.735,\quad \text{logit}(0.5) = 0,\quad \text{logit}(0.80) \approx 1.386 logit(0.85)≈1.735,logit(0.5)=0,logit(0.80)≈1.386
平均logit=1.735+0+1.3863=1.040 \text{平均logit} = \frac{1.735 + 0 + 1.386}{3} = 1.040 平均logit=31.735+0+1.386=1.040
pˉ=σ(1.040)≈0.739 \bar{p} = \sigma(1.040) \approx 0.739 pˉ=σ(1.040)≈0.739
0.5在logit空间的值正好是0,相当于"什么都没说",只减少了有效样本数,不改变聚合方向。如果去掉那次失败的运行,剩下两次的logit平均是:
σ(1.735+1.3862)=σ(1.561)≈0.826 \sigma\left(\frac{1.735 + 1.386}{2}\right) = \sigma(1.561) \approx 0.826 σ(21.735+1.386)=σ(1.561)≈0.826
logit平均的0.739比算术平均的0.717更接近真实值0.826------失败运行对logit平均的干扰要小得多。
1.5 更聪明的融合:带先验的分层收缩
如果我们对这类问题有历史经验(比如"股价涨的历史基率是48%"),可以把这个先验信息也加进来。
公式长这样:
p=σ(αq⋅1K∑k=1Klogit(pk)+(1−αq)⋅logit(πq)) p = \sigma\left(\alpha_q \cdot \frac{1}{K}\sum_{k=1}^{K} \text{logit}(p_k) + (1-\alpha_q) \cdot \text{logit}(\pi_q)\right) p=σ(αq⋅K1k=1∑Klogit(pk)+(1−αq)⋅logit(πq))
其中 πq\pi_qπq 是问题的先验基率,αq\alpha_qαq 是一个0到1之间的权重,由K次试验的方差决定:
- 试验结果高度一致(方差小)→ αq\alpha_qαq 接近1 → 信任试验结果
- 试验结果分歧很大(方差大)→ αq\alpha_qαq 变小 → 被拉向先验
完整计算示例(K=3):
场景A:三次试验很一致
- 三次输出:p₁=0.85, p₂=0.82, p₃=0.88
- 先验基率:π_q = 0.60
- 结果方差很小(约0.0006),α_q ≈ 0.994
计算过程:
logit(0.85)≈1.735,logit(0.82)≈1.516,logit(0.88)≈1.992 \text{logit}(0.85) \approx 1.735,\quad \text{logit}(0.82) \approx 1.516,\quad \text{logit}(0.88) \approx 1.992 logit(0.85)≈1.735,logit(0.82)≈1.516,logit(0.88)≈1.992
试验logit均值=1.735+1.516+1.9923=1.748 \text{试验logit均值} = \frac{1.735 + 1.516 + 1.992}{3} = 1.748 试验logit均值=31.735+1.516+1.992=1.748
先验logit=logit(0.60)≈0.405 \text{先验logit} = \text{logit}(0.60) \approx 0.405 先验logit=logit(0.60)≈0.405
加权logit=0.994×1.748+0.006×0.405=1.740 \text{加权logit} = 0.994 \times 1.748 + 0.006 \times 0.405 = 1.740 加权logit=0.994×1.748+0.006×0.405=1.740
p=σ(1.740)≈0.851 p = \sigma(1.740) \approx 0.851 p=σ(1.740)≈0.851
结果0.851几乎就是试验平均值,先验0.60几乎没影响------因为三次运行都很笃定,系统选择相信模型。
场景B:三次试验分歧巨大
- 三次输出:p₁=0.90, p₂=0.45, p₃=0.15
- 先验基率:π_q = 0.55
- 结果方差很大(约0.095),α_q ≈ 0.513
计算过程:
logit(0.90)≈2.197,logit(0.45)≈−0.201,logit(0.15)≈−1.735 \text{logit}(0.90) \approx 2.197,\quad \text{logit}(0.45) \approx -0.201,\quad \text{logit}(0.15) \approx -1.735 logit(0.90)≈2.197,logit(0.45)≈−0.201,logit(0.15)≈−1.735
试验logit均值=2.197−0.201−1.7353=0.087 \text{试验logit均值} = \frac{2.197 - 0.201 - 1.735}{3} = 0.087 试验logit均值=32.197−0.201−1.735=0.087
先验logit=logit(0.55)≈0.201 \text{先验logit} = \text{logit}(0.55) \approx 0.201 先验logit=logit(0.55)≈0.201
加权logit=0.513×0.087+0.487×0.201=0.143 \text{加权logit} = 0.513 \times 0.087 + 0.487 \times 0.201 = 0.143 加权logit=0.513×0.087+0.487×0.201=0.143
p=σ(0.143)≈0.536 p = \sigma(0.143) \approx 0.536 p=σ(0.143)≈0.536
结果0.536被明显拉向先验0.55。三次运行一个说很可能、一个说五五开、一个说不太可能,模型自己都吵翻了,这时候系统选择回归历史经验,不做过度自信的判断。
二、普拉特校准(Platt Scaling)
2.1 校准到底在做什么
模型输出的"概率"往往不是真实概率。比如一个分类器说"80%置信是猫",但统计下来,它说80%的那些图片里,其实只有60%真的是猫。
校准就是给模型的输出装一个翻译器,把"模型说的概率"翻译成"真实发生的频率"。
2.2 全局普拉特校准:怎么拟合
Platt scaling的做法是用一个sigmoid函数把原始预测映射到校准后的概率:
calibrated(p)=σ(a⋅logit(p)+b) \text{calibrated}(p) = \sigma\left(a \cdot \text{logit}(p) + b\right) calibrated(p)=σ(a⋅logit(p)+b)
里面有两个参数:
- a(斜率):控制收缩程度。a < 1表示把极端预测往中间压
- b(截距):控制整体左移或右移
怎么找到a和b?用验证集拟合。
假设我们有8个验证样本,模型预测过,且已知真实结果:
| 样本 | 模型预测p | 真实结果y |
|---|---|---|
| 1 | 0.2 | 0(未发生) |
| 2 | 0.3 | 0 |
| 3 | 0.4 | 0 |
| 4 | 0.5 | 0 |
| 5 | 0.6 | 1(发生) |
| 6 | 0.7 | 1 |
| 7 | 0.8 | 1 |
| 8 | 0.9 | 1 |
观察规律:模型预测≤0.5时都没发生,≥0.6时都发生了。但模型太"犹豫"了------0.6应该更接近1,0.5应该更接近0。
拟合目标:找一组(a, b),让校准后的概率尽量匹配真实标签。数学上就是最小化负对数似然:
mina,b−∑iyi⋅ln(σ(a⋅logit(pi)+b))+(1−yi)⋅ln(1−σ(⋯ )) \min_{a,b} \quad -\sum_{i} \lefty_i \\cdot \\ln(\\sigma(a\\cdot\\text{logit}(p_i)+b)) + (1-y_i)\\cdot\\ln(1-\\sigma(\\cdots))\\right a,bmin−i∑yi⋅ln(σ(a⋅logit(pi)+b))+(1−yi)⋅ln(1−σ(⋯))
说人话就是:真实发生的事件(y=1),校准后概率越大越好;没发生的(y=0),校准后概率越小越好。
用上面8个样本拟合后,假设得到 a=2.5, b=0。我们来验证效果:
| 原始p | logit§ | 2.5×logit§ | σ(结果) = 校准后 |
|---|---|---|---|
| 0.2 | -1.386 | -3.465 | 0.031 |
| 0.3 | -0.847 | -2.118 | 0.107 |
| 0.4 | -0.405 | -1.013 | 0.266 |
| 0.5 | 0.000 | 0.000 | 0.500 |
| 0.6 | +0.405 | +1.013 | 0.734 |
| 0.7 | +0.847 | +2.118 | 0.893 |
| 0.8 | +1.386 | +3.465 | 0.969 |
| 0.9 | +2.197 | +5.493 | 0.996 |
校准后,低的更低、高的更高,模型从"模棱两可"变得"果断明确"。a=2.5 > 1说明这个模型本身太保守,需要把预测往两端拉伸。
2.3 全局校准的问题:基率偏斜的陷阱
全局校准用同一套(a, b)处理所有问题。但当你的系统处理多种不同类型的问题时,麻烦就来了------不同类别的问题,历史基率可能天差地别。
举个具体的例子,假设你的系统处理四类问题:
| 数据源 | 问题类型 | 历史基率(Yes的比例) |
|---|---|---|
| ACLED | 某国冲突死亡人数30天内翻10倍 | 2% |
| Wikipedia | 某国疫苗接种率年底达标 | 3% |
| yfinance | 某股票30天后涨了 | 48% |
| FRED | 美国GDP本季度正增长 | 65% |
ACLED的问题,100个里只有2个答案是Yes;FRED的问题,100个里有65个是Yes。基率差了30多倍。
现在全局校准拟合出了参数 a=0.8, b=0.1。我们来看ACLED的一个预测:
问题 :苏丹冲突死亡人数会在30天内翻10倍吗?
模型原始预测 :p = 0.05(模型认为不太可能,给了5%概率)
真实基率:2%
全局校准计算:
logit(0.05)=ln0.050.95≈−2.944 \text{logit}(0.05) = \ln\frac{0.05}{0.95} \approx -2.944 logit(0.05)=ln0.950.05≈−2.944
a⋅logit(p)+b=0.8×(−2.944)+0.1=−2.255 a \cdot \text{logit}(p) + b = 0.8 \times (-2.944) + 0.1 = -2.255 a⋅logit(p)+b=0.8×(−2.944)+0.1=−2.255
calibrated=σ(−2.255)≈0.095 \text{calibrated} = \sigma(-2.255) \approx 0.095 calibrated=σ(−2.255)≈0.095
模型本来给了0.05,已经比较接近真实基率0.02了。全局校准反而把它拉到了0.095,越校越偏。
为什么会这样?因为全局参数要"照顾"所有数据源。yfinance的基率接近0.5,校准曲线在中间区域要准确;ACLED的基率极端偏斜,但样本量可能不大,全局优化不会为了它大幅调整参数。结果就是:基率正常的源校准得不错,基率极端的源被牺牲了。
2.4 分层普拉特校准:偏移值怎么来
解决办法是保留全局参数(a, b),但给每个数据源s增加一个独立的偏移量 δ_s:
calibrateds(p)=σ(a⋅logit(p)+b+δs) \text{calibrated}_s(p) = \sigma\left(a \cdot \text{logit}(p) + b + \delta_s\right) calibrateds(p)=σ(a⋅logit(p)+b+δs)
δ_s 怎么计算?两步分层拟合:
第一步:拟合全局参数(a, b)
把所有数据源的样本混在一起,忽略数据源差异,先拟合出全局的a和b。这一步和普通Platt scaling完全一样。
第二步:固定(a, b),逐个拟合δ_s
对每个数据源s,拿它自己的验证样本,在固定a和b的前提下,单独优化δ_s:
minδs−∑i∈syi⋅ln(σ(a⋅logit(pi)+b+δs))+⋯ +λ⋅δs2 \min_{\delta_s} \quad -\sum_{i \in s} \lefty_i \\cdot \\ln(\\sigma(a\\cdot\\text{logit}(p_i)+b+\\delta_s)) + \\cdots\\right + \lambda \cdot \delta_s^2 δsmin−i∈s∑yi⋅ln(σ(a⋅logit(pi)+b+δs))+⋯+λ⋅δs2
后面加了一项 L2 正则 λ·δ_s²,防止某个数据源因为样本少而过度偏移。正则项的作用是把δ_s往0的方向"拉",只有当数据充分支持时,偏移才会变大。
直观理解δ_s和基率的关系:
δ_s大致等于该数据源基率的logit值。基率低于0.5,δ_s为负;基率高于0.5,δ_s为正:
| 数据源 | 基率 | logit(基率) | δ_s(约) | 含义 |
|---|---|---|---|---|
| ACLED | 0.02 | -3.89 | -1.5 | 极低基率,大幅负偏移 |
| Wikipedia | 0.03 | -3.48 | -1.2 | 极低基率,大幅负偏移 |
| yfinance | 0.48 | -0.08 | 0.0 | 接近中性,无需偏移 |
| FRED_GDP | 0.65 | +0.62 | +0.4 | 偏高基率,正偏移 |
注意δ_s的绝对值比logit(基率)小------因为有L2正则在约束,而且全局参数b已经承担了一部分偏移。δ_s只是在全局基础上的"微调"。
2.5 完整数值对比:全局 vs 分层
继续用上面的参数:全局 a=0.8, b=0.1;各源δ:ACLED=-1.5,Wikipedia=-1.2,yfinance=0,FRED=+0.4。
案例一:模型给出合理预测(接近基率)
| 数据源 | 原始p | 全局校准计算 | 全局结果 | 分层校准计算 | 分层结果 | 真实基率 |
|---|---|---|---|---|---|---|
| ACLED | 0.05 | σ(0.8×(-2.944)+0.1) | 0.095 | σ(0.8×(-2.944)+0.1-1.5) | 0.023 | 0.02 |
| yfinance | 0.55 | σ(0.8×0.201+0.1) | 0.565 | σ(同上+0) | 0.565 | 0.48 |
ACLED原始0.05已经接近基率0.02了,全局校准拉到0.095反而偏离。分层校准通过δ=-1.5精确拉回0.023,几乎就是真实基率。
案例二:模型过度乐观(严重偏离基率)
ACLED问题,模型误读了某条新闻,给出 p=0.80:
- 全局校准:σ(0.8×1.386 + 0.1) = σ(1.209) ≈ 0.770
- 分层校准:σ(0.8×1.386 + 0.1 - 1.5) = σ(-0.291) ≈ 0.428
全局校准几乎没起作用(0.80→0.77),仍然严重偏离基率0.02。分层校准通过ACLED专属的负偏移,把结果压到0.43,向真实基率大幅回归------虽然还不够低,但比全局版本靠谱多了。
为什么叫"分层"? 全局参数(a, b)在顶层共享,所有数据源共同学习;偏移量δ_s在底层独立,每个数据源自适应调整。形成"全局-局部"两层结构,既保证了泛化能力,又照顾了各源的特殊性。
三、完整预测流程:从问题到答案
结合BLF论文《Agentic Forecasting using Sequential Bayesian Updating of Linguistic Beliefs》的设计,我们用一个完整案例走一遍全流程。
3.1 案例背景
问题:WorldAtlas.com 会在2025年7月1日前展示"美洲湾"(Gulf of America)吗?
背景:2025年1月20日,特朗普签署行政令,要求将"墨西哥湾"改名为"美洲湾"。Google Maps已经改了,但WorldAtlas这个网站呢?
数据源类型:市场类问题(来自预测平台),先验(市场价格)约为0.57
3.2 第一步:初始化信念
初始信念 p₀ = 0.5,置信度低,没有任何证据。
3.3 第二步:迭代推理循环(5次独立运行)
对同一个问题跑5次独立的搜索-推理循环,每次都是不同的搜索路径。以下是其中一次(Trial 2)的完整过程:
| 步骤 | 概率 | 动作与关键证据 |
|---|---|---|
| 0 | 0.50 | 初始状态,一无所知 |
| 1 | 0.05 | 搜索"改名成功率" → 发现"历史上改名尝试大多失败,基率<1%",概率骤降 |
| 2 | 0.65 | 查阅白宫官网 → 确认行政令确实签署了;搜索发现Google Maps已更新标签,概率回升 |
| 3-8 | 0.65 | 多次搜索WorldAtlas相关信息,没有决定性发现,卡在0.65 |
| 9 | 0.28 | 搜索"worldatlas.com maps API" → 关键发现:WorldAtlas用的是静态地图图片,不是动态Google Maps!静态网站更新地图通常要1-3年 |
| 10 | 0.28 | 提交预测 |
5次独立运行的最终结果:
- Trial 1: p = 0.62
- Trial 2: p = 0.28(发现了静态地图这个关键点)
- Trial 3: p = 0.41
- Trial 4: p = 0.83
- Trial 5: p = 0.73
5次结果差异很大,标准差约0.20------这就是要跑多次的原因,单次运行的随机性太强了。
3.4 第三步:多试次聚合
算术平均(默认方法):
pˉ=0.62+0.28+0.41+0.83+0.735=2.875=0.574 \bar{p} = \frac{0.62 + 0.28 + 0.41 + 0.83 + 0.73}{5} = \frac{2.87}{5} = 0.574 pˉ=50.62+0.28+0.41+0.83+0.73=52.87=0.574
如果用分层收缩聚合(带先验):
先算各次的logit:
- logit(0.62) ≈ 0.489
- logit(0.28) ≈ -0.942
- logit(0.41) ≈ -0.364
- logit(0.83) ≈ 1.586
- logit(0.73) ≈ 0.996
试验logit均值:
0.489−0.942−0.364+1.586+0.9965=1.7655=0.353 \frac{0.489 - 0.942 - 0.364 + 1.586 + 0.996}{5} = \frac{1.765}{5} = 0.353 50.489−0.942−0.364+1.586+0.996=51.765=0.353
5次试验方差较大,假设 α_q = 0.7(分歧中等,部分回归先验)。
先验 π_q = 0.57,logit(0.57) ≈ 0.282。
加权logit:
0.7×0.353+0.3×0.282=0.247+0.085=0.332 0.7 \times 0.353 + 0.3 \times 0.282 = 0.247 + 0.085 = 0.332 0.7×0.353+0.3×0.282=0.247+0.085=0.332
聚合结果:
pagg=σ(0.332)≈0.582 p_{\text{agg}} = \sigma(0.332) \approx 0.582 pagg=σ(0.332)≈0.582
因为试验分歧不小,结果被稍微拉向了先验0.57。
3.5 第四步:分层普拉特校准
假设这是市场类问题,对应数据源的 δ_s = +0.1(市场类问题基率略偏高于0.5)。全局参数 a=0.9, b=0.05。
校准计算:
logit(0.582)≈0.331 \text{logit}(0.582) \approx 0.331 logit(0.582)≈0.331
a⋅logit(p)+b+δs=0.9×0.331+0.05+0.1=0.448 a \cdot \text{logit}(p) + b + \delta_s = 0.9 \times 0.331 + 0.05 + 0.1 = 0.448 a⋅logit(p)+b+δs=0.9×0.331+0.05+0.1=0.448
pfinal=σ(0.448)≈0.610 p_{\text{final}} = \sigma(0.448) \approx 0.610 pfinal=σ(0.448)≈0.610
3.6 最终结果
校准后的最终预测:约61%概率会展示。
真实结果:No(没有展示)。
虽然这次预测错了,但注意5次运行中有一次(Trial 2)发现了关键线索并给出了正确方向的低概率(0.28)。多试次聚合的意义就在于:即使大部分运行跑偏,只要有一次抓住关键信息,它就能在一定程度上修正整体结果。而校准则确保这个61%是一个"诚实"的概率估计------系统说61%的时候,长期来看大约61%的事件真的会发生。
3.7 全流程总结
问题输入
↓
初始化信念(p=0.5,无证据)
↓
K次独立迭代循环(搜索→推理→更新信念)
↓ 产出K个原始预测
多试次聚合(算术平均 / logit收缩 + 先验)
↓ 产出一个聚合后的预测
分层普拉特校准(按数据源加δ_s偏移)
↓ 产出校准后的真实概率估计
最终输出
三个核心模块各司其职:
- 信念迭代:保证信息被充分搜集和推理
- 多试次聚合:降低单次运行的随机性
- 分层校准:消除系统性偏差,输出可信的概率