AI 工程基础体系 · 第 7/100 篇。内容覆盖机器学习、深度学习与生成式 AI;模型、数据、评测、权限和成本会作为同一生产系统处理。

机器学习评测:指标、交叉验证、阈值、校准、偏差和统计显著性

机器学习评测不是在测试集上打印一个分数,而是回答一组不同的问题:

  • 模型是否能正确排序或预测?
  • 这个分数是否对应真实概率?
  • 在什么阈值下把分数转成决策?
  • 换一批数据后,结论是否仍然成立?
  • 两个模型的差异是否超过随机波动?
  • 模型是否只在某些群体、时间段或任务类型上有效?
  • 在真实系统的延迟、成本、权限和失败约束下,模型是否值得上线?

因此,模型评测对象不应只包含模型权重。生产中的评测对象至少包括:

E=(D,P,M,τ,C,A,R)E=(D,\,P,\,M,\,\tau,\,C,\,A,\,R)

其中:

  • DD:数据集及其版本;
  • PP:数据预处理、特征和提示词流程;
  • MM:模型;
  • τ\tau:阈值、解码参数或决策策略;
  • CC:成本和延迟约束;
  • AA:权限、工具调用和资源访问;
  • RR:评测规则、参考答案、Judge 或人工评分流程。

只更换模型而保持其他变量不变,才有可能把指标变化归因于模型本身。


一、先区分四种“评测结果”

“模型更好”至少可能指四件不同的事:

  1. 预测性能更好:例如分类准确率、回归误差更优。
  2. 决策性能更好:在具体阈值、成本和风险下,错误更少或收益更高。
  3. 概率质量更好:模型说“有 80% 概率”时,长期是否真的约有 80% 的样本为正例。
  4. 生产结果更好:考虑延迟、成本、权限、工具失败和人工复核后,业务指标更优。

这四者不必一致。一个模型可能 AUC 更高,但在固定召回率下的精确率更低;可能准确率更高,但概率严重过度自信;可能离线分数更高,但每次请求成本太高,无法部署。

评测前应先写出决策目标。例如:

在每天 10 万条交易中,人工审核能力为 2%,希望召回至少 90% 的欺诈交易,同时降低误报。

这比“选择 F1 最高的模型”更具体,因为它明确了资源约束和主要错误。


二、分类指标:从混淆矩阵开始

设真实标签 y{0,1}y\in\{0,1\},模型先输出分数 s(x)s(x),再通过阈值 tt 得到预测:

