TT Lab
开始
学习 学习路径 课程

上周那个模型更好,可没人找得到

名字不是版本

在 TT Lab 中继续学习

一句话总结

数据的版本不靠名称,而靠内容的哈希;正常范围不要从数据里推断,而要从业务出发提前声明好,再用机器去核对新收集的数据。

为什么需要它

复现被破坏的地方,大多不是代码,而是数据。训练脚本有 git 守着,而 train.csv 没有人守着。哪天上游流水线重复加载了一次,行数就增加了;下周有人改了缺失值处理,值就变了。文件名还是原来的,训练正常结束,指标只是稍微动了一点。这种组合藏得最久。

第二个问题是,“正常”的定义没有写在任何地方。哪怕混进了月费为负数的行,Python 也会把它当作 float 好好读进来,模型也会拿这个值去训练。人察觉到的时候,是几周之后预测变得奇怪了,而那时,用那份数据训练出来的模型已经在线上了。

工作原理

数据版本用指纹来确定。先算出文件字节的 SHA-256,同名的文件是不是同一个文件,就能一次回答(hashlib)。MLflow 的数据集对象也是出于同样的想法,把名称、摘要、原始位置、schema 和概要一起带着(MLflow Dataset Tracking)。摘要如果附在运行记录上,“这个模型是用什么数据做出来的”就不再是猜测,而成了查询。

正常范围用 schema 来声明。TensorFlow Data Validation 的文档把 schema 说明为“用代码写下输入数据必须满足的性质”,采用把统计量与 schema 对照来找出异常的方式。同一份文档列为常见问题的,是缺失值、标签混进特征导致模型提前看到答案,以及超出预期范围的值(TensorFlow Data Validation)。

계약(선언)        monthly_fee 0~200,  late_payments 필수
관찰(계산)        train.csv 의 monthly_fee 는 9.3 ~ 88.69
검증(대조)        week2.csv 240행 중 4행이 계약 위반

验证规则本身几行就够了。必填列是不是空的,能不能读成数字,是否落在声明的范围内。难的不是规则,而是 预先定好遇到违规时怎么办。是整个停下,是只去掉违规的行继续,还是只留下警告、照样训练,这些可能因列而异。

这里重要的是把声明和观察分开。如果把今天数据的最小值、最大值直接当作规则,那么今天碰巧混进来的离群值,就成了明天的规则。范围由业务来定,数据只负责确认是否落在这个范围内。

划分泄漏是另一条轴线。scikit-learn 的文档把泄漏定义为“预测时点无法使用的信息被用在了构建模型上”,并说明其结果是性能估计过于乐观,在真实的新数据上会更差。最常见的原因,它列的是没有把训练和评估子集正确分开(Common pitfalls)。

在现场相遇的样子

加入契约验证之后,最初几天告警会纷至沓来。大多数不是因为数据不好,而是因为契约写得比现实窄。这时如果把验证关掉,就回到了原点,所以还是一条条读告警、修改契约更好。这个过程,本身就是团队第一次就“我们的数据是什么样子”达成共识的过程。

第二是悄悄丢弃的习惯。如果用 try/except 跳过违反契约的行,训练会继续跑,但样本减少了这件事,哪里都不会留下。几个月后又变成“为什么性能下降了”。被丢弃的行如果作为隔离文件留下来,以后就能追查原因。

第三是想靠指标来抓泄漏。对于记忆力强的模型,泄漏会把指标大幅抬高,而在简单的线性模型里,几乎纹丝不动。所以划分不应该靠看数字来怀疑,而要 用标识符是否重叠来检查。

第四是把数据卡拖到以后。如果不写下这份数据来自哪里、不能说明什么,下一个人就会在不知道这些局限的情况下使用它。拿合成数据做出的模型,直接接到真实的客户应答上,这类事故就是这么发生的。数据卡不必长——来源、指纹、划分规则,再加上“用它不能说这样的话”两三行就够了,而这两三行,会在几个月后省掉一次会议。

下一项实验要做什么

给三个划分取指纹,用业务规则声明数据契约,用它去核对新收集的数据,把违规的行隔离起来。然后找出训练和评估里包含同一个客户的坏划分,去掉重叠之后,亲自测一测指标到底动了多少。