跳到内容

8.2 切分、交叉验证与不确定性:验证结构必须模拟部署结构

模型工坊把同一台设备的日志随机分进五折,得到几乎完美的结果。情报官换成新设备测试,性能骤降。问题不是折数太少,而是训练折已经见过每台验证设备的“指纹”。

交叉验证只有在切分单位与部署边界一致时才有意义。随机 K 折、分层 K 折、GroupKFold 和时间回放估计的是不同泛化任务。

本课目标

  • 从部署总体选择 random、group 或 temporal split;
  • 把所有 learned preprocessing 放入 fold 内;
  • 区分模型选择、性能估计与最终 test;
  • 正确理解 CV 均值、折间标准差和置信区间;
  • 使用 nested CV 与 paired comparison 控制选择偏差。

1. 切分是在定义“新数据”

几个常见部署问题:

部署问题验证中应隔离什么
同分布新独立样本随机行(iid 假设近似成立)
新用户/新设备/新医院整个 group
已有用户的未来行为时间,可能允许历史用户出现
新用户的未来行为group 与 time 都要隔离
新地区/新站点location/domain

先写部署问题,再选 splitter。不能因为 cv=5 方便,就反过来声称模型能泛化到新设备。

2. Train、Validation、Test 职责不同

  • train:拟合模型参数与 fold 内前处理;
  • validation/inner CV:选择特征、模型、超参数、校准与阈值;
  • test/outer fold:评估已冻结的整个选择流程。

只要看过 test 结果后改变任何选择,test 就参与了开发。一次性 test 的价值来自流程约束,不来自文件名。

对持续迭代产品,可按时间维护滚动 backtest 与最终 shadow/champion–challenger 评估;不要长期反复“保留”一份已被全团队记住结果的 test。

3. Stratification 是工程措施,不恢复 iid

StratifiedKFold 尽量保持每折类别比例,能避免稀有类在某折完全消失并降低某些指标波动。但它不会解决:

  • 同用户跨折泄漏;
  • 时间穿越;
  • 空间/机构相关性;
  • 重复样本;
  • 标签窗口重叠。

有 group 且希望近似保持类别比例,可考虑 StratifiedGroupKFold,但 group 不重叠优先于完美比例;组数、大小和类分布可能使理想分层根本不可行。

4. Group split 的统计单位是 Group

同一病人多次就诊、同一设备多条日志、同一文档多个片段通常相关。若目标是新 group 泛化,所有同 group 行必须在同一折。

评估与区间估计也应尊重 group:一万个日志来自十台设备,并不等于一万个独立样本。可同时报告:

  • micro/row-weighted 指标;
  • macro/group-equal 指标;
  • group 分布与最差/低分位表现;
  • leave-one-site/group-out sensitivity。

选择哪种加权取决于部署总体:未来流量按行发生,还是每家机构等权。

5. 时间切分要处理标签成熟与 Gap

时间模型不能用未来训练过去。还要考虑 feature window 与 label horizon:

text
feature window ---- cutoff ---- label horizon
train labels mature | gap | validation features/labels

若预测“未来 30 天故障”,训练末尾样本的标签要等 30 天才成熟。Train 与 validation 间可能需要 embargo/gap,防止重叠窗口或延迟标签穿越。

TimeSeriesSplit 提供 expanding-window 和 gap 等机制,但默认假定行已按时间排序,且其等间隔说明关系到折间指标可比性。真实系统还可能需要 rolling window、固定测试时长、季节覆盖和 group-time 双重约束。

6. 所有 Learned Transform 都在 Fold 内 Fit

泄漏不只来自 target encoding。以下步骤只要从数据估计参数,就属于训练过程:

  • 缺失填补、缩放、winsorization;
  • vocabulary、one-hot category set;
  • PCA、feature selection;
  • target/frequency encoding;
  • resampling/SMOTE;
  • calibration 和 threshold tuning;
  • anomaly/cluster representation。

使用 Pipeline/ColumnTransformer 把它们放入 CV。即使 transform 不看 y,在全数据拟合也会让 validation distribution 影响 representation。

7. 一个 Group-aware 多指标评估骨架

python
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedGroupKFold, cross_validate
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

candidate = Pipeline([
    ("impute", SimpleImputer(strategy="median")),
    ("scale", StandardScaler()),
    ("model", LogisticRegression(max_iter=2_000)),
])

cv = StratifiedGroupKFold(
    n_splits=5,
    shuffle=True,
    random_state=42,
)

