3. 实验跟踪入门
在金融建模中,一次“跑通模型”并不等于一次“可复现的实验”。你可能用同一份行情数据、同一套特征工程,却因为随机种子、学习率、滑窗长度或手续费假设不同,得到完全不同的回测曲线。MLflow 实验跟踪要解决的核心问题,就是把这些“看不见的差异”变成可查询、可比较、可追溯的记录。本节从最基础的两个概念——Experiment 与 Run——出发,带你完成一次完整的记录动作。
核心原理:Experiment 是容器,Run 是执行实例
很多初学者会把 Experiment 理解成“一次实验”,这是最常见的认知偏差。在 MLflow 中,Experiment 是一个逻辑分组容器,通常对应一个业务问题或一个研究方向,例如“A股多因子选股”“信用违约预测”“期权波动率曲面拟合”。而 Run 是这个容器内的一次具体执行,对应一次训练、一次回测或一次参数扫描中的单个组合。
因此,一个 Experiment 下可以包含成百上千个 Run。每个 Run 拥有独立的 run_id、开始时间、结束时间、状态(RUNNING / FINISHED / FAILED / KILLED),并挂载四类核心信息:参数(Params)、指标(Metrics)、工件(Artifacts)和标签(Tags)。参数是输入,指标是输出,工件是产物,标签是检索维度。理解这四者的分工,比记住 API 名称更重要。
一句话记忆:Experiment 回答“我在研究什么问题”,Run 回答“我这一次具体怎么做的、结果如何”。
方法流程:一次完整的记录动作
下面以“多因子选股回测”为例,演示从创建 Experiment 到记录 Run 的标准流程。假设你已经在本地或服务器上配置好 MLflow Tracking Server,环境变量 MLFLOW_TRACKING_URI 指向该服务。
- 设置或创建 Experiment。如果实验已存在则复用,不存在则创建,避免每次运行都产生新实验。
- 启动 Run。使用
with mlflow.start_run()上下文管理器,确保异常时 Run 状态能正确标记为 FAILED。 - 记录参数。把学习率、滑窗长度、手续费率、随机种子等输入写入 Params。
- 记录指标。把夏普比率、最大回撤、年化收益、IC 均值等评估结果写入 Metrics。
- 记录工件。把模型文件、回测净值曲线图、特征重要性表等写入 Artifacts。
- 打标签。用 Tags 标记阶段、负责人、数据版本,便于后续检索。
import mlflow
import mlflow.sklearn
mlflow.set_tracking_uri("http://127.0.0.1:5000")
mlflow.set_experiment("A股多因子选股")
with mlflow.start_run(run_name="lr005_win20") as run:
# 1. 记录参数
mlflow.log_param("learning_rate", 0.05)
mlflow.log_param("window", 20)
mlflow.log_param("commission", 0.0003)
mlflow.log_param("seed", 42)
# 2. 训练与回测(此处省略具体建模代码)
sharpe, max_drawdown, annual_return = 1.65, -0.12, 0.23
# 3. 记录指标
mlflow.log_metric("sharpe", sharpe)
mlflow.log_metric("max_drawdown", max_drawdown)
mlflow.log_metric("annual_return", annual_return)
# 4. 记录工件
mlflow.log_artifact("reports/equity_curve.png")
mlflow.sklearn.log_model(model, "model")
# 5. 打标签
mlflow.set_tag("stage", "best")
mlflow.set_tag("owner", "quant_team")
print("run_id:", run.info.run_id)
运行结束后,打开 MLflow UI,你会在“A股多因子选股”实验下看到这条 Run,并能直接对比不同 Run 的指标列。这就是实验跟踪最直接的收益:把散落在终端和笔记本里的结果,变成一张可排序的表格。
案例场景:参数扫描中的 Run 组织
假设你要对学习率和滑窗长度做网格搜索,共 3×3=9 个组合。此时不要创建 9 个 Experiment,而应在同一个 Experiment 下创建 9 个 Run。每个 Run 记录自己的参数组合和对应指标,最后在 UI 中按 sharpe 降序排列,即可快速定位最优组合。若把每个组合都建成独立 Experiment,实验列表会迅速膨胀,失去分组意义。
| 记录对象 | API 方法 | 金融场景示例 | 是否可覆盖 |
|---|---|---|---|
| 参数 Params | log_param | learning_rate=0.05 | 不可覆盖 |
| 指标 Metrics | log_metric | sharpe=1.65 | 可多次记录形成序列 |
| 工件 Artifacts | log_artifact | equity_curve.png | 同名可覆盖 |
| 标签 Tags | set_tag | stage=best | 可覆盖 |
误区与复盘
误区一:把 Experiment 当 Run 用。每次调参都新建 Experiment,导致实验列表爆炸,无法横向对比。正确做法是同一研究方向共用一个 Experiment。
误区二:只记指标不记参数。看到 sharpe=1.65 却不知道对应哪组超参数,实验记录等于白做。参数和指标必须成对出现。
误区三:工件路径混乱。把大文件直接写进代码目录再上传,容易造成路径冲突。建议统一使用 reports/、models/ 等约定目录。
复盘建议:每次 Run 结束后,检查三件事——参数是否完整、指标是否包含风险维度(如最大回撤)、工件是否可复现。金融实验尤其要记录数据版本和手续费假设,否则回测结果无法复现。