4、MLflow实战:自动记录(autolog)、嵌套运行(nested runs)、父子运行管理、搜索与查询实验
说实话,MLflow 最让我觉得「真香」的功能,就是 autolog。你想想看,以前做实验的时候,手动记录每个参数、每个指标,代码里到处插着 mlflow.log_param 和 mlflow.log_metric,不仅累,还容易漏。autolog 一出,这些全自动了。
4.1 自动记录(autolog):一键开启,省心省力
autolog 的原理很简单——它通过 monkey-patch 的方式,自动拦截主流框架的训练过程。你只需要在训练开始前调用一行代码,剩下的交给它。
import mlflow
# 开启自动记录
mlflow.autolog()
# 然后正常训练你的模型
# MLflow 会自动记录:参数、指标、模型结构、数据集信息...
我个人习惯在项目入口处就调用 mlflow.autolog(),这样不管后面跑多少个实验,每个实验的细节都会被自动捕获。支持的主流框架包括:TensorFlow、PyTorch、Scikit-learn、XGBoost、LightGBM 等。
autolog 自动记录的内容清单:
- 参数:学习率、批量大小、层数、优化器配置等
- 指标:训练损失、验证准确率、F1 分数等
- 模型:训练完成的模型文件(自动保存为 MLflow 模型格式)
- 数据集信息:样本数、特征数等
- 源代码版本:Git commit ID(如果有)
小提示:如果你只想记录某个特定框架,可以指定框架名称:mlflow.autolog(log_input_examples=True, log_model_signatures=True)。我在项目中经常开启 log_input_examples,这样部署时能自动生成输入示例,省了不少事。
4.2 嵌套运行(nested runs):把实验组织成树
单次运行(run)只能记录一个实验。但实际工作中,我们经常需要跑一组对比实验,或者在一个大实验里包含多个子实验。这时候就需要嵌套运行了。
嵌套运行说白了就是「run 里面套 run」。父 run 管理整体实验,子 run 管理具体细节。比如你调参时,父 run 记录「调参任务」,每个子 run 记录一组参数组合的实验结果。
with mlflow.start_run(run_name="调参实验_2024") as parent_run:
mlflow.log_param("任务", "XGBoost 调参")
# 嵌套子运行
for lr in [0.01, 0.001, 0.0001]:
with mlflow.start_run(nested=True, run_name=f"lr={lr}") as child_run:
# 子 run 自动记录
mlflow.autolog()
# 训练代码...
# 所有指标和参数都会记录到子 run 中
嗯,这里要注意:子 run 的 nested=True 参数不能少。我曾经漏掉这个参数,结果子 run 变成了独立的 run,跟父 run 完全没关系,查实验时乱成一团。
4.3 父子运行管理:实验的树状结构
嵌套运行创建了父子关系,那怎么管理这些关系呢?MLflow 提供了几个实用方法。
| 方法 | 作用 | 使用场景 |
|---|---|---|
mlflow.get_run(run_id) |
获取指定 run 的详细信息 | 查看某个子 run 的指标 |
run.data.tags |
获取 run 的标签 | 判断 run 是否为嵌套运行 |
mlflow.search_runs() |
搜索实验 | 按条件筛选 run |
每个子 run 的标签里会自动添加 mlflow.parentRunId 字段。你可以通过这个字段找到某个父 run 下的所有子 run。
# 获取父 run 下的所有子 run
parent_run_id = "abc123"
filter_string = f"tags.mlflow.parentRunId = '{parent_run_id}'"
child_runs = mlflow.search_runs(
experiment_ids=["1"],
filter_string=filter_string
)
print(f"找到 {len(child_runs)} 个子运行")
避坑指南:我曾经在嵌套运行中忘记关闭子 run 的 autolog,导致父 run 也记录了子 run 的指标,数据重复。建议在子 run 内部显式控制 autolog 的作用范围,或者用 mlflow.end_run() 明确结束。
4.4 搜索与查询实验:从海量实验中找到你要的
实验多了之后,手动翻 UI 找实验简直是噩梦。MLflow 的搜索功能可以帮你精准定位。
搜索的核心是 mlflow.search_runs(),它支持多种过滤条件:
- 按实验 ID:
experiment_ids=["1", "2"] - 按参数:
filter_string="params.learning_rate = '0.001'" - 按指标:
filter_string="metrics.accuracy > 0.9" - 按标签:
filter_string="tags.mlflow.runName = '实验A'" - 组合条件:
filter_string="params.model = 'xgboost' and metrics.f1 > 0.85"
# 搜索所有准确率大于 0.9 且使用 XGBoost 的实验
results = mlflow.search_runs(
experiment_ids=["1"],
filter_string="params.model = 'xgboost' and metrics.accuracy > 0.9",
order_by=["metrics.accuracy DESC"],
max_results=10
)
# 结果是一个 DataFrame,可以直接分析
print(results[["run_id", "params.learning_rate", "metrics.accuracy"]])
你想想看,如果手动记录,要找到「准确率 > 0.9 的 XGBoost 实验」得翻多少日志?用 search_runs 一行代码搞定。
我的习惯:每次实验结束后,我会用 search_runs 把最佳实验的 run_id 保存到一个配置文件中。这样后续部署、对比、回滚都直接引用这个 ID,不会搞混。
4.5 知识体系总览
下面这张图概括了本章的核心逻辑:autolog 负责自动采集数据,嵌套运行构建实验的树状结构,搜索查询帮你从树中快速定位目标。
4.6 实战:完整的工作流
最后,我把这些知识点串起来,给你一个可以直接用的实战模板。
import mlflow
import xgboost as xgb
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
# 1. 设置实验
mlflow.set_experiment("iris_classification")
# 2. 开启 autolog
mlflow.autolog()
# 3. 父运行:整体调参任务
with mlflow.start_run(run_name="xgboost_tuning") as parent_run:
mlflow.log_param("task", "XGBoost hyperparameter tuning")
# 准备数据
data = load_iris()
X_train, X_test, y_train, y_test = train_test_split(
data.data, data.target, test_size=0.2, random_state=42
)
# 4. 嵌套子运行:每组参数
for lr in [0.1, 0.01, 0.001]:
with mlflow.start_run(nested=True, run_name=f"lr_{lr}"):
model = xgb.XGBClassifier(
learning_rate=lr,
n_estimators=100,
max_depth=3
)
model.fit(X_train, y_train)
# autolog 会自动记录参数和指标
# 你不需要手动 log 任何东西
# 5. 搜索最佳实验
results = mlflow.search_runs(
experiment_ids=[mlflow.get_experiment_by_name("iris_classification").experiment_id],
filter_string="tags.mlflow.parentRunId = '{}'".format(parent_run.info.run_id),
order_by=["metrics.test_score DESC"]
)
best_run_id = results.iloc[0]["run_id"]
print(f"最佳实验 run_id: {best_run_id}")
这个模板我用了大半年,从数据准备到调参到结果检索,一条龙搞定。你只需要替换模型和数据,就能直接跑起来。
核心要点回顾:
- autolog 自动记录参数、指标、模型,省去手动 log 的繁琐
- 嵌套运行用
nested=True创建父子关系,组织对比实验 - 父子关系通过
mlflow.parentRunId标签维护 - search_runs 支持按参数、指标、标签组合过滤,结果直接分析