y^t={1,s(x)t0,s(x)<t\hat y_t = \begin{cases} 1,&s(x)\ge t\\ 0,&s(x)<t \end{cases}

混淆矩阵包含四种结果:

真实值 预测为正 预测为负
正例 TP,真正例 FN,假负例
负例 FP,假正例 TN,真负例

所有常见分类指标都可以从这四个计数得到。

2.1 准确率、精确率、召回率和特异度

准确率(Accuracy)

Accuracy=TP+TNTP+TN+FP+FN\text{Accuracy}=\frac{TP+TN}{TP+TN+FP+FN}

它表示总体预测正确的比例。准确率隐含了一个危险假设:各类样本和各类错误的代价大致相同。

例如,1000 个样本中只有 10 个欺诈样本。一个“永远预测非欺诈”的模型有:

Accuracy=9901000=99%\text{Accuracy}=\frac{990}{1000}=99\%

但它的欺诈召回率为 0。准确率高并不表示模型有用。

精确率(Precision)

Precision=TPTP+FP\text{Precision}=\frac{TP}{TP+FP}

在模型判为正例的样本中,真正为正的比例。它回答“发出的告警有多少值得处理”。

召回率(Recall)或真正例率(TPR)

Recall=TPR=TPTP+FN\text{Recall}=\text{TPR}=\frac{TP}{TP+FN}

在全部正例中被识别出来的比例。它回答“真正的风险有多少被发现”。

特异度(Specificity)

Specificity=TNR=TNTN+FP\text{Specificity}=\text{TNR}=\frac{TN}{TN+FP}

在全部负例中被正确排除的比例。假正例率为:

FPR=1Specificity=FPFP+TN\text{FPR}=1-\text{Specificity}=\frac{FP}{FP+TN}

精确率和召回率的分母不同,因此不能互相替代。召回率高可能只是把几乎所有样本都判为正例;精确率高可能是阈值太严格,只报出极少数容易识别的正例。

2.2 F1、Fβ 与业务代价

F1 是精确率和召回率的调和平均:

F1=2PRP+RF_1=\frac{2PR}{P+R}

调和平均会强烈惩罚其中一个很低的情况。若精确率为 0.9、召回率为 0.1,则:

F1=2×0.9×0.10.9+0.1=0.18F_1=\frac{2\times0.9\times0.1}{0.9+0.1}=0.18

Fβ 用 β\beta 调整对召回率的重视程度:

Fβ=(1+β2)PRβ2P+RF_\beta=(1+\beta^2)\frac{PR}{\beta^2P+R}

β>1\beta>1 更重视召回率,β<1\beta<1 更重视精确率。

但是 F1 不是普适的业务目标。它没有直接表达:

  • 一个 FN 和一个 FP 是否代价相同;
  • 预测为正例是否消耗人工审核资源;
  • 模型分数是否具有概率含义;
  • 不同样本是否有不同损失。

如果错误成本已知,更直接的目标是期望成本:

E[C]=CFNP(FN)+CFPP(FP)+CABSTAINP(拒答)\mathbb E[C] = C_{FN}P(FN)+C_{FP}P(FP)+C_{ABSTAIN}P(\text{拒答})

其中 CFNC_{FN}CFPC_{FP} 分别是两类错误的成本。这样选择阈值时,不需要把所有问题压缩为 F1。


三、阈值:模型分数不是最终决策

分类器通常先输出一个连续分数,阈值只是把分数映射成行动。改变阈值不会改变样本的排序,但会改变 TP、FP、TN、FN 的数量。

设正例先验概率为 π=P(y=1)\pi=P(y=1),在阈值 tt 下:

P=πTPR(t)πTPR(t)+(1π)FPR(t)P=\frac{\pi\cdot TPR(t)} {\pi\cdot TPR(t)+(1-\pi)\cdot FPR(t)}

这个公式说明了一个重要事实:即使 TPR 和 FPR 不变,正例比例 π\pi 改变,精确率也会改变。这就是为什么欺诈检测、故障检测等稀有事件任务不能只看 ROC-AUC。

3.1 ROC 曲线与 PR 曲线

ROC 曲线绘制:

x=FPR,y=TPRx=FPR,\qquad y=TPR

ROC-AUC 可以理解为随机抽取一个正例和一个负例时,模型把正例排在负例前面的概率。它主要衡量排序能力,与某一个具体阈值无关。

PR 曲线绘制:

x=Recall,y=Precisionx=\text{Recall},\qquad y=\text{Precision}

在正例稀少时,PR 曲线通常更能反映告警质量。PR 曲线的基线约等于正例比例;如果正例率只有 0.1%,精确率 1% 虽然看起来很低,实际上可能比随机基线高十倍。

ROC-AUC 高而 PR 表现差并不矛盾。一个模型可以把正例整体排在负例前面,因此 AUC 高,但在真正需要的高精确率区域中仍然有大量假正例。

3.2 阈值的一个完整算例

假设验证集有 100 个正例、900 个负例。某模型在三个阈值下的结果如下:

阈值 TP FN FP TN
0.3 95 5 180 720
0.5 85 15 60 840
0.8 60 40 10 890

计算阈值 0.5 时:

Precision=8585+60=0.586\text{Precision}=\frac{85}{85+60}=0.586

Recall=85100=0.85\text{Recall}=\frac{85}{100}=0.85

Specificity=840900=0.933\text{Specificity}=\frac{840}{900}=0.933

如果一个 FN 的成本为 10,一个 FP 的成本为 1,则只计算错误成本:

  • 阈值 0.3:5×10+180×1=2305\times10+180\times1=230
  • 阈值 0.5:15×10+60×1=21015\times10+60\times1=210
  • 阈值 0.8:40×10+10×1=41040\times10+10\times1=410

在这个成本模型下,0.5 优于 0.3 和 0.8;但如果人工审核每个 FP 还需支付额外成本,最优阈值可能改变。

阈值必须在验证集上选择,最后只在测试集上评估。若反复查看测试集并调整阈值,测试集就参与了训练流程,最终分数会偏乐观。

3.3 Reject option 与多阈值系统

有些系统不应强迫所有样本自动决策。例如:

  • s<0.2s<0.2:自动通过;
  • s>0.8s>0.8:自动拦截;
  • 0.2s0.80.2\le s\le0.8:转人工。

这会增加人工成本,但可能降低高风险自动错误。此时评测应同时报告自动覆盖率、人工转交率、自动决策错误率和总体成本,而不是只报告一个分类指标。


四、校准:分数是否像概率

**校准(Calibration)**关注的是概率预测的可靠性,而不是排序能力。

若模型输出 p(x)=0.8p(x)=0.8,理想含义是:所有输出约为 0.8 的样本中,约 80% 最终为正例。形式化地说,对所有概率值 pp

P(Y=1P^=p)=pP(Y=1\mid \hat P=p)=p

实际模型输出通常是连续值,因此会把预测概率分箱。第 bb 个箱中的平均预测概率为:

confidenceb=1BbiBbp^i\text{confidence}_b=\frac{1}{|B_b|}\sum_{i\in B_b}\hat p_i

真实正例比例为:

accuracyb=1BbiBbyi\text{accuracy}_b=\frac{1}{|B_b|}\sum_{i\in B_b}y_i

绘制两者关系即可得到可靠性图(reliability diagram)。

4.1 区分排序、分类和概率质量

一个模型可能:

  • AUC 高,但概率过度自信;
  • AUC 与另一个模型相同,但校准更好;
  • 校准很好,但排序能力弱;
  • 阈值下 F1 高,却不能用于风险定价。

例如,模型 A 输出 [0.51, 0.52, 0.53],模型 B 输出 [0.01, 0.5, 0.99]。如果真实标签对应 [0,1,1],B 的排序可能更好,但其概率不一定更可靠。AUC 不会评价概率值本身。

常用的概率质量指标包括:

Brier score

Brier=1ni=1n(p^iyi)2\text{Brier}=\frac1n\sum_{i=1}^{n}(\hat p_i-y_i)^2

它是概率预测的均方误差,越低越好。对于二分类,Brier score 是严格适当评分规则:在真实概率为 qq 时,报告 p=qp=q 的期望损失最小。

Log loss 或交叉熵

LogLoss=1ni[yilogp^i+(1yi)log(1p^i)]\text{LogLoss} =-\frac1n\sum_i [y_i\log\hat p_i+(1-y_i)\log(1-\hat p_i)]

它对“自信但错误”的预测惩罚很大。预测 0.999 但真实为 0,损失远高于预测 0.6。

4.2 校准方法及边界

常见校准方法包括:

  • Platt scaling:在模型分数上拟合一个逻辑回归;
  • Isotonic regression:拟合单调但非参数的映射;
  • 温度缩放(temperature scaling):深度分类模型常用,在 logits 上除以温度后重新 softmax。

校准器必须只在独立的校准集或交叉验证的 out-of-fold 预测上拟合。若直接在训练预测上校准,模型已经“见过”这些标签,校准会过于乐观。

校准也可能随时间、地区、群体和数据分布变化。重新校准不一定能修复排序能力下降;如果数据发生概念漂移,单纯调整概率映射可能不够。

下面是一个可运行的 scikit-learn 示例:

import numpy as np
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.calibration import CalibratedClassifierCV, calibration_curve
from sklearn.metrics import brier_score_loss, log_loss, roc_auc_score

X, y = make_classification(
    n_samples=3000,
    n_features=10,
    weights=[0.8, 0.2],
    class_sep=1.0,
    random_state=42,
)

# 第一部分用于训练,第二部分完全不参与模型和校准器拟合
X_train, X_eval, y_train, y_eval = train_test_split(
    X, y, test_size=0.4, stratify=y, random_state=42
)

base = LogisticRegression(max_iter=1000)
base.fit(X_train, y_train)

# cv="prefit" 表示 base 已训练;校准器只使用 eval 数据。
# 生产中更推荐在交叉验证流程中生成 out-of-fold 预测,
# 避免把最终评估集同时用于校准和最终无偏评估。
calibrated = CalibratedClassifierCV(base, method="sigmoid", cv="prefit")
calibrated.fit(X_eval, y_eval)

p_base = base.predict_proba(X_eval)[:, 1]
p_cal = calibrated.predict_proba(X_eval)[:, 1]

print("base AUC:", roc_auc_score(y_eval, p_base))
print("calibrated AUC:", roc_auc_score(y_eval, p_cal))
print("base Brier:", brier_score_loss(y_eval, p_base))
print("calibrated Brier:", brier_score_loss(y_eval, p_cal))
print("calibrated LogLoss:", log_loss(y_eval, p_cal))

prob_true, prob_pred = calibration_curve(
    y_eval, p_cal, n_bins=10, strategy="quantile"
)
print("observed:", np.round(prob_true, 3))
print("predicted:", np.round(prob_pred, 3))

这里校准通常不会改变样本排序,因此 AUC 可能几乎不变;它主要改善 Brier、LogLoss 和可靠性图。示例为了展示 API,把 X_eval 用作校准数据,因此它不再是严格的最终测试集。严格实验应拆成训练集、校准集和最终测试集,或使用嵌套交叉验证。


五、交叉验证:估计泛化,而不是制造更多数据

泛化误差是模型在未来同分布样本上的期望损失:

R(f)=E(X,Y)P[(f(X),Y)]R(f)=\mathbb E_{(X,Y)\sim P}[\ell(f(X),Y)]

测试集提供一个近似估计,但单次切分受样本组成影响。交叉验证(Cross-Validation,CV)通过多次训练和验证,估计模型在不同训练子集和验证子集上的表现。

5.1 K 折交叉验证的过程

将数据划分为 KK 个互不重叠的折 D1,,DKD_1,\ldots,D_K。第 kk 次:

  • DDkD\setminus D_k 训练;
  • DkD_k 评估;
  • 得到分数 mkm_k

平均分数:

mˉ=1Kk=1Kmk\bar m=\frac1K\sum_{k=1}^{K}m_k

样本标准差:

s=1K1k=1K(mkmˉ)2s=\sqrt{\frac1{K-1}\sum_{k=1}^{K}(m_k-\bar m)^2}

标准误不能机械地认为是 s/Ks/\sqrt K,因为各折训练集高度重叠,折间结果并不独立。它可以作为粗略的波动描述,但不能直接当成严格独立重复实验的误差。

5.2 分层、分组和时间序列

普通 K 折随机切分要求样本近似独立同分布(i.i.d.)。真实数据常常不满足:

  • 同一个用户有多条记录;
  • 同一患者有多个检查;
  • 同一设备连续产生时间序列;
  • 同一文档的不同切片出现在多个集合;
  • 未来信息不能用于预测过去。

相应地应使用:

  • StratifiedKFold:尽量保持每折类别比例;
  • GroupKFold:同一组必须完整地位于某一折;
  • TimeSeriesSplit:按时间顺序训练过去、验证未来;
  • 按实体、时间、项目或文档做显式切分。

随机切分用户行为数据的典型反例是:用户历史行为同时出现在训练和验证中,模型可能记住用户身份,而不是学习可泛化规律。验证分数很高,但新用户上线后性能骤降。

5.3 预处理必须在每个训练折内拟合

标准化、缺失值填充、特征选择、目标编码、过采样和降维都可能使用数据统计量。正确流程是:

训练折拟合预处理验证折变换\text{训练折}\xrightarrow{\text{拟合预处理}}\text{验证折变换}

不能先对全量数据计算均值、词表或目标编码,再切分数据。即使没有直接使用验证标签,也可能把验证分布信息泄漏到训练中。

使用 Pipeline 可以把预处理和模型绑定:

from sklearn.datasets import load_breast_cancer
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold, cross_validate
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

X, y = load_breast_cancer(return_X_y=True)

pipe = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
    ("model", LogisticRegression(max_iter=2000)),
])

cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=7)
result = cross_validate(
    pipe,
    X,
    y,
    cv=cv,
    scoring=["accuracy", "roc_auc"],
    return_train_score=False,
)

print("accuracy:", result["test_accuracy"].mean())
print("accuracy std:", result["test_accuracy"].std(ddof=1))
print("roc_auc:", result["test_roc_auc"].mean())

Pipeline 的关键不是语法便利,而是保证每个折分别调用 fit。如果先对全量数据 fit_transform,再把结果交给交叉验证,就破坏了评测边界。

5.4 调参、模型选择与嵌套交叉验证

如果用交叉验证结果选择超参数,再用同一结果报告性能,会产生选择偏差。因为你选择的是“在这组验证结果上看起来最好的配置”,这个最好分数通常高于其真实泛化性能。

嵌套交叉验证把流程分成两层:

  1. 外层 CV:估计最终泛化性能;
  2. 内层 CV:只在外层训练集内部选择超参数。

对每个外层折:

Douter-train内层调参在外层验证集评估D_{\text{outer-train}} \rightarrow \text{内层调参} \rightarrow \text{在外层验证集评估}

外层验证集从未参与超参数选择,因此更接近无偏估计。若最终还需要训练生产模型,完成评估后可以用所有可用训练数据重新拟合选定配置,但必须保留独立测试集用于最终确认。


