版权与内容来源声明
本文为原创整理。文中涉及官方文档、开源仓库、论文与公开报道的内容,均在附表 A 中标注来源;引用官方原文保持原样,不作改写。文中命令、版本号与界面截图以本文成文时的实测/核验结果为准,标注「待验证」的部分请以你本地环境实际输出为判断依据。本文不推荐任何破解、盗版软件,也不推荐任何规避地区限制的访问方式,亦不对任何收益结果作承诺。转载请注明出处。
一、那句「置信度 0.9」到底在说什么
1.1 一个很常见的接法
合同要素抽取上线第二周,业务方在需求群里发了一张截图:同一条条款,模型抽出的付款账期是「60 天」,后面跟着confidence: 0.9。他们的结论很干脆——九成把握,那就不用全量人工复核了,剩下那一成抽检。
这个推理每一步都合理,只有一处没有验证:0.9 这个数,是不是「打 0.9 分的那批样本里,九成确实是对的」?
多数时候不是。它更接近「模型内部给这个答案打了 0.9 的分」。一个是频率,一个是打分,长得一模一样,含义差得很远。频率可以拿去算期望、定人力、做报价;打分只能用来排序和分档。
1.2 不是模型在撒谎,是分数没对过表
Guo、Pleiss、Sun、Weinberger 在On Calibration of Modern Neural Networks(ICML 2017,arXiv:1706.04599)的摘要里写得很直接:modern neural networks, unlike those from a decade ago, are poorly calibrated。
这句话经常被读成「新模型不如老模型」,其实它说的是另一件事:十来年前的浅层网络,分数碰巧更像频率;网络变深变宽之后,这个巧合没有了。所以不是模型学会了撒谎,是它输出的那个数,本来就没有承诺过「等于频率」。
同一篇摘要还点了几个影响因素——depth、width、weight decay、Batch Normalization,并给出结论:temperature scaling,作为 Platt Scaling 的单参数变体,在校准预测这件事上出奇地有效。注意最后半句:它说的是校准,不是更准。这两件事经常被混为一谈。
1.3 准和校准是两件事
一个模型可以很准但校准很差,也可以不太准但校准很好。把这两个维度交叉一下,四种情形在业务上长得完全不同:
| 情形 | 平均准确率 | 平均置信度 | 校准状态 | 业务侧的感受 |
|---|---|---|---|---|
| 又准又说实话 | 高 | 高,且和准确率贴合 | 好 | 阈值好定,人力好排 |
| 很准但话太满 | 高 | 普遍 0.99 | 差,过度自信 | 按 0.99 排的人力永远不够 |
| 不太准但很谦 | 中 | 普遍 0.55 | 差,欠自信 | 明明能用,却被大量拒答 |
| 又差又满 | 低 | 普遍 0.95 | 差,过度自信 | 最危险的一种,报表还很好看 |
一句判据:准确率问「对了多少」,校准问「说 0.9 的时候是不是真有九成对」。这两个数可以一个很好一个很差,修好一个不会自动带上另一个。
1.4 先把问题定位对
业务方那句「置信度 0.9 就该九成对」,错在把一个排序信号当成了频率估计。这个定位很关键:如果以为是「模型能力不够」,去换更大的模型、加更多数据,校准未必变好,甚至可能更差(1.2 节那句摘要说的正是这个方向);如果知道是「分数没对过表」,那么要做的是一次校准加一张图,工作量小得多,也更可控。
二、ECE:把「说多满」和「做对多少」压成一个数
2.1 先分箱
做法很朴素:按模型自己给出的分数,切成 10 到 15 个等宽区间(下称箱),把样本按分数丢进对应的箱。然后对每个箱问两件事——这箱平均说了多少(conf),这箱实际对了多少(acc)。
如果模型是校准好的,这两列应该几乎相等:说 0.8 的那箱,实际就该对八成。差距出现在哪里、有多大,就是校准问题的形状。
2.2 公式逐项读
ECE = Σ_m (|B_m| / n) · |acc(B_m) − conf(B_m)|
m是箱的序号,B_m是落在第 m 箱里的样本集合;|B_m| / n是这个箱占总样本的比例,起权重作用:人多的箱说话更算数;acc(B_m)是箱内实际答对的比例,conf(B_m)是箱内分数的平均值;- 外面套绝对值,再对所有箱求和。
为什么要套绝对值。如果不套,一半样本过度自信(差 +0.1)、一半欠自信(差 −0.1),加起来正好抵消,ECE 会告诉你「完美校准」。这是这个指标最容易踩的坑,也是它最容易被拿来粉饰的地方:只要模型在两端各错一个方向,平均数就干净了。
为什么是 10 到 15 箱。箱太少,箱内差异被平均掉,过度自信和欠自信在同一个箱里对消;箱太多,每箱样本变少,acc 的随机抖动盖过真实差距,ECE 变成噪声。箱数的上界由样本量决定,经验上每箱至少几十条才有意义;样本只有几百条的时候,报 ECE 必须同时报样本量,否则这个数字没法判断。
2.3 二十行算一遍
下面这段只依赖标准库,造一组「分数偏高、准确率偏低」的数据,把分箱和 ECE 走通。
importrandom random.seed(7)rows=[]# (打分, 是否答对)for_inrange(4000):p=random.random()rows.append((min(0.999,p**(1/2.6)),1ifrandom.random()<pelse0))defece(rows,bins=10):box=[[]for_inrange(bins)]fors,yinrows:box[min(int(s*bins),bins-1)].append((s,y))total=0.0forbinbox:ifnotb:continueconf=sum(sfors,_inb)/len(b)acc=sum(yfor_,yinb)/len(b)total+=abs(acc-conf)*len(b)/len(rows)# 权重乘差距returntotalprint(round(ece(rows),4))跑法:
python calib.py输出是一个小数。这组合成数据的 ECE 是 0.218——但单看这个数没有意义,得拆开看箱。逐箱明细节选如下:
| 置信区间 | 箱内样本 | 平均置信 conf | 实际准确率 acc | 差距 acc 减 conf | 对 ECE 的贡献 |
|---|---|---|---|---|---|
| [0.4, 0.5) | 321 | 0.451 | 0.125 | 负 0.327 | 0.0262 |
| [0.6, 0.7) | 529 | 0.655 | 0.355 | 负 0.299 | 0.0396 |
| [0.7, 0.8) | 623 | 0.752 | 0.477 | 负 0.276 | 0.0429 |
| [0.8, 0.9) | 796 | 0.852 | 0.673 | 负 0.178 | 0.0355 |
| [0.9, 1.0) | 937 | 0.952 | 0.871 | 负 0.081 | 0.0190 |
这张表要连着两句说明看:第一,数据是脚本里用随机数合成的,只用来把分箱口径跑通,不代表任何具体模型、产品或厂商的校准水平;第二,它演示的形态是标准的过度自信——每一箱的实际准确率都低于平均置信度,最高那一箱说了 0.95,实际只对 0.87。开头那个「0.9 当九成用」的现场,就差在最后这 8 个点上。
2.4 ECE 的三个局限
- 它把差距平均掉了。一个 ECE 可能由某一箱的巨大差距贡献,也可能由十箱各差一点摊出来,前者要命,后者也许能忍。要看分布,就得看图。
- 它对箱边界敏感。同样一批数据,换一种分箱方式,数值会变。所以报告里必须写箱数,两次比较必须用同一套边界。
- 它是无条件的平均。业务真正关心的往往只是某一段(比如只用 0.9 以上那批直出),而 ECE 把 0.3 到 0.5 那堆没人用的样本也算进去了。
三、可靠性图:45° 线上下三种形态
3.1 怎么画,用文字也能说清
横轴是置信度,纵轴是实际准确率,取值都是 0 到 1。从左下角到右上角画一条对角线,这条 45° 线上每个点都满足「说多少就是多少」,它是参照系,不是目标形态——没有哪个真实模型会精确落在线上。
然后把每个箱画成一个点:横坐标取该箱的平均置信度,纵坐标取该箱的实际准确率,点的大小可以是箱内样本数。把点连成折线,就得到可靠性图。不需要任何绘图库,把 2.3 节那张表的两列照坐标点上去,形状就出来了。
3.2 三种形态
| 形态 | 折线相对 45° 线的位置 | 读起来是什么 | 常见成因 | 先动哪里 |
|---|---|---|---|---|
| 过度自信 | 整体落在 45° 线下方:说 0.9,点在 0.8 出头 | 话比本事满 | 网络深、训练充分、BatchNorm、weight decay 偏大(Guo 等把这几个列为影响因素) | 单一温度参数就能压回大半 |
| 欠自信 | 整体落在 45° 线上方:说 0.6,点在 0.75 | 本事比话多 | 训练不足、正则过强、标签噪声大、模型偏小 | 温度往小调,但先排除数据问题 |
| S 形 | 两端在线的一方、中段在另一方,整体像被拧过 | 一头要压、一头要抬 | 分数被挤在中间段,或不同来源的数据混在一起 | 分段校准,或按业务分段各拟一个温度 |
读这张表有个前提:三种形态都只是「形状」,成因那一列是经验归类,不是论文结论。同一张图上的同一个形状,换一个数据集可能是完全不同的原因,所以要回到数据本身去看,不能照表抓药。
3.3 看图先看两端
中间段样本最多、曲线最平,最容易给人「还行」的错觉;真正决定业务后果的是两端:你打算用的那个阈值附近,以及你最不敢放手的高分端。
回到开头那个场景:业务要用的是 0.9 以上那一档直出,那就把 0.85 到 1.0 单独放大看,看这一段的点离 45° 线有多远、这一段有多少样本。整体 ECE 好看,不代表这一段好看;反过来,整体难看,也不代表你要用的那一段不能用。
3.4 S 形为什么麻烦
温度缩放是一个单调变换:所有分数按同一条曲线重排,整体往一个方向压或拉。S 形的问题恰恰是「一头要压、一头要抬」,一个参数做不到两件事,硬拟的结果是两头的误差都没修好,中段还被带偏。
遇到 S 形只有两条路:要么分段(按分数段或按业务线各拟一个温度),要么承认这段分数只能当序用——它能把样本从最不可信排到最可信,但不能报成概率。后者听起来是退让,实际上比报一个错概率安全得多。
四、三种「置信度」,测法完全不同
4.1 logits 概率
softmax 之后的那个数,是最容易被当成「概率」的一个。三个坑:
- 解码方式一改,含义就变了。贪心解码拿到的是 top-1 的分数;换成采样(top-k、top-p、调温度),分布被改过,再从这个分布里取概率,量纲就不再是同一件事。历史阈值在换配置那天全部作废。
- 序列级概率要连乘。一句话三十个 token,每个 0.99,连乘下来是 0.74。乘不乘、要不要按长度归一化,各家做法不同,报数时必须说清是 token 级还是序列级。
- 只在同一套配置下可比。换模型、换提示、换解码参数,都要重新校准,不能拿旧阈值套新配置。
4.2 自述概率
另一条路是让模型自己把把握写出来,比如「90% confidence」。Lin、Hilton、Evans 在Teaching Models to Express Their Uncertainty in Words(arXiv:2205.14334,2022)里走的就是这条:GPT-3 可以不用 logits,直接用自然语言表达置信度,而且这些自述概率是校准的;作者同时提出 CalibratedMath 测试集,并把 verbalized probability 与从 logits 提取不确定性这两条路放在一起比较。
要抓住的是两个点:不用 logits,以及自述概率是校准的。别顺手延伸成「自述一定比 logits 好」——原文做的是比较,不是排名;而且「校准」这个结论建立在它自己的测试集与提问方式上,换一种问法、换一个任务,还是要重新画图。
4.3 采样一致性
同一个问题采样 N 次,看答案分布:8 次选 A、2 次选 B,一致率 0.8。这个数很好拿,也很容易被误用,因为它测的是稳定性,不是正确率。
系统性的错会被稳定地重复。一个每次都错得一模一样的模型,一致率可以接近满分。所以采样一致性适合回答「这个答案稳不稳、要不要再问一遍」,不适合回答「这个答案有多大概率对」。它还有个隐性成本:N 倍推理开销,N 取多少又会反过来影响这个数本身。
4.4 一张对照表
| 维度 | logits 概率 | 自述概率 | 采样一致性 |
|---|---|---|---|
| 从哪来 | softmax 的输出 | 模型自己写出的百分比 | 同一问题多次采样的答案分布 |
| 测的是什么 | 模型内部的相对偏好 | 模型对「自己会不会对」的判断 | 输出的稳定性 |
| 能否直接当频率 | 要先校准,且绑定解码配置 | 校准过才能用,须注明在哪个集合上校的 | 不能,它是稳定性而非正确率 |
| 额外成本 | 要能拿到 logits | 多一次生成或一次追问 | N 倍推理开销 |
| 典型误用 | 换了解码配置还在用老阈值 | 把「九成把握」当承诺写进合同 | 一致率高就当成对率高 |
一句纪律:报表上写「置信度」三个字不够,必须同时写清是哪一种、在哪个集合上校准过、用什么解码配置取出来的。三项缺一项,这个数就无法被复核,也就没法在出问题的时候定位。
五、拒答:阈值不是拍出来的,是选出来的
5.1 允许模型说「不知道」
选择性预测的思路很直接:分数低于阈值就不作答,转人工或输出「无法判断」。剩下的那部分,错误率会降——最没把握的那批被挡在门外了。代价是覆盖率下降,也就是有更多问题需要人来兜底。
这件事的关键在于它是一笔兑换,不是一次提升。拒答不会让模型变聪明,它只是把「答错」换成了「不答」,而「不答」也有成本,只是这个成本落在别人头上(人工、时效、体验)。
5.2 覆盖率与错误率
横轴覆盖率(作答的样本占比),纵轴是该覆盖率下的错误率。阈值从低往高抬,工作点从右上往左下走:错误率降,覆盖率也降。
这条曲线之所以重要,是因为它把兑换比例摆在了明面上。没有它,讨论「阈值定多少」就只能是拍脑袋;有了它,至少能说清「定在 0.9 意味着七成六的问题要转人工」。
5.3 跑一遍看形状
defcurve(rows,ts=(0.7,0.8,0.9,0.95)):n=len(rows)fortints:kept=[(s,y)fors,yinrowsifs>=t]cover=len(kept)/n err=sum(1-yfor_,yinkept)/len(kept)ifkeptelse0.0print(f"阈值{t:.2f}覆盖率{cover:.3f}剩余错误率{err:.3f}")用 2.3 节那组合成数据,四个阈值跑出来是这样:
| 阈值 | 覆盖率 | 剩余错误率 | 被拒比例 | 业务侧发生了什么 |
|---|---|---|---|---|
| 0.70 | 0.589 | 0.300 | 0.411 | 四成人要人工看,错误率从 0.501 降到 0.300 |
| 0.80 | 0.433 | 0.220 | 0.567 | 一半以上转人工 |
| 0.90 | 0.234 | 0.129 | 0.766 | 只剩两成多直出 |
| 0.95 | 0.123 | 0.061 | 0.877 | 直出不到一成三,更像一个昂贵的过滤器 |
表里 0.501 是不拒答时的整体错误率。再强调一次:这组数据是刻意造出来的差模型,只为把曲线形状跑出来,不是任何真实系统的水平。真实系统上这条曲线通常更平,也就是说,同样的阈值抬升换来的错误率下降更少——那就更需要在选点前先看清形状。
5.4 选点看业务能接受哪一头
把两笔成本写出来:一次错误流到下游的返工成本记作C_e,一次拒答的人工处理成本记作C_r。总成本大致是「错误数 × C_e + 拒答数 × C_r」,在曲线上逐个工作点扫一遍,最低点就是该选的阈值。
- 如果
C_e远大于C_r(报价单错一个数字要赔钱),阈值往高抬,宁可多拒; - 如果
C_r远大于C_e(只是给坐席做提示,错了人一眼能看出来),阈值往低压,宁可多答。
这两个成本的比值,业务方必须自己给,工程侧给不了,也不该替业务决定。工程能做的是把曲线画准、把每个工作点的代价算清,然后让选点变成一个可以争论的数字,而不是一句「感觉 0.9 差不多」。
反模式也要提一句:为了让报表上的准确率好看,把阈值抬到 0.95,覆盖率只剩一成多。数字是漂亮了,但系统不在干活,原本的人工工作量一分没少,还多了一层维护成本。
这份资料是什么:一页校准自查清单,把「画图、拟合、分档、选点」四步该记录的字段列成表,和本章的兑换逻辑配套。放在资料包里,扫码即可获取:
六、温度和分箱:参数只能碰验证集
6.1 温度缩放改的是什么
把 logits 除以 T 再做 softmax:T 大于 1 把分布压平,降低过度自信;T 小于 1 把分布变尖,提高自信。它只有一个参数,而且不改变 argmax——也就是说,它不动准确率,只动分数。
这正是它好用的原因。校准的职责是让分数对得上频率,不该顺手把排序改了;如果一次校准之后准确率变了,那多半不是校准,是又训了一遍模型。Guo 等说它「出奇地有效」,分量落在「单参数」上:不需要为每个类别、每个分数段各拟一套参数,一个 T 就能把大部分过度自信压回去。
6.2 三条纪律
第一,T 只能用验证集拟合。在测试集上拟 T,等于让校准器提前看了答案,报出来的 ECE 是自己骗自己。数据划分应该是训练、验证、测试三段:T 在验证集上拟,ECE 只在测试集上算一次,算完就封存。
第二,分箱边界也要固定在验证集上。边界每次现定,两次 ECE 就没有可比性,也没法判断「这次改动到底有没有用」。报告里写清箱数和样本量,是让这个数字可复核的最低要求。
第三,拟合目标要说清楚。常见做法是在验证集上最小化负对数似然,而不是直接最小化 ECE。原因是 ECE 依赖分箱,不可导也不平滑,拿它当优化目标会很不稳;用似然拟合、用 ECE 验收,是更常见的分工。
6.3 什么时候温度也不够
- 分布漂移。上线三个月,输入分布换了,0.9 的含义跟着变,而 T 是旧分布下的参数。校准不是一次性动作,是要定期回算的。
- 分段差异。不同业务线的分数分布差别很大,一个全局 T 顾此失彼,只能分段拟。
- 长尾类别。样本极少的类别,验证集上那几箱几乎是空的,拟合出来的 T 被大类主导,小类等于没校准。
这三种情况都不是「再调一下参数」能解决的,要靠分段、分层分别拟,或者干脆承认这段分数只能用来看排序。承认这一点不丢人,把没校准的数当概率用才丢人。
七、上线判据:把置信度当分级信号
7.1 三条判据
| 环节 | 该做什么 | 常见反例 | 为什么 |
|---|---|---|---|
| 上线前 | 在留出集上画一次可靠性图,记录箱数与样本量 | 只看训练集的损失曲线 | 训练集上的分数天然偏乐观,越训越乐观 |
| 拟合 | T 只在验证集上拟,测试集只用一次 | 反复在测试集上调 T 直到 ECE 好看 | 调的次数越多,报出来的数越假 |
| 使用 | 置信度当分级信号:高直出、中抽检、低转人工 | 把 0.9 乘进报价、乘进风控评分 | 乘法要求这个数是频率,而它不是 |
三条里最容易被跳过的是第一条,因为它不产生任何功能。但它是唯一能在上线前发现「分数整体偏高一截」的手段,而这个问题一旦上线,只会以「模型老出错」的形式暴露,排查成本高得多。
7.2 为什么不能乘进报价和风控
把 0.9 乘进期望值,等于默认「打 0.9 的样本里有九成是对的」。如果实际只有八成七,这个期望值就系统性偏高;而报价、授信、风控这类场景是线性外推,偏差会被金额放大——不是「稍微不准」,而是「一直往一个方向偏」。
更麻烦的是它不可解释。结果错了,你说不清是模型判错了,还是概率乘错了;两种错混在一个数字里,改哪一头都只能靠猜。分级信号没有这个问题:档位切错,看那一档的回算准确率就知道错在哪一层。
7.3 分档要连着监控
分档不是切三刀就完事。每一档都要单独回算实际准确率,因为整体准确率是最钝的指标:高档掉五个点、低档涨五个点,整体可能一动不动,但业务侧的感受却翻了个面。
监控的最小粒度建议是「档 × 业务线」,先看哪一档先漂。漂移通常不是齐步走的,它从一个高分档、一条业务线开始,等整体指标动起来,偏差往往持续了两三周。
7.4 收束
回到标题那句话。模型说「不确定」的时候,它给的往往不是一个概率,而是一个分数;分数只有在被校准过之后,才有资格被叫作概率。
所以在 0.9 和「九成会对」之间,必须夹两道工序:一次校准(在验证集上拟参数、在留出集上验收),和一条曲线(覆盖率换错误率,选点由业务定)。少这两道,0.9 就只是一个好看的两位数,写进报表里,谁看都放心,用起来谁都不放心。
这份资料是什么:一份分档落地的模板,把「档位、动作、回算口径、复核周期」四列做成表,和本章的判据配套。放在资料包里,扫码即可获取:
附表 A:本文引用事实与出处对照表
两条来源均为公开论文,截至 2026-10-24 可按 arXiv 编号检索到;本文只引用摘要层面的结论,未展开论文中的实验设置与数据,也未对结论作改写。
| 编号 | 事实 | 出处 | 本文引用位置 |
|---|---|---|---|
| 1 | modern neural networks, unlike those from a decade ago, are poorly calibrated | Guo, Pleiss, Sun, Weinberger,On Calibration of Modern Neural Networks,ICML 2017,arXiv:1706.04599,摘要 | 第 1.2 节 |
| 2 | depth、width、weight decay、Batch Normalization 是影响校准的重要因素 | 同上,摘要 | 第 1.2、3.2 节 |
| 3 | temperature scaling 作为 Platt Scaling 的单参数变体,在校准预测上出奇有效 | 同上,摘要 | 第 1.2、6.1 节 |
| 4 | GPT-3 可以不用 logits、直接用自然语言表达置信度,且这些自述概率是校准的 | Lin, Hilton, Evans,Teaching Models to Express Their Uncertainty in Words,arXiv:2205.14334,2022,摘要 | 第 4.2 节 |
| 5 | 提出 CalibratedMath 测试集;比较 verbalized probability 与从 logits 提取不确定性 | 同上,摘要 | 第 4.2 节 |
| 6 | ECE 公式与 10 到 15 箱的分箱口径;表 2、表 5 的数值 | 随文脚本2026-10-24-10_校准实验.py在合成数据上运行所得(随机种子 7,4000 条),非任何真实模型数据;复跑python 2026-10-24-10_校准实验.py | 第 2.2、2.3、5.3 节 |
| 7 | 三种形态的成因归类、两条成本的选点方法、分档监控粒度 | 作者基于上述两篇与工程实践的经验判断,非论文原文结论 | 第 3.2、5.4、7.1、7.3 节 |
附表 B:术语速查表
| 术语 | 一句话解释 | 在本文哪里用到 |
|---|---|---|
| 校准 | 分数与实际频率对齐的程度,与准确率是两件事 | 第 1.3 节 |
| ECE | 期望校准误差,按分箱加权平均「置信度与准确率的差距」 | 第 2.2 节 |
| 可靠性图 | 横轴置信度、纵轴实际准确率的折线,45° 线为完美校准参照 | 第 3.1 节 |
| 温度缩放 | logits 除以 T 再 softmax,单参数,不改 argmax | 第 6.1 节 |
| 选择性预测 | 允许拒答,只在有把握的子集上作答 | 第 5.1 节 |
| 覆盖率 | 被作答的样本占总样本的比例 | 第 5.2 节 |
| 自述概率 | 模型用自然语言写出的置信百分比 | 第 4.2 节 |
| 采样一致性 | 同一问题多次采样后答案的分布集中度,测稳定性而非正确率 | 第 4.3 节 |
写在最后:这篇用到的资料
写这篇文章时,把两篇论文的摘要和几份工程笔记又翻了一遍,顺手也整理了几份配套的东西:
- 大模型学习路线图:从零基础到能自己动手做 Agent,按阶段说明每一步该学什么、哪些可以先跳过
- 《LangChain + LangGraph + MCP 智能体开发实战》视频课:7 个模块,从私有化部署、Embedding+RAG 到 MCP+Agent 全流程
- AI 大模型知识库(在线可查):Agent Skills 从入门到落地、Claude Skills 完全指南等专题,按目录浏览即可
- 640 套 AI 大模型行业报告 + 经典 PDF 书籍:看行业落地案例和别人怎么做的时候用得上
- 大模型零基础到精通教学视频:跟着敲一遍,比只读文档快得多
资料是我自己整理的,放在下面这个码上,扫码即可获取:
如果这篇对你有用,评论区说说你卡在哪一步,我按留言再补后续的拆解。