qinghuan
← 回到知识探索

AI 直觉 / 案例拆解

测试分数很漂亮,是否偷看了测试集?

从一列事后字段和一次提前求平均,理解数据泄漏为何会让评估失去原本的意义。

展开本文目录
  1. 字段存在,不代表当时可用
  2. 没有标签,求平均也可能提前看了试卷
  3. 把切分、拟合和评估的顺序写清
  4. 记住这三点
  5. 别把例子用过头
  6. 自己试一小步
  7. 查原始资料
  8. 找一份资料继续练
开始前,会这些就够了
  • 知道训练集用于学习,测试集用于最终评估
  • 理解平均数
遇到不熟悉的词,先看这 5 个解释
训练
利用数据和既定目标来调整模型参数的过程。在常见神经网络中,程序先计算输出与目标之间的差距,再按更新规则反复调整,希望对新数据也有用。
数据泄漏
建模或选择方案时,用上了真实预测时不该得到的信息,例如测试集统计量、未来才产生的字段或重复出现的答案。这样评估可能显得很好,却不能代表面对新数据的能力。
评测
用事先确定的样本、任务和评分规则,检查模型或整个系统能否满足要求的过程。既可检查答案,也可检查工具操作和最终状态,结果要连同测试条件一起理解。
分组验证
评估模型能否处理新对象时,把同一用户、设备或文档的记录作为一组,整组放进训练或验证的一边,避免同一个对象同时出现在两边。
时间切分
模拟用过去预测未来时,按记录发生的先后划分数据:较早的一段用于训练,较晚的一段用于验证或测试,而不是把所有日期随意打散。
打开完整术语小抄 ↗

假设你要在订单创建时预测是否会退货。离线表里有一列“退款完成时间”,模型用上它之后分数非常好。问题是,这列信息在下单时还不存在。即使训练行和测试行完全分开,测试仍在考一个现实中拿不到答案提示的任务。这是虚构案例,但它点出了数据泄漏的核心。

泄漏不一定是有人故意把答案复制进输入。只要建模时用了预测当下不该获得的信息,评估就可能过于乐观。检查重点应是每条信息什么时候产生、谁在什么步骤使用了它,而不只是文件名里有没有 train 和 test。

字段存在,不代表当时可用

回看一张整理好的历史表,很容易忘记其中的字段来自不同时间。下单时能获得商品和数量,却不能获得将来是否退款的结果。给每个输入字段加上“最早可用时间”,再与要作预测的时间比较,才能把事后信息找出来。用于评价的最终结果当然可以稍后得到,但不能作为当时的输入。

也要检查同一个对象是否以多条记录出现。如果任务是预测从未见过的新用户,训练集和测试集混入同一用户的近似记录,可能让模型靠熟悉的个人特征过关。如果任务本来就是预测已知用户的未来行为,切分目标又不同;分组不是万能规定,必须先写清希望推广到谁、哪个时间段。

没有标签,求平均也可能提前看了试卷

预处理也会从数据中学习数值。例如把每个数减去平均值,需要先确定平均值取自哪里。下面用三个训练值和一个测试值演示:仅用训练值求平均,与把测试值也算进去,会让同一个训练数得到不同的中心化结果。差别出现于模型训练之前,所以不能靠之后再切一份测试文件来消除。

这个小程序只证明测试数据影响了预处理参数,没有训练分类器,也不声称这一次平均值变化必然提高分数。泄漏可能以多种方式改变结果;其根本问题是评估没有保持原先规定的信息边界。缺失值填补、特征选择和降维等会学习参数的步骤,也要检查拟合数据来自哪一部分。

小实验 · python
from statistics import mean

train = [10.0, 12.0, 14.0]
test = [1000.0]
train_mean = mean(train)
leaky_mean = mean(train + test)
print(f"train-only mean={train_mean:.1f}")
print(f"with-test mean={leaky_mean:.1f}")
print(f"same training value: {12.0 - train_mean:.1f} vs {12.0 - leaky_mean:.1f}")

怎样运行:Python 3.10+;保存为 leakage_mean.py,在该目录运行 python leakage_mean.py。只用标准库,无需联网。

运行后对照结果
train-only mean=12.0
with-test mean=259.0
same training value: 0.0 vs -247.0

把切分、拟合和评估的顺序写清

通常先按任务需要划分数据,再只在训练部分拟合预处理参数和模型。验证、测试和线上输入使用训练时学到的同一套转换规则。scikit-learn 中 fit 表示学习参数,transform 表示按已学规则转换;把这些步骤放进正确使用的 Pipeline,可减少交叉验证时把验证折提前用于拟合的失误。

管道不会自动知道“退款完成时间”来自未来,也不会替你决定用户应如何分组。时间预测要让评估符合先见过去、后看未来的使用方式;新对象预测则要避免同一个对象横跨训练与评估。具体是否要留时间间隔、如何处理重复记录,仍取决于数据产生过程。

模型和阈值可以用验证集选择,最终测试集用于检验已选方案。如果反复查看测试成绩,再据此改特征或参数,它也逐渐参与了选择。记录切分规则、拟合范围与查看测试结果的次数,远比只保留最高分更有帮助;发现泄漏后应重做干净评估,不能把旧分数继续当成未知数据上的能力。

记住这三点

  • 用预测时拿不到的信息,可能让离线评价高估实际能力。
  • 预处理的拟合范围也属于训练边界,测试数据不能提前参与。
  • 分组、时间切分和管道各解决一部分问题,仍须检查字段来源。

别把例子用过头

  • 退款案例完全虚构;本地代码只展示均值受影响,没有制造或测量模型准确率。
  • 固定的逐条无状态转换与需要拟合参数的转换应区分,不能见到预处理就一概判定泄漏。
  • 如何分组或按时间评估取决于目标使用场景,不是所有数据都套同一切分方式。

自己试一小步

怎样为一个预测任务画出“不准提前知道什么”的时间线?

  1. 选择自拟的退货或设备维护任务,写明预测发生的时刻。
  2. 列五个候选字段,分别标出产生时间与是否依赖最终结果。
  3. 画出切分、预处理拟合、模型选择和最终测试的执行顺序。

做到这些,就算完成

  • 输入字段在预测时确实可获得。
  • 预处理参数的拟合范围明确。
  • 重复对象、时间先后和最终测试反馈都在检查范围内。

查原始资料

想确认一个细节,先看这份资料对应的部分,不必从头读完所有文档。

找一份资料继续练

先选一项,按下面的范围继续学;不必从头读完整门课程。