实验追踪工具:MLflow 入门归纳总结
说到模型训练,很多新手朋友容易踩同一个坑:跑了一堆实验,最后分不清哪个超参数组合、哪个数据集版本、哪个代码 commit 产出的指标最好。我以前也吃过这个亏,笔记本里记满了参数列表,结果隔两周自己都看不懂。后来接触了 MLflow,才发现追踪实验其实可以很优雅。
MLflow 不是一个“大而全”的平台,它是一套轻量级的实验追踪、模型管理和部署工具。它的核心思想是:你只管写训练代码,剩下的记录、对比、复现全交给它。今天我把最常用的部分——Tracking——给你拆开揉碎讲清楚。
MLflow 的四个组件
先看一眼整体结构,心里有个地图。MLflow 分四块,但日常用到最多的就是 Tracking:
说白了,Tracking 帮你把每一次实验的参数、指标、代码版本、环境信息、模型文件都存起来,还能用一个 Web UI 对比多条实验曲线。我问你,没有追踪工具之前,你是不是靠手工复制文件夹来保存不同模型?嗯,我以前也是。
安装与初始化
安装特别简单,一条命令搞定:
pip install mlflow
要是想用 UI 查历史记录,再装个扩展存储:
pip install mlflow[extras]
我个人习惯用 file:// 本地存储起步,等实验多了再切到 MySQL 或者 S3。你只要记住:一个 MLflow 服务可以跑在单机上,也能跑在集群里。我们先用本地模式试起来。
启动 UI 也很简单:
mlflow ui --port 5000
然后浏览器访问 http://localhost:5000,就能看到一个空白的实验列表。别急,马上就有内容了。
核心概念:实验 & 运行
MLflow 里两个最基本的概念:
- 实验(Experiment):一个项目主题,比如“BERT fine-tune on IMDB”、“ResNet50 on CIFAR-10”。每个实验可以包含很多次运行。
- 运行(Run):一次具体的训练尝试,记录下这次的所有元数据。
我建议一开始就给每个项目建一个实验,这样不同方向的结果不会混在一起。我自己踩过坑:所有实验都放在默认 “Default” 实验里,半个月后里面混了十几个不同项目的跑分,看曲线的时候头都大了。
基本使用流程(代码实战)
我们写一段典型的训练代码,把 MLflow 加进去。假设你正在训练一个 PyTorch 分类模型:
import mlflow
import mlflow.pytorch
# 1. 设置实验名称(不存在就自动创建)
mlflow.set_experiment("CIFAR10_ResNet18")
# 2. 开始一次运行
with mlflow.start_run(run_name="exp_lr_0.001"):
# 记录超参数
mlflow.log_param("learning_rate", 0.001)
mlflow.log_param("batch_size", 64)
mlflow.log_param("optimizer", "Adam")
mlflow.log_param("epochs", 10)
# 模拟训练过程(实际你放自己的训练循环)
for epoch in range(10):
train_loss = 1.2 - 0.1 * epoch # 假数据
val_acc = 0.3 + 0.06 * epoch
# 记录每个 epoch 的指标
mlflow.log_metric("train_loss", train_loss, step=epoch)
mlflow.log_metric("val_accuracy", val_acc, step=epoch)
# 记录最终的模型(保存为 MLflow 格式)
mlflow.pytorch.log_model(model, artifact_path="model")
关键点:mlflow.log_metric 可以传 step 参数,这样 UI 里就会自动画折线图,效果跟在 TensorBoard 里看一样直观。
跑完一次后,打开 UI 就能看到这次运行的所有信息。你可以在左侧「Experiments」栏选中你的实验,右侧会列出所有运行,点击任意一条就能看到详细参数、指标曲线和 artifact 列表。
我还常用哪些命令?
| API | 用途 | 注意的点 |
|---|---|---|
log_param(key, value) |
记录超参数或配置(不可修改) | 一个运行中 key 只能设一次,设第二次会报错 |
log_metric(key, value, step=None) |
记录数值指标(可多次写入) | 同一 key 可以写多次,UI 按 step 画曲线 |
log_artifact(local_path) |
上传本地文件/文件夹到 artifact 存储 | 常用于上传 best_model.pth、config.yaml、confusion_matrix.png |
log_artifacts(local_dir) |
上传整个目录 | 递归上传,注意文件量大时可能慢 |
set_tag(key, value) |
给运行打标签 | 我习惯打“status: finished”或“description: baseline” |
💡 实用小技巧:如果你想对比两次运行在同一个指标上的差异,可以在 UI 里“Compare Runs”选中多条,MLflow 会自动生成指标差异表和并行坐标图(Parallel Coordinates Plot),一眼看出哪个超参数影响最大。
避坑指南(我踩过的,你别再踩)
我曾经犯过一个低级错误: 在 Jupyter notebook 里反复执行 mlflow.start_run() 但忘了写 with 语句,导致每次运行之间嵌套了,所有指标都记录到了同一个 run 里面。后来我养成了习惯——永远用 with mlflow.start_run(): 上下文管理,而不是主动调用 start 和 end。
另一个常见的坑是:artifact 路径默认是当前目录下的 mlruns 文件夹。如果你在多个项目之间切换,或者用 Git 管理代码,千万别把 mlruns 提交到版本控制里。我建议在 .gitignore 里加上 mlruns/,或者显式指定存储位置:
# 设置 artifact 存储路径为你自己的目录
mlflow.set_tracking_uri("file:///home/user/mlflow_tracking")
⚠ 注意: 如果你在分布式训练(比如 PyTorch DDP)中使用 MLflow,不要在每个 worker 里都创建独立的 run。默认情况下,一个进程调用 mlflow.start_run() 就够。如果所有 worker 都写,你会得到一堆混乱的 run。正确做法是只让 rank 0 进程记录实验,其他 worker 跳过记录逻辑。
if dist.get_rank() == 0:
mlflow.start_run()
# ... 记录代码
mlflow.end_run()
进阶:用 MLflow 做超参数搜索
当你用 Grid Search 或者 Optuna 跑一堆组合时,每个组合都应该是一个独立的 run。我的写法是这样:
for lr in [0.01, 0.001, 0.0001]:
for bs in [32, 64]:
with mlflow.start_run(nested=True):
mlflow.log_param("learning_rate", lr)
mlflow.log_param("batch_size", bs)
# 训练并记录指标
...
加上 nested=True 会让这些 run 在 UI 里以“树状结构”展示,父 run 是主调,子 run 是每次搜索。这种模式特别适合一键拉平所有搜索空间的结果对比。
如何查看历史指标?
除了 Web UI,你也能用 Python 客户端查:
from mlflow.tracking import MlflowClient
client = MlflowClient()
experiment = client.get_experiment_by_name("CIFAR10_ResNet18")
runs = client.search_runs(experiment.experiment_id)
for run in runs:
print(run.info.run_id, run.data.metrics.get("val_accuracy"))
这条代码把我从手动截图对比中解放出来了。以前我每次改参数都打开 Excel 手动输入,现在直接写个脚本导出所有指标,画对比图,省了至少 80% 的时间。
小结:MLflow 入门的三个关键
- 先解决记录问题:用
log_param和log_metric把你的超参数和每步指标存下来。 - 再用 UI 做对比:本地起一个
mlflow ui,就能像刷博客一样看不同实验的曲线。 - 最后固化习惯:每次新实验前
set_experiment,每次运行用with start_run,每次迭代结束时存好 artifact。
嗯,入门 MLflow 就这点东西。等你跑个几十次实验,开始觉得手动对比麻烦时,自然会想用它的 Search API 做自动化分析。到那时候,你已经是半个 MLflow 老手了。