六、数据泄漏:比模型算法更常见的评测错误

**数据泄漏(Data Leakage)**是训练过程获得了在真实预测时不可获得的信息。它不一定表现为显式使用测试标签。

常见类型包括:

6.1 标签泄漏

特征直接或间接由标签生成。例如预测住院期间是否死亡,却使用“出院诊断”作为特征;预测贷款违约,却使用未来催收结果。

6.2 时间泄漏

用未来数据预测过去。例如随机切分交易记录,训练集包含 2024 年 3 月,验证集包含 2024 年 2 月;模型通过时间相关特征间接看到未来分布。

6.3 实体泄漏

同一个用户、患者、设备或文档同时出现在训练与验证中。深度模型尤其容易通过近重复内容获得虚假的高分。

6.4 预处理泄漏

在切分前拟合标准化、词表、特征选择或目标编码。目标编码尤其危险,因为它直接使用标签统计。

诊断泄漏的方法包括:

  • 按时间重新评估;
  • 按用户、设备或文档重新分组切分;
  • 删除可疑特征后比较分数;
  • 检查训练与验证的重复、近重复和模板;
  • 做“标签打乱测试”:若随机标签后分数仍异常高,流程可能存在泄漏;
  • 比较随机切分和真实部署切分的差异。

七、偏差:统计估计偏差、模型偏差和群体差异不是一回事

