数据划分与数据泄漏

为什么要划分

训练误差回答“模型对见过的数据拟合得怎样”;我们真正关心的是泛化:对未来、未见样本是否仍然有效。因此需要互相隔离的数据:

  • 训练集:拟合模型参数和所有需要从数据学习的预处理器。
  • 验证集:选择特征、模型、超参数和决策阈值。
  • 测试集:方案锁定后做最终估计,不参与选择。

样本很少时可在训练区内部做交叉验证;测试集仍应保持封存。

数据泄漏是什么

只要训练过程看到了真实部署时不该知道的信息,离线分数就会虚高。典型泄漏包括:

  • 用全量数据的均值填缺失值,然后再划分。
  • 同一用户、同一文档的近重复切片同时进入训练和测试。
  • 用“处理结果”“最终状态”等预测发生后才产生的字段。
  • 反复查看测试结果并据此改特征;此时测试集已经变成验证集。
  • RAG 评测中,题目由同一段语料机械生成,答案措辞直接泄漏来源。

关键规则:fit 只能发生在训练数据上;验证/测试只调用 transform 和 predict。

随机划分并非总是正确

数据关系建议划分原因
独立同分布、类别稳定分层随机划分保持类别比例
同一用户多条记录按用户分组防止身份特征跨集合泄漏
时间序列、上线后未来数据按时间前后划分模拟真实未来
文档被切成多个 chunk按原文档分组防止近重复段落泄漏

random_state 用于复现实验,但固定随机数不会修复错误的划分逻辑。

当既有分组又要尽量维持类别比例时,可在训练区采用 StratifiedGroupKFold;它优先保证组不重叠,并只会在这一约束下尽量保持类别比例,组数太少或分布过于偏斜时不可能同时做到两者。对时间数据,TimeSeriesSplit 可以避免“用未来训练、在过去评估”,但其可比性仍依赖样本时间间隔和业务窗口的定义。任何 splitter 都不能自动判断同源副本、延迟标签或权限变化是否已经泄漏。

最小代码形态

from sklearn.model_selection import train_test_split
 
X_train, X_test, y_train, y_test = train_test_split(
    texts,
    labels,
    test_size=0.25,
    stratify=labels,
    random_state=42,
)

这是独立样本分类的起点;有用户、文档或时间关联时应改用 group/time-aware 策略。

练习

判断是否泄漏,并说明如何修复:

  1. 先对 10 万条日志做 TF-IDF,再把向量随机拆成训练/测试。
  2. 同一 FAQ 文档的多个 chunk 被随机拆分。
  3. 预测工单是否升级,却使用“升级操作人”字段。
  4. 每次改模型都看测试集 F1,直到满意。

掌握检查

  • 能解释验证集和测试集为何不能互换。
  • 能为用户、文档和时间相关数据选择正确划分单位。
  • 能在 Pipeline 中让预处理只拟合训练集。

下一步:特征与预处理管线。

参考资料

核验日期:2026-07-22。