跳到主要内容

数据划分与泄漏

2008 年的 KDD Cup 比赛要从乳腺 X 光数据里找出癌症。Kaufman 等人 2011 年的论文回顾这场比赛时写道:大多数参赛者没理会的病人编号,本身就有极强、出人意料的预测力。数据由多个临床研究、机构或设备来源拼成,每个来源分到一段连续的编号,合并时又没有隐去来源,于是编号透露了病人来自哪里,而有的来源患癌比例高。

编号里的信号来自数据拼合的方式,不来自乳腺影像本身;真正筛查新病人时,不会有这样一段按来源排好的编号,比赛里的高分也就不作数。这就是数据泄漏(Data Leakage):训练、特征工程、模型选择或评估环节使用了在真实预测场景中不可得的信息,导致评估结果虚高。

删掉编号也不一定就干净了。Kaufman 等人指出,如果不同编号段对应不同地点、不同设备的医院,数据里还会留下别的痕迹:比如患癌比例高的那家,图像灰度也偏高,模型不看编号,照样能从其余数据认出来源,分数仍然偏乐观。

这类错误在科研里也不少见。Kapoor 和 Narayanan 2022 年调查了采用机器学习方法的研究领域,在 17 个领域里找到已被指出的泄漏错误,涉及 329 篇论文,有的结论被严重高估。

防泄漏的办法,是先说清预测那一刻能拿到哪些信息,再让切分和每一步预处理都守住这条边界。文末用一个反复改提示词的例子,把下面的规则用到大模型评测上。

定义信息边界​

在动手切分数据前,必须明确以下元数据:

unit 预测对象(如:患者、订单、设备)
prediction_time 必须做出预测的时间点
horizon 预测目标的时间跨度(如:未来30天)
available_info 预测时刻真实可用且合规的信息集合
label_time 标签生成、成熟及可能修订的时间
split_key 禁止跨集合的实体、群体或时间维度

信息的合法性是相对于部署时的信息集 I(t)\mathcal{I}(t) 而言的。数据库里存在某个字段,不代表它在预测时刻 tt 已经生成,也不代表业务规则允许模型使用它。

泄漏类型与对策​

类型典型场景失真原因控制手段
目标泄漏/结果发生后的信息泄漏用出院后的账单数据预测入院时的风险特征由结果事件产生(因果倒置)审计事件时间戳与数据血缘(Lineage)
时间泄漏随机切分时间序列,或使用了未来的数据修订值训练集看到了“未来”的状态采用前向切分(Forward Split)或数据版本(Vintage)管理
实体/群体泄漏同一患者/设备同时出现在训练集和测试集模型记住了身份或历史模式使用 Group Split,必要时结合 Group + Time
重复/近重复同一图片的不同裁剪、模板化文档跨集合出现测试集并非独立新样本切分前对近重复样本进行聚类或分组
预处理泄漏在全量数据上拟合 Scaler、Imputer、PCA 或特征选择测试集分布影响了变换参数所有变换仅在训练折(Fold)内 Fit,测试集仅 Transform
模型选择泄漏反复根据测试集指标调整模型或阈值测试集变成了隐式的验证集锁定测试集,或获取全新的 Holdout 数据
基准污染训练语料中包含测试题及其答案模型在背诵而非泛化溯源(Provenance)、去重、来源与时间隔离

注意: 强相关特征(Proxy)不一定非法,判断标准是:该信息在预测时刻是否真实存在且被允许使用。部署时合法可用的 Proxy 特征,可能只是捷径、偏见或脆弱的相关性,这属于模型鲁棒性问题,而非严格意义上的泄漏。对此应通过子群体分析、干预测试和分布偏移检测来评估,而不是简单地将所有强相关特征视为泄漏。

切分策略应模拟真实使用场景​

不同的部署问题需要不同的切分逻辑:

部署问题推荐切分方式该切分无法回答的问题
同分布的新独立样本分层随机切分(Stratified Random)时间漂移、同一实体的记忆效应
新患者/用户/设备群体切分(Group Split)未来政策或环境变化
未来时间段时间前向切分(Temporal Forward)新机构或新地区的情况
新机构/地区外部/群体保留(External/Group Holdout)所有未知的外部环境
空间依赖样本空间块切分(Blocked Spatial)未观测区域的任意外推

提示: 分层抽样(Stratification)仅保证标签比例一致,它不能防止同一实体、时间邻近或重复样本跨越训练/测试边界。

测试集始终单独留出;其余训练数据进行五折交叉验证,每轮轮换验证折。查看清晰大图

先看右侧:测试集从一开始就单独留出。左侧每一行用一个蓝色折做验证,其余绿色折用于拟合;预处理也要在每轮的训练折内重新拟合。测试集不参与选模型,只用于最后评估。图中用五折演示这个过程,实际切几折、按什么分组,由上一节的部署问题决定。

Pipeline 不变量​

设训练索引为 ItrI_{tr},验证索引为 IvaI_{va}。任何从数据中学习参数的变换 TηT_\eta 必须满足:

η^=fit⁡(T,DItr),Xva′=Tη^(XIva).\hat\eta=\operatorname{fit}(T,D_{I_{tr}}), \qquad X'_{va}=T_{\hat\eta}(X_{I_{va}}).