“偏差”有多个含义,混用会导致错误结论。

7.1 统计学中的偏差

估计量 θ^\hat\theta 的偏差定义为:

Bias(θ^)=E[θ^]θ\text{Bias}(\hat\theta) =E[\hat\theta]-\theta

例如,样本方差使用分母 nn

σ^2=1ni=1n(xixˉ)2\hat\sigma^2=\frac1n\sum_{i=1}^{n}(x_i-\bar x)^2

它对总体方差有向下偏差;使用 n1n-1 可以得到无偏估计。在模型评测中,如果反复根据验证集选择配置再报告同一个验证集分数,报告的分数也会产生选择偏差。

7.2 偏差—方差分解

对平方损失,固定输入 xx 时,期望预测误差可分解为:

E[(Yf^(x))2]=(E[f^(x)]f(x))2Bias2+E[(f^(x)E[f^(x)])2]Variance+σ2不可约噪声E[(Y-\hat f(x))^2] = \underbrace{(E[\hat f(x)]-f(x))^2}_{\text{Bias}^2} + \underbrace{E[(\hat f(x)-E[\hat f(x)])^2]}_{\text{Variance}} + \underbrace{\sigma^2}_{\text{不可约噪声}}

其中:

  • f(x)=E[YX=x]f(x)=E[Y\mid X=x] 是真实回归函数;
  • Bias² 表示模型平均预测偏离真实函数;
  • Variance 表示训练数据变化导致预测波动;
  • σ2\sigma^2 是标签噪声。

简单模型往往偏差高、方差低;复杂模型往往偏差低、方差高。正则化、更多数据、数据增强和合理的模型容量可能降低方差,但不能保证降低所有类型的偏差。

7.3 数据、标签和分布造成的偏差

工程中还常说:

  • 采样偏差:训练样本不能代表部署人群;
  • 测量偏差:不同群体的特征质量不同;
  • 标签偏差:标签来自人工决策,而人工决策本身不公平;
  • 选择偏差:只有被审核或被治疗的人有标签;
  • 分布偏移:训练分布 Ptrain(X,Y)P_{\text{train}}(X,Y) 与部署分布不同。

要先明确偏差相对于哪个目标。总体准确率下降不一定代表公平性改善;某群体的召回率更低,也不自动说明模型存在可归责的算法缺陷,可能是数据量、标签质量或条件分布不同导致。

7.4 群体评测与不可同时满足的约束

对群体 gg,可以分别计算:

TPRg=TPgTPg+FNg,FPRg=FPgFPg+TNgTPR_g=\frac{TP_g}{TP_g+FN_g},\quad FPR_g=\frac{FP_g}{FP_g+TN_g}

也可以观察精确率、校准误差和拒答率。

当不同群体的基础率 P(Y=1G=g)P(Y=1\mid G=g) 不同时,通常无法同时满足:

  • 完全校准;
  • 相同 TPR;
  • 相同 FPR;
  • 相同精确率。

这不是实现细节,而是概率条件之间的约束。若模型在各群体内都严格校准,且不同群体基础率不同,则相同阈值下的精确率通常不能完全相同。讨论公平性时必须明确目标、决策阈值、法律和业务含义,不能只挑一个最有利的指标。


