谁也找不到上周那个模型
目标
把一次训练做成“运行(run)”这个记录单位,把参数、指标、代码和数据指纹留在账本里,使以后能把那次运行重新复现。最后,用真实的日志确认没有留下记录的运行是如何消失的。
为什么重要
把模型做好,和把那个模型重新做出来,是两件不同的事。训练同时依赖随机数种子、数据版本和预处理代码,其中只要有一样没有写下来,两个月后就不会再得到同样的数字。实验跟踪工具所做的,不是漂亮的图表,而是 强制把足以让一次训练重新立起来的事实写下来。这里要亲自设计这个最小集合——亲手填一遍工具自动填好的那些格子,以后看到那些格子空着的账本,就能立刻察觉。
步骤
- 实验定义——先写下要最大化什么。
- 把第一次运行以一行写进账本。
- 改变参数,把运行增加到 4 个以上。
- 给所有运行加上代码和数据指纹。
- 用目标指标,从账本里重新挑出最佳运行。
- 只凭记录复现那次运行。
- 调查没有留下记录就消失的运行。
- 把账本导出成能独立成立的一包。
参考
- 所有工作都在
/root/mlops下进行。请先执行mkdir -p /root/mlops。 - 材料在
/opt/fixtures/mlops。数据说明请看DATA-CARD.md。 - 训练器运行示例:
python3 /opt/fixtures/mlops/train_model.py --train /opt/fixtures/mlops/train.csv --valid /opt/fixtures/mlops/valid.csv --lr 0.1 --epochs 40 --seed 7 - 评分器会用账本里写的参数 重新运行 训练器,核对指标。编造数字会被抓到。
- 常见错误——把
matches或reproducible写成字符串"true",以及用覆盖写(w)打开账本,把前面的运行删掉。 - 实验 Pod 没有卷。会话一结束,
/root就整个消失,所以想保留的东西,要像第 8 步的一包那样另外复制出来。
先写下什么算做得好
把实验定义保存到 /root/mlops/experiment.json。name 是 churn-baseline,objective_metric 是 valid_accuracy,direction 是 max,owner 是负责人姓名(2 个字符以上),dataset 是 /opt/fixtures/mlops/train.csv。
如果在选定指标之前就开始实验,以后就会挑对自己有利的数字。先把要最大化什么定死。
把第一次运行以一行写进账本
运行一次 /opt/fixtures/mlops/train_model.py,把结果以一行 JSON 记到 /root/mlops/runs.jsonl。一行中包含 run_id、params(lr、epochs、seed)、metrics(valid_accuracy、train_accuracy)、data(train、valid 路径)、started_at。指标原样抄写运行输出。
训练器会向标准输出打印一个 JSON 对象。不要用手去抄那些值,而是在 Python 里接收下来写进账本,这样就不会在抄写时出错。
再跑四次,做到可以比较
用不同的 lr、epochs、seed 组合增加运行,在 /root/mlops/runs.jsonl 里留下 4 个以上。run_id 每次运行必须不同,同样的参数组合不能写两次。每一行的指标都必须与用这些参数重新运行时得到的值相同。
把参数列表放在代码里循环,五行就一次做出来了。评分器会用每一行的参数重新运行训练器,并核对指标。
给代码和数据打上指纹
在 /root/mlops/runs.jsonl 的所有行里加入 code_sha256 和 data_sha256。code_sha256 是 /opt/fixtures/mlops/train_model.py 的、data_sha256 是 /opt/fixtures/mlops/train.csv 的 SHA-256 十六进制字符串。
即使参数相同,只要训练代码或数据变了,就是不同的实验。用 hashlib.sha256 对文件字节做哈希。
从账本里重新挑出最好的运行
读取 /root/mlops/experiment.json 的目标指标和方向,从 /root/mlops/runs.jsonl 中挑出最好的运行,把 run_id、metric、value、selected_by 保存到 /root/mlops/best.json。并列时,选账本中先出现的运行。
不要用眼睛挑,要用代码挑。如果由人来挑,下周再挑一次就会得出不同的答案。
只凭记录把那次运行复现出来
从 /root/mlops/runs.jsonl 中读取 /root/mlops/best.json 所挑选的运行的参数,重新运行训练器,把结果以 run_id、params、valid_accuracy、matches、code_sha256、data_sha256 保存到 /root/mlops/reproduce.json。matches 在与账本的值相同时为布尔值 true。
复现靠的不是“记忆”,而是“记录”。只使用账本里写的参数,字符串 "true" 不是布尔值。
调查没有留下记录就消失的运行
/opt/fixtures/mlops/ghost_run.log 是上周有人运行的一次运行的日志片段。把 ghost_metric(日志中写的值)、best_recorded_metric(/root/mlops/runs.jsonl 的最高值)、gap(两者之差,四舍五入到小数点后第四位)、reproducible、missing_fields(仅凭日志无法得知的运行记录字段,按字典序)、recovery_plan(40 个字符以上)保存到 /root/mlops/incident.json。
从日志里能读出来的只有一个指标。数一数运行记录应具备的字段中哪些是空的,就能看出为什么无法复现。
会话结束之前把账本带出去
把 runs.jsonl、experiment.json、best.json 复制到 /root/mlops/export/ 下,并把 experiment、run_count、best_run_id、files 保存到 /root/mlops/export/manifest.json。files 的每一项包含 path(只有文件名,不带目录)、sha256、bytes,best_run_id 必须是从所装入的账本里重新计算得到的值。
实验 Pod 没有卷,会话一结束 /root 就消失了。这一包必须能独立成立——只有账本而没有目标,以后就无法解释。