result = cross_validate(
    candidate,
    X,
    y,
    groups=device_id,
    cv=cv,
    scoring={
        "roc_auc": "roc_auc",
        "average_precision": "average_precision",
        "neg_log_loss": "neg_log_loss",
    },
    return_train_score=True,
    return_estimator=True,
    n_jobs=-1,
)

当前 sklearn 是否启用 metadata routing 会影响 groups 的传法;项目应固定版本并按官方 API 调整。更重要的是先验证每折 group 交集为空,而不是只相信 splitter 名称。

8. 折间标准差不是置信区间

K 个 CV 分数不是 K 个独立同分布实验:训练集高度重叠,验证集也来自同一有限数据。mean ± std 描述这组 split 的离散程度,不能直接当总体性能的 95% confidence interval。

不确定性来源至少包括:

  • 有限测试样本;
  • 数据切分;
  • 模型初始化/随机训练;
  • 超参数搜索选择;
  • 标签误差与延迟;
  • 未来分布漂移。

报告一个小数点后三位的均值,不会消除这些来源。

9. Bootstrap 必须重采正确单位

对冻结模型的独立 test,可 bootstrap test units 估计 metric sampling uncertainty。若同一用户有多行,应 cluster bootstrap 用户,而不是独立抽行;时间序列可用 block bootstrap,但 block 长度本身是建模选择。

Rare event 指标可能出现 bootstrap sample 没有正例、分布高度偏斜。应报告有效 replicate、interval 方法、抽样单位,并检查 percentile/BCa 等方法的适用性。

Bootstrap interval 只覆盖指定抽样机制下的有限样本不确定性,不覆盖未来 drift、隐藏泄漏和不断试模型造成的选择偏差。

10. 比较模型要使用 Paired Evidence

两个模型在同一 test 样本上的误差相关。比较差值:

$$ \Delta=M_A-M_B, $$

并对同一行/group 的 paired predictions 重采样,通常比给两个独立区间更有力。分类准确率可考虑 McNemar,AUC 有专门方法,通用指标可 paired bootstrap/permutation,但都要满足相应独立单位假设。

统计显著不等于业务重要。预先定义最小有意义差异、非劣界或成本门槛;同时检查运行时间、校准与群体影响。

11. Nested CV 评估的是“选择流程”

普通 GridSearchCV 在开发集内部做 CV,再在一份未触碰 test 上评估,是 holdout test 设计,不叫 nested CV。

Nested CV 有两层循环:

  1. outer split 留出本轮评估数据;
  2. 只在 outer-train 内运行 inner search;
  3. 用选出的流程预测 outer-test;
  4. 聚合所有 outer-test 结果。

它估计“若在类似训练数据上重新调参,这套选择流程表现怎样”。样本少且调参很多时很有价值,但计算昂贵,outer folds 也不是完全独立实验。

12. Learning Curve 回答数据还是模型受限

在多个训练规模上重复完整 fit,并用固定验证协议比较 train/validation loss:

  • 两者都差且接近:可能 representation/容量不足或目标噪声大;
  • train 好、validation 差:可能 variance/泄漏后修正/分布差异;
  • validation 随数据持续改善:更多同分布数据可能有价值;
  • 曲线平台:继续收集同类数据的边际收益可能小。

每个规模都要保持 group/time 结构,且 transform 重新 fit。不能从单次随机子样本画平滑线后过度解释。

常见误区

  • K=5/10 天然可靠:折数由独立单位、类数、成本和估计目标决定。
  • StratifiedKFold 解决类别不平衡的一切:它只控制折内比例。
  • CV 标准差就是误差条:折相关,不能直接当置信区间。
  • 无监督前处理可在全数据拟合:进入 pipeline 后同样泄漏。
  • GridSearchCV 自带 nested CV:只有显式 outer loop 才是 nested。

练习

  1. 为“新设备”和“已有设备未来故障”分别画切分边界。
  2. 检查每折 train/validation group 交集和时间顺序。
  3. 比较全数据缩放与 fold 内缩放造成的估计差异。
  4. 对同一 test predictions 做 row bootstrap 与 group bootstrap。
  5. 实现 outer group CV + inner search,比较 non-nested 结果。

小结

验证方法不是折数模板,而是部署条件的模拟器。独立单位、时间、标签窗口和前处理共同决定切分;CV 均值与标准差只是有限证据。Nested CV、paired comparison 和正确单位的 bootstrap 能回答更严格的问题,但不能替代对未来漂移的监控。

下一课把这套验证结构放进模型选择:搜索空间、预算、early stopping、校准和阈值都要在 inner loop 内完成。

Built with VitePress | Software Systems Atlas