八、统计显著性:差异是否超过随机波动

设模型 A 和 B 在同一批样本上的指标分别为 m^A\hat m_Am^B\hat m_B。观察到:

Δ=m^Bm^A\Delta=\hat m_B-\hat m_A

并不意味着 B 真实更好。有限样本会产生随机波动,需要估计差异的不确定性。

8.1 置信区间和显著性

置信区间描述估计量的抽样不确定性。常见的 95% 置信区间不是“参数有 95% 概率在这个区间内”,而是指如果重复抽样并用同样方法构造区间,长期约 95% 的区间覆盖真实参数。

显著性检验通常设:

H0:Δ=0H_0:\Delta=0

若 p 值小于预设显著性水平 α\alpha,则在该检验假设和模型下拒绝 H0H_0。p 值不是“B 优于 A 的概率”,也不是效果大小。样本量很大时,极小的业务差异也可能显著;样本量很小时,有意义的差异也可能不显著。

8.2 配对比较优于独立比较

当两个模型在相同样本上预测时,结果是配对的。应优先利用配对结构,而不是把两组指标当作独立样本。

对逐样本损失:

di=B(xi,yi)A(xi,yi)d_i=\ell_B(x_i,y_i)-\ell_A(x_i,y_i)

平均差异为:

dˉ=1nidi\bar d=\frac1n\sum_i d_i

如果样本独立,可以对 did_i 做配对 t 检验或 bootstrap。分类准确率还可以使用 McNemar 检验,比较:

  • A 正确、B 错误的样本数 n01n_{01}
  • A 错误、B 正确的样本数 n10n_{10}

它关注的是两模型预测不一致的样本,而不是把所有预测当作独立观测。

对于 AUC,可使用适合相关 ROC 曲线的配对检验。对于 F1、PR-AUC 等复杂指标,bootstrap 通常更直观。

8.3 配对 bootstrap 示例

import numpy as np

rng = np.random.default_rng(42)

# 每一行是一个样本;loss 越低越好
loss_a = np.array([0, 1, 0, 0, 1, 0, 1, 0, 0, 1])
loss_b = np.array([0, 1, 1, 0, 1, 0, 0, 0, 1, 1])

d = loss_b - loss_a  # B - A;负值表示 B 损失更低
n = len(d)
boot = np.empty(10000)

for j in range(len(boot)):
    index = rng.integers(0, n, size=n)
    boot[j] = d[index].mean()

delta = d.mean()
ci_low, ci_high = np.quantile(boot, [0.025, 0.975])

print("observed difference (B - A):", delta)
print("95% bootstrap interval:", (ci_low, ci_high))

如果区间整体低于 0,说明在这个 bootstrap 估计下 B 的损失很可能低于 A。它仍然依赖样本独立性。若样本来自用户、会话、文档或时间序列,应按用户、会话、文档或时间块进行聚类 bootstrap,而不是逐行随机抽样。

8.4 交叉验证分数不能简单做普通 t 检验

K 折中的训练集重叠,使各折分数相关。直接把五个折当作五个独立实验,会低估不确定性。更可靠的做法包括:

  • 使用多个独立随机重复和独立测试集;
  • 对最终测试集做配对 bootstrap;
  • 对用户、会话或时间块进行聚类重采样;
  • 在样本量和计算允许时使用重复嵌套交叉验证;
  • 同时报告每折、重复实验和测试集差异,而不是只报均值。

8.5 多重比较与实际意义

当同时比较几十个模型、几十个切片和几十个指标时,偶然出现“显著”结果的概率会上升。应预先定义主要指标,或使用 Bonferroni、Holm、Benjamini–Hochberg 等多重比较控制方法。

统计显著性之外还要报告:

  • 绝对差异和相对差异;
  • 置信区间;
  • 业务阈值;
  • 成本、延迟和资源占用;
  • 是否影响关键群体或高风险案例。

“提升 0.05 个百分点且 p<0.05”未必值得换模型。


九、回归评测:误差定义决定模型偏好

回归模型输出连续值 y^\hat y。常见指标包括:

MAE

MAE=1niyiy^i\text{MAE}=\frac1n\sum_i|y_i-\hat y_i|

它表示平均绝对误差,对异常值相对稳健,直观地使用原始单位。

MSE

MSE=1ni(yiy^i)2\text{MSE}=\frac1n\sum_i(y_i-\hat y_i)^2

大误差会被平方放大,因此适合大误差代价明显的场景。

RMSE

