数据划分与数据泄漏
为什么要划分
训练误差回答“模型对见过的数据拟合得怎样”;我们真正关心的是泛化:对未来、未见样本是否仍然有效。因此需要互相隔离的数据:
- 训练集:拟合模型参数和所有需要从数据学习的预处理器。
- 验证集:选择特征、模型、超参数和决策阈值。
- 测试集:方案锁定后做最终估计,不参与选择。
样本很少时可在训练区内部做交叉验证;测试集仍应保持封存。
数据泄漏是什么
只要训练过程看到了真实部署时不该知道的信息,离线分数就会虚高。典型泄漏包括:
- 用全量数据的均值填缺失值,然后再划分。
- 同一用户、同一文档的近重复切片同时进入训练和测试。
- 用“处理结果”“最终状态”等预测发生后才产生的字段。
- 反复查看测试结果并据此改特征;此时测试集已经变成验证集。
- 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 策略。
练习
判断是否泄漏,并说明如何修复:
- 先对 10 万条日志做 TF-IDF,再把向量随机拆成训练/测试。
- 同一 FAQ 文档的多个 chunk 被随机拆分。
- 预测工单是否升级,却使用“升级操作人”字段。
- 每次改模型都看测试集 F1,直到满意。
掌握检查
- 能解释验证集和测试集为何不能互换。
- 能为用户、文档和时间相关数据选择正确划分单位。
- 能在 Pipeline 中让预处理只拟合训练集。
下一步:特征与预处理管线。
参考资料
核验日期:2026-07-22。