这涵盖了标准化、缺失值填充、词表构建、PCA、特征选择、目标编码(Target Encoding)、过采样及学习式数据增强。

  • 交叉验证(CV): 每一折内部都必须重新拟合所有变换。
  • 嵌套交叉验证(Nested CV): 外层循环用于评估泛化性能,内层循环用于超参数或特征选择。
  • 常见错误: 先在全量数据上做特征选择,再划分交叉验证折,依然构成泄漏。

一个只用训练数据拟合的变换​

scikit-learn 泄漏指南遵循同一规则:在训练数据上学习预处理参数,然后原样应用。假设训练特征值为 2 和 4,验证值为 100。训练集均值为 3,所以验证值中心化后为 97。若全局中心化,均值会变成 106/3106/3,验证数据便改变了训练样本的表示。

train = [("A", 2.0), ("B", 4.0)]
valid = [("C", 100.0)]
assert {g for g, _ in train}.isdisjoint(g for g, _ in valid)
center = sum(x for _, x in train) / len(train)
assert [x - center for _, x in train] == [-1.0, 1.0]
assert [x - center for _, x in valid] == [97.0]

这个示例测试的是新实体:A、B 在训练集,C 只在验证集。如果要预测的是已知患者的下一次就诊,就可以合法使用他当时已有的历史记录;患者不重叠的测试回答的是另一个问题:模型对没见过的患者表现如何。若目标是未来的新患者,应先定一个时间截点:训练只用截点之前、标签已经成熟的记录,测试只用截点之后、从未进入训练集的患者。这样做会把截点前就来过的老患者排除在测试之外,测试人群因此变了,报告里要写明排除了多少人。

流水线能防止跨折拟合错误,却不能消除训练集内部的所有目标泄漏。目标编码可能把某行自身的标签写入该行特征;训练编码应采用折外计算或合适的时序计算。重采样只作用于训练数据,不要改变验证集和测试集的类别比例。

实战案例:反复改提示词,测试集悄悄变成了验证集​

场景: 设想你在给一个只回答自家产品手册的问答助手调系统提示词。手里有 30 道写好标准答案的题,每改一版提示词就把 30 道全跑一遍,挑分数最高的那版上线。

错误设计:

  • 每一版提示词都照着这 30 道题的分数来改。改过二十轮以后,这 30 道题就成了验证集,分数涨的那部分里有一截是对这 30 道题本身的拟合。
  • few-shot 示例从这 30 道题里挑,等于把考题和答案写进了提示词。
  • 为了凑题量,先让模型把每道题改写成五种问法,再随机切出测试集,于是同一道题的改写同时落进调参集和测试集。
  • 检索增强的题是按手册片段出的。同一份文档的片段一部分出了调参题,一部分出了测试题,两边的问题共用同样的上下文和措辞。

稳健设计:

  1. 先切再扩: 先按原始题目分组(检索增强的题按源文档分组)切出调参集和测试集,再在调参集里做改写和扩充。
  2. 示例只从调参集取: few-shot 示例和提示里的样例回答只来自调参集。
  3. 调参只看调参集: 每一版提示词都只在调参集上比较,测试集锁起来不看。
  4. 测试集只跑一次: 选定最终版本后跑一次,按题型报告结果。之后还要再改提示词,就换一批新题当测试集。
  5. 固定运行配置: 模型 ID、温度和评分方式写进运行卡片,测试前后保持不变。

换成新题以后分数如果明显下降,说明旧分数里有一截来自对那 30 道题的拟合。这和 KDD Cup 2008 里的病人编号是同一类问题:分数里混进了真实使用时拿不到的信息。

泄漏探针与负对照​

为了主动发现泄漏,建议执行以下检查:

  • 时间审计: 为每个特征记录事件发生时间、入库时间、修订时间。
  • 单特征探针: 单独训练可疑特征,若性能异常高,需追溯其数据血缘。
  • ID 探针: 移除或打乱实体标识符及高基数代理特征。KDD Cup 2008 的病人编号就是这一条要抓的东西。
  • 重复审计: 检查精确重复及近重复样本。
  • 标签置乱(Permutation): 若完整流水线在随机打乱标签后仍显著高于随机基线,可能存在代码缺陷或泄漏。
  • 边界断言: 代码层面断言同一组样本不跨集合、前向评估时训练时间早于测试时间、变换器未在测试集上拟合。
  • 全新保留集: 对于被反复查看过的基准测试集,另取一批全新的保留数据。

局限: 标签置乱无法发现所有泄漏。它只能抓到流水线里会跟着标签重新计算的泄漏。如果泄漏特征是用真实标签预先算好、存在表里的,标签一打乱它就对不上了,置乱后的分数照样回到随机水平,检验显示正常,泄漏却还在。所以必须结合数据血缘分析。

概念辨析​

  • 过拟合(Overfitting): 模型学习了训练样本中的噪声,即使流水线合法也可能发生。训练准确率高本身不等于泄漏:过拟合的模型,训练准确率同样很高。
  • 泄漏(Leakage): 开发流程越过了信息边界。
  • 分布偏移(Distribution Shift): 部署时的数据分布与评估时不同。
  • 混杂(Confounding): 共同原因扭曲了因果解释。

这四者可以同时存在:修好泄漏以后,过拟合和分布偏移照样可能还在。

发布前检查清单​

prediction_time: explicit
label_horizon_and_maturity: explicit
unit_and_group_key: explicit
split_algorithm_and_seed: versioned
all_learned_transforms_fit_on_train_only: true
test_used_for_selection: false
duplicate_and_overlap_audit: complete
feature_availability_lineage: reviewed
known_residual_risks: listed
探索关联打开关联网络