RMSE=MSE\text{RMSE}=\sqrt{\text{MSE}}

与目标变量同单位,但仍然保留平方损失对大误差的惩罚。

MAPE

MAPE=100%niyiy^iyi\text{MAPE}=\frac{100\%}{n} \sum_i\left|\frac{y_i-\hat y_i}{y_i}\right|

yiy_i 接近 0 时会爆炸,对零值不能直接使用。它还会不对称地惩罚低估和高估。

如果业务更关心分位数预测,可使用 pinball loss。对分位数 τ(0,1)\tau\in(0,1)

Lτ(y,y^)={τ(yy^),yy^(1τ)(y^y),y<y^L_\tau(y,\hat y)= \begin{cases} \tau(y-\hat y),&y\ge\hat y\\ (1-\tau)(\hat y-y),&y<\hat y \end{cases}

它允许模型输出预测区间或不同风险水平,而不是只输出条件均值。


十、深度学习与生成式 AI:指标不能脱离任务协议

深度学习模型的输出可能是分类概率、序列、图像、向量或生成文本。评测首先要固定:

  • 输入格式和预处理;
  • 模型版本与随机种子;
  • 解码策略,如 temperature、top-p、beam search;
  • 最大输出长度;
  • 工具、检索库和系统提示词;
  • 超时、重试和失败计费规则。

否则一次实验比较的可能不是模型,而是整个运行配置。

10.1 生成任务的参考答案指标

机器翻译、摘要和代码生成中,BLEU、ROUGE、Exact Match 等指标可以提供自动化信号,但它们只度量某种表面或参考答案相似性。

例如,两个正确答案可能使用完全不同的措辞,ROUGE 较低;一个答案复制了参考文本片段,ROUGE 较高,但事实可能错误。代码任务中,运行测试比字符串相似度更接近功能正确性。

应根据任务设计可验证指标:

  • 数值问题:最终数值与容差;
  • SQL:在固定数据库上执行结果;
  • 代码:单元测试、静态检查、资源限制;
  • 检索问答:引用是否存在、答案是否被文档支持;
  • 结构化输出:JSON Schema、字段约束和枚举合法性;
  • 安全任务:攻击集上的拒答和越权率。

10.2 LLM Judge 的统计和偏差

LLM Judge 是另一个模型根据评分标准评价输出。它可以降低人工成本,但不是客观真值。Judge 可能存在:

  • 偏好更长或更像自己的答案;
  • 受位置、格式和措辞影响;
  • 对某种语言或风格有偏见;
  • 在困难样本上不稳定;
  • 被候选答案中的指令注入;
  • 与人工专家标准不一致。

因此应保存每条样本的:

(input,reference,candidate,judge prompt,judge version,score,reason)(\text{input},\text{reference},\text{candidate},\text{judge prompt},\text{judge version},\text{score},\text{reason})

不能只保存平均分。应在人工标注子集上测 Judge 与人工的一致性,并对模型 A/B 使用成对比较、随机交换答案顺序和盲评,减少位置偏差。

10.3 Agent 评测的是轨迹而不只是最终文本

Agent 的一次任务通常包含:

输入规划工具调用观察后续行动最终答案\text{输入} \rightarrow \text{规划} \rightarrow \text{工具调用} \rightarrow \text{观察} \rightarrow \text{后续行动} \rightarrow \text{最终答案}

评测应同时检查:

  • 最终任务是否完成;
  • 工具参数是否正确;
  • 是否调用了不必要或危险的工具;
  • 是否违反权限;
  • 是否在失败后正确重试或停止;
  • 是否泄露敏感信息;
  • 总 token、工具调用次数、延迟和金钱成本。

一个最终答案正确但绕过权限的 Agent 不能算成功。一次人工审核后完成的任务,也不应与全自动完成的任务使用同一个“成功率”解释。


十一、离线评测与生产评测的连接

离线评测通常拥有固定标签和可重复环境,适合模型开发;生产评测面对真实流量、分布变化和系统故障。两者之间必须明确映射。

可以将一次生产请求的结果记录为:

request_id
dataset_version
model_version
prompt_or_feature_version
policy_version
permission_context
input_hash
output_hash
decision
human_feedback
latency_ms
input_tokens
output_tokens
tool_calls
cost
error_type

其中 permission_context 很重要:同一个 Agent 在不同权限下可观察到的数据不同,不能把权限扩大后的结果与普通用户结果直接比较。

成本可以写成:

Ctotal=Cinference+Cretrieval+Ctool+Chuman+CfailureC_{\text{total}} = C_{\text{inference}} +C_{\text{retrieval}} +C_{\text{tool}} +C_{\text{human}} +C_{\text{failure}}

评测一个模型时,至少要观察质量—成本前沿,而不是只选质量最高者。若模型 B 的成功率略高,但 token、工具调用和人工复核成本大幅增加,则是否采用取决于业务价值和风险预算。


十二、常见失败表现与诊断顺序

12.1 准确率很高但上线无效

优先检查类别不平衡、正例率变化和混淆矩阵。再看 PR 曲线、固定召回率下的精确率,以及真实业务阈值下的成本。

12.2 交叉验证很高,线上骤降

优先怀疑泄漏、错误切分和分布偏移。按用户、时间、文档或设备重切分,通常比继续调参更有信息量。

12.3 AUC 提升但业务指标下降

可能是阈值仍沿用旧模型,或者新增排序能力集中在业务不关心的区域。重新在验证集上选择阈值,并报告目标工作点,而不是只比较 AUC。

12.4 概率看起来很自信但经常错

检查可靠性图、Brier score 和 LogLoss。对深度模型尤其要检查过度自信,并使用独立校准集。校准不能替代错误分析:如果高置信错误集中在某个群体,仍需追查数据和模型机制。

12.5 新模型平均分更高但用户体验更差

按任务类型、长度、语言、用户群体、风险级别和失败类型切片。平均值可能掩盖关键长尾。对生成模型还应比较拒答率、幻觉率、工具失败率和成本。

12.6 “统计显著”但没有实际价值

查看效应大小及其区间,换算为每天减少多少错误、节省多少成本或增加多少人工工作。显著性检验回答“是否可能不是随机波动”,不回答“是否值得上线”。


十三、一个可复用的评测协议

一个严谨的评测协议可以按以下因果顺序建立:

  1. 定义部署任务:输入、输出、决策、允许延迟、成本、权限和风险。
  2. 定义主要指标:区分排序、分类、概率、生成质量和系统指标。
  3. 定义切分单位:样本、用户、会话、文档、时间段或项目。
  4. 固定版本:数据、标签、特征、提示词、模型、解码、工具和 Judge。
  5. 建立训练—验证—校准—测试边界:任何拟合行为都不能接触最终测试集。
  6. 选择阈值或决策策略:只在验证数据中优化,明确错误成本。
  7. 报告分层结果:总体、关键群体、时间段、任务类型和失败模式。
  8. 进行配对比较:在同一批样本上运行模型 A 和 B,保留逐样本结果。
  9. 估计不确定性:使用合适的 bootstrap、配对检验或重复实验。
  10. 验证生产约束:延迟、成本、并发、权限、工具错误和回退策略。
  11. 冻结测试结论:测试集只用于最终确认,不能反复用于调参。
  12. 上线后监控漂移:监控输入分布、标签延迟、校准、阈值工作点、切片指标和成本。

一个完整的模型报告不应只写:

AUC = 0.93

而应至少包含:

数据版本:v17
切分:按用户分组、按时间保留最后两周测试
主要目标:召回率 >= 0.90 时最大化精确率
阈值:0.63,在验证集选择
测试结果:Recall、Precision、PR-AUC、Brier、95% 配对区间
切片:地区、设备、用户新旧程度
系统结果:P95 延迟、单请求成本、超时率、人工转交率
限制:标签延迟 14 天,测试集不代表节假日流量

这样的结果才能支持工程决策。


十四、最后的判断原则

指标是目标的数学投影,不是目标本身。交叉验证估计的是某种切分假设下的泛化能力;如果切分假设与部署过程不一致,交叉验证可以非常精确地估计错误对象。阈值决定分数如何变成行动;校准决定概率是否可信;偏差分析决定平均性能是否掩盖了系统性问题;统计显著性决定观察到的差异是否足以排除部分随机波动。

在机器学习、深度学习和生成式 AI 系统中,可靠评测应同时回答三层问题:

模型是否有效决策是否合适系统是否值得运行\text{模型是否有效} \quad\rightarrow\quad \text{决策是否合适} \quad\rightarrow\quad \text{系统是否值得运行}

只有当数据边界、指标含义、阈值策略、概率可靠性、群体表现、不确定性、权限和成本都被纳入同一评测协议时,离线分数才真正具有生产意义。


系列导航与关联阅读

官方资料

本文依据研究论文、标准组织与主流框架官方文档重新梳理;正文、示例与工程清单由 WR BLOG 编写。