实验与可复现追踪
本节目标
学会把一次训练从“某个 notebook 跑出了好结果”变成一条可解释的记录,并理解可复现的边界。
为什么要追踪实验
模型指标只是结果,不是证据。两次运行都得到 0.91,可能使用了不同数据切分、特征代码或依赖版本;只保存模型文件,无法判断哪一次可信,也无法安全回滚。
先版本化问题,而不只是模型
在第一次训练前写一张简短的 problem contract(问题契约):
- 谁在什么时刻使用预测,最终做什么决策;
- 预测对象、时间范围与禁止使用的数据;
- 标签如何定义、何时到达、谁可以修订;
- 错误的业务代价以及必须单独观察的关键切片;
- 何时应该拒绝预测或交给人工;
- 当前问题定义、标签和评测策略各自的版本。
如果标签从“7 天内退款”改成“30 天内退款”,即使训练代码一行没变,也已经是不同问题版本。MLOps 血缘必须追踪这种语义变化;否则注册表里的模型版本号只能说明文件不同,无法说明它们是否仍在解决同一问题。
一次 run(运行) 可以理解为一组不可混淆的实验事实。最小记录通常包括:
| 类别 | 应记录什么 | 它回答的问题 |
|---|---|---|
| 数据 | 数据快照标识、时间范围、切分规则、标签版本 | 用了哪些样本? |
| 代码 | Git commit、入口脚本、脏工作区说明 | 运行了哪段代码? |
| 参数 | 超参数、随机种子、特征开关 | 如何训练? |
| 环境 | Python 与库版本、操作系统、容器镜像摘要(若使用) | 在什么环境运行? |
| 结果 | 整体和关键切片指标、运行时间、资源消耗 | 表现如何? |
| 制品 | 模型、预处理器、输入输出签名、评测报告 | 产出了什么? |
| 责任 | 运行者、用途、状态、审批记录 | 谁为何做出决定? |
追踪不是把所有文件都复制一份
大数据集和模型权重通常存放在对象存储或制品仓库,追踪系统记录其稳定标识、摘要和位置。关键原则是:记录必须足以定位正确对象,并能检测对象是否被悄悄替换。文件名 model.pkl 不够;内容摘要、版本 ID 或不可变 URI 才能提供更强证据。
以 MLflow 为例,Tracking 将 runs 组织在 experiments 中,并记录参数、指标、标签与制品。它能保存证据,但不会自动保证数据正确或实验设计合理;“已经登记”不能替代审查。
可复现的三种强度
- 可重跑:输入与环境可获得,流程能再次执行。
- 统计可复现:考虑随机性后,核心指标落在预期范围。
- 逐位一致:输出字节完全相同,要求更严格的算法、硬件和依赖控制。
GPU 算子、并行顺序和硬件差异都可能引入非确定性。因此先写清任务需要哪一种强度。多数模型晋级更关心统计可复现与结论稳定,而不是盲目追求逐位一致。
从零建立一条 run 记录
假设你训练一个垃圾邮件分类器:
- 为原始数据和标签生成快照 ID;
- 固定切分规则并记录随机种子,防止训练集和测试集重叠;
- 记录 Git commit;若工作区有未提交改动,必须显式标记;
- 保存参数和环境依赖;
- 同时报告整体准确率与重要切片(例如不同语言)的召回率;
- 保存模型、预处理器、输入字段和预测输出定义;
- 写下这次 run 是探索、候选还是已拒绝,避免误用。
常见错误与排查
- 只记录最佳指标:失败实验消失后,团队会重复踩坑,也可能发生选择偏差。
- 数据路径可变:
data/latest.csv后续被覆盖。改用不可变快照或内容摘要。 - 代码提交可追踪但环境不可追踪:依赖升级后无法重现。保存锁文件或镜像摘要。
- 随机种子写了却仍不一致:检查多进程、GPU 非确定性和数据加载顺序,并把剩余差异写入报告。
- 把追踪平台当质量保证:平台只能记录;数据泄漏、指标选错仍需测试和评审。
练习与自测
为你做过的一次数据分析先写 problem contract,再设计 run 清单,至少包含表格中的七类信息。然后回答:
- 如果数据快照相同但标签修订了,是否仍算同一次输入?为什么?
- Git commit 相同能否证明训练代码完全相同?脏工作区会带来什么问题?
- 哪些情况下逐位一致值得付出额外成本?
小结与下一步
实验追踪的核心是让结论能被定位、复核和比较。下一步进入 数据、特征与训练管线,把这些证据自动产出。
参考资料
- MLflow Tracking(核对于 2026-07-14)
- MLflow Model Registry 工作流(核对于 2026-07-14)