困惑度与校准:模型说自己信几分 本节摘要:如果你的模型对一千个答案报「90% 有把握」却只答对六百个,它就没校准好。校准是可信评估的一半,另一半是困惑度——告诉你模型觉得留出文本到底像不像话。本节从模型适配器供给的 token 负对数似然算困惑度(perplexity),从分箱预测概率算期望校准误差(ECE)与Brier 分数,并产出可靠性图数据。三者接进评估框架,让运行器能给模型报告附上 、 、 。读完本节,你能说清为什么 ECE 只看平均间隙而 Brier 直接惩罚离散,以及为什么「精度赢、Brier 输」的模型是比「精度低几分局但老实报不确定」更差的生产部署。 对应原课程:Phase 19 · Lesson 73 · (原英文 )。本节属第 20 章「毕业项目」的评估赛道。
本节摘要:如果你的模型对一千个答案报「90% 有把握」却只答对六百个,它就没校准好。校准是可信评估的一半,另一半是困惑度——告诉你模型觉得留出文本到底像不像话。本节从模型适配器供给的 token 负对数似然算困惑度(perplexity),从分箱预测概率算期望校准误差(ECE)与Brier 分数,并产出可靠性图数据。三者接进评估框架,让运行器能给模型报告附上
perplexity、ece、brier。读完本节,你能说清为什么 ECE 只看平均间隙而 Brier 直接惩罚离散,以及为什么「精度赢、Brier 输」的模型是比「精度低几分局但老实报不确定」更差的生产部署。
对应原课程:Phase 19 · Lesson 73 ·
perplexity-calibration(原英文phases/19-capstone-projects/73-perplexity-calibration/docs/en.md)。本节属第 20 章「毕业项目」的评估赛道。
阅读完本节,你应当能够:
perplexity、ece、brier。困惑度是每 token 负对数似然平均的指数化。越低越好:困惑度 1 表示模型对每个真实 token 赋概率 1;困惑度等于词表大小表示模型均匀、啥也没学。真实数字在中间:2026 年强基座模型在 WikiText-103 上约 8~12,差的在同一文本上 50+。
框架自己不算对数概率,那来自模型适配器。框架只聚合:收一份每 token 对数概率列表、一份每序列 token 计数列表,返回语料困惑度。
def perplexity(neg_log_probs, token_counts): assert all(x >= 0 for x in neg_log_probs), "负对数似然必须非负" total_nll = sum(neg_log_probs) total_tokens = sum(token_counts) if total_tokens == 0: return float("nan") return math.exp(total_nll / total_tokens)
实现处理零 token 边界并断言负对数似然非负。常见错误是忘取负:返回 log p 而非 -log p 的适配器会产出低于 1 的困惑度,这是不可能的,函数把它当契约违反抓出来。
期望校准误差把预测按置信度分进固定数量的桶,再测各桶置信度与准确率的平均间隙,按桶大小加权。
标准用 [0,1] 上 10 个等宽桶,实现支持任意正整数,暴露 bins 参数让运行器在发表约定(10)与对比约定(15)间选。ECE 受桶数与样本量偏置:10 桶 100 预测时分不清 0.02 ECE 与随机噪声。实现返回已填充桶数连同 ECE,让运行器在样本太少时拒绝报单数。
ECE 只关心平均间隙。一个在半数桶过自信、另半数欠自信的模型可 ECE 低却局部校准差。Brier 分数测每预测对真实结果的平方误差,直接惩罚离散。二值结果下 Brier 是 mean((p_i - y_i)^2),可分解为可靠性、分辨率、不确定性;实现算分数与分解,运行器报标量但把分解记给仪表盘。
def brier(p, y): return float(np.mean((p - y) ** 2))
可靠性图画每桶预测置信度对经验准确率,对角线是完美校准。函数返回三数组:每桶平均置信度、每桶平均准确率、每桶计数。画图代码在下游,本节停在数据形状。
返回的元组是调用层画图或算自定义 ECE 变体(自适应 ECE、扫描 ECE)所需的;返回 numpy 数组省去下游转换。
框架不假设置信度来自 softmax,接受每预测任一 [0,1] 数。多选任务的天然置信度是「选项对数似然上的 softmax」;自由文本的天然置信度是模型自报概率或平均对数似然的指数。评估只消费这个数,它从哪来是适配器的活。
0.0(或零填充数组);困惑度对零 token 返回 NaN。这些路径不发警告,运行器检视值决定报或跳。这些情况烤进测试。真实模型在真实基准不会命中,但有 bug 的适配器或小样本会,运行器不该崩。
校准不像 F1 是每任务指标,而是每模型报告。运行器跨整个评估累积 (置信度, 正确) 对,一次性算 ECE、Brier、可靠性数据。困惑度在留出文本语料上算,独立于逐任务打分。接口:
report = CalibrationReport.from_predictions(confidences, correct) report.ece # float report.brier # float report.reliability # 三 numpy 数组的元组 report.populated_bins # int PerplexityResult.from_token_nll(neg_log_probs, token_counts) # 返回困惑度与每 token 平均负对数似然
| 指标 | 度量什么 | 何时用 | 局限 |
|---|---|---|---|
| 困惑度 | 留出文本的负对数似然指数化 | 基座模型质量 | 词表/分词敏感,不可跨模型比 |
| ECE | 分箱置信-准确平均间隙 | 报告整体校准 | 只看平均,局部偏差可抵消 |
| Brier | 对结果的平方误差 | 直接惩罚离散 | 对类不平衡敏感 |
| 可靠性图 | 每桶置信-准确 | 诊断局部校准 | 仅诊断,非单数 |
业界对比:HELM 报校准,ECE 是其标配;lm-eval-harness 算困惑度;OpenCompass 报 Brier。它们的共识与本节一致:校准是最被忽视的评估轴——大多数排行榜只报一个准确率就完事,「精度赢、Brier 输」的模型是更差的生产部署。
CalibrationReport 与 PerplexityResult:本节产出是 75 节运行器附到模型报告的可信数字;populated_bins 让运行器在样本太少时拒绝报单数。from_predictions / from_token_nll 让任何适配器只要供给 (置信度, 正确) 或 (负对数似然, token 计数) 就能接入。main.py 定义 perplexity、expected_calibration_error、brier_score、reliability_diagram 与 CalibrationReport/PerplexityResult dataclass;demo 在已知真值的合成预测上跑(良好校准、过自信、欠自信三种);code/tests/test_calibration.py 钉死每个边界加合成预测器的参考值。函数排序从标量到向量到报告,每个函数带短 docstring 写清数学与契约。
本节不调模型、不实现 softmax、不从输出 token 估置信度(那是适配器的活)、不做温度缩放或 Platt 缩放(那些是后验修整,属另一课)。要点是让三个数(困惑度、ECE、Brier)可信且可复现。
💡 校准管道就位后,在留出验证片上加温度缩放、重算 ECE、看间隙收缩——那是另一课,但地基在这里。
[0,1] 数,不假设 softmax。下一节,我们将进入「排行榜聚合」——把 71~73 节的指标数字聚合成跨模型可比的排行榜,含 pass-at-k 与置信区间。