实验与可复现追踪

本节目标

学会把一次训练从“某个 notebook 跑出了好结果”变成一条可解释的记录,并理解可复现的边界。

为什么要追踪实验

模型指标只是结果,不是证据。两次运行都得到 0.91,可能使用了不同数据切分、特征代码或依赖版本;只保存模型文件,无法判断哪一次可信,也无法安全回滚。

先版本化问题,而不只是模型

在第一次训练前写一张简短的 problem contract(问题契约):

  • 谁在什么时刻使用预测,最终做什么决策;
  • 预测对象、时间范围与禁止使用的数据;
  • 标签如何定义、何时到达、谁可以修订;
  • 错误的业务代价以及必须单独观察的关键切片;
  • 何时应该拒绝预测或交给人工;
  • 当前问题定义、标签和评测策略各自的版本。

如果标签从“7 天内退款”改成“30 天内退款”,即使训练代码一行没变,也已经是不同问题版本。MLOps 血缘必须追踪这种语义变化;否则注册表里的模型版本号只能说明文件不同,无法说明它们是否仍在解决同一问题。

一次 run(运行) 可以理解为一组不可混淆的实验事实。最小记录通常包括:

类别应记录什么它回答的问题
数据数据快照标识、时间范围、切分规则、标签版本用了哪些样本?
代码Git commit、入口脚本、脏工作区说明运行了哪段代码?
参数超参数、随机种子、特征开关如何训练?
环境Python 与库版本、操作系统、容器镜像摘要(若使用)在什么环境运行?
结果整体和关键切片指标、运行时间、资源消耗表现如何?
制品模型、预处理器、输入输出签名、评测报告产出了什么?
责任运行者、用途、状态、审批记录谁为何做出决定?

追踪不是把所有文件都复制一份

大数据集和模型权重通常存放在对象存储或制品仓库,追踪系统记录其稳定标识、摘要和位置。关键原则是:记录必须足以定位正确对象,并能检测对象是否被悄悄替换。文件名 model.pkl 不够;内容摘要、版本 ID 或不可变 URI 才能提供更强证据。

以 MLflow 为例,Tracking 将 runs 组织在 experiments 中,并记录参数、指标、标签与制品。它能保存证据,但不会自动保证数据正确或实验设计合理;“已经登记”不能替代审查。

可复现的三种强度

  1. 可重跑:输入与环境可获得,流程能再次执行。
  2. 统计可复现:考虑随机性后,核心指标落在预期范围。
  3. 逐位一致:输出字节完全相同,要求更严格的算法、硬件和依赖控制。

GPU 算子、并行顺序和硬件差异都可能引入非确定性。因此先写清任务需要哪一种强度。多数模型晋级更关心统计可复现与结论稳定,而不是盲目追求逐位一致。

从零建立一条 run 记录

假设你训练一个垃圾邮件分类器:

  1. 为原始数据和标签生成快照 ID;
  2. 固定切分规则并记录随机种子,防止训练集和测试集重叠;
  3. 记录 Git commit;若工作区有未提交改动,必须显式标记;
  4. 保存参数和环境依赖;
  5. 同时报告整体准确率与重要切片(例如不同语言)的召回率;
  6. 保存模型、预处理器、输入字段和预测输出定义;
  7. 写下这次 run 是探索、候选还是已拒绝,避免误用。

常见错误与排查

  • 只记录最佳指标:失败实验消失后,团队会重复踩坑,也可能发生选择偏差。
  • 数据路径可变:data/latest.csv 后续被覆盖。改用不可变快照或内容摘要。
  • 代码提交可追踪但环境不可追踪:依赖升级后无法重现。保存锁文件或镜像摘要。
  • 随机种子写了却仍不一致:检查多进程、GPU 非确定性和数据加载顺序,并把剩余差异写入报告。
  • 把追踪平台当质量保证:平台只能记录;数据泄漏、指标选错仍需测试和评审。

练习与自测

为你做过的一次数据分析先写 problem contract,再设计 run 清单,至少包含表格中的七类信息。然后回答:

  1. 如果数据快照相同但标签修订了,是否仍算同一次输入?为什么?
  2. Git commit 相同能否证明训练代码完全相同?脏工作区会带来什么问题?
  3. 哪些情况下逐位一致值得付出额外成本?

小结与下一步

实验追踪的核心是让结论能被定位、复核和比较。下一步进入 数据、特征与训练管线,把这些证据自动产出。

参考资料