第四章:实验跟踪工具——MLflow、Weights & Biases、Neptune

说实话,我刚开始做 AI 训练那会儿,实验管理基本靠「本地记事本 + 文件夹命名」。跑了一百多个实验后,自己都分不清哪个 config 对应哪个 checkpoint。后来一个前辈跟我说:别省那几分钟,用个实验跟踪工具,省下的时间够你睡好几觉。

嗯,这话我记住了。今天咱们就聊聊三个主流工具:MLflow、Weights & Biases(W&B)和 Neptune。它们都能帮你记录参数、指标、模型版本,但各有脾气。

为什么需要实验跟踪?

你想想看,一个典型训练实验有多少变量?学习率、批次大小、优化器、数据增强方式、网络层数……我见过一个团队,光学习率就试了 30 个不同值。如果不记录,结果出来根本不知道哪个参数起效。

实验跟踪的核心任务就三条:

  • 记录——把每次实验的参数、代码版本、环境依赖、训练指标都存下来
  • 对比——不同实验之间能快速看曲线、表格,找出最优组合
  • 复现——拿到一个实验 ID,就能原样复现当初的结果

下面这张图能帮你快速理解这三个工具的定位差异。

实验跟踪工具对比框架 MLflow Weights & Biases Neptune 参数记录 · 指标可视化 · 模型版本管理 · 团队协作 MLflow 独特优势 开源、自托管 模型注册中心 推理服务一键部署 Python/R/Java SDK W&B 独特优势 云端协作最强 自动超参调优 实验对比面板 报告嵌入交互 Neptune 独特优势 元数据结构化 自定义仪表盘 硬件监控集成 代码版本快照 选择建议:个人/小团队用 MLflow;需要协作/报告用 W&B;企业级结构化实验用 Neptune

MLflow:开源派的首选

MLflow 由 Databricks 打造,2018 年就开源了。它的理念很简单:轻量、开放、语言无关。你可以在本地跑,也可以在云上搭,不收一分钱。

我最早在项目中用 MLflow 是因为客户要求所有实验必须在内网完成,不能把数据传到第三方。MLflow 的 Tracking Server 自己搭,代码里加几行就搞定。

核心组件

  • Tracking —— 记录参数、指标、模型、工件
  • Projects —— 将代码打包成可复用的运行环境
  • Models —— 模型注册与推理服务
  • Registry —— 模型版本管理与阶段标注

看个最简例子:

import mlflow

# 开启一次实验
with mlflow.start_run():
    # 记录参数
    mlflow.log_param("learning_rate", 0.001)
    mlflow.log_param("batch_size", 64)
    
    # 模拟训练循环
    for epoch in range(10):
        loss = 0.5 ** (epoch + 1)
        mlflow.log_metric("loss", loss, step=epoch)
    
    # 保存模型
    mlflow.pytorch.log_model(model, "model")
    
    # 记录额外文件
    mlflow.log_artifact("config.yaml")

我个人习惯把模型注册到 MLflow Registry,然后用 mlflow.models.serve 一键启动 REST API。这在小团队里非常方便——不需要额外搭推理框架。

小技巧:如果你的实验在 Docker 容器里跑,记得挂载 mlruns 目录到宿主机,否则容器销毁后实验记录就丢了。

Weights & Biases:协作与可视化之王

如果你不介意把数据放到云端(有自托管方案),W&B 在可视化方面可以说是碾压级别的。它的实验面板可以同时对比几十个实验,曲线实时刷新,还能自动帮你做超参重要性分析。

第一次用 W&B 时,我跑了 20 个超参组合,它自动生成了平行坐标图和相关性热图,我一眼就看出「学习率 1e-4 配合 AdamW 效果最好」。这在我过去手动对比时要花大半天。

接入方式几乎一样简单:

import wandb

# 初始化,自动接手超参和代码版本
wandb.init(project="my-awesome-project", config={
    "learning_rate": 0.001,
    "batch_size": 64,
    "epochs": 10,
})

# 训练循环
for epoch in range(10):
    loss = 0.5 ** (epoch + 1)
    # 记录指标,自动生成图表
    wandb.log({"loss": loss, "accuracy": 1 - loss})
    
    # 偶尔记录图像/直方图
    if epoch % 3 == 0:
        wandb.log({"predictions": wandb.Image(pred_img)})

# 结束运行
wandb.finish()

注意:W&B 免费版有项目数和存储限制。我们团队一次百万级图片训练,日志膨胀到几十 GB,不得不升级到专业版。如果预算敏感,提前评估日志量。

W&B 还有一个杀手级功能:Sweeps。你只需要定义一个搜索空间配置:

program: train.py
method: bayes
metric:
  name: val_loss
  goal: minimize
parameters:
  learning_rate:
    min: 0.0001
    max: 0.01
    distribution: log_uniform
  dropout:
    values: [0.1, 0.2, 0.3, 0.5]
  optimizer:
    values: ["adam", "sgd"]

然后一个命令 wandb sweep <config.yaml>,它会自动调度多个 agent 并行搜索最优超参。我用它一次跑了 80 个实验,中间去睡了个觉,第二天就有了最优组合。

Neptune:结构化实验管理的瑞士军刀

Neptune 比较晚进入市场,但它在「结构化」方面做得特别细致。它把实验记录看作一个元数据图谱,你可以给实验打标签、添加自定义属性、关联 Git commit、甚至跟踪数据集的版本。

我前年做一个图像分割项目,需要同时跟踪输入图片的分辨率、GPU 显存使用率、模型参数量、和每个类别的 IoU。Neptune 的自定义仪表盘让我把这些信息都放在一个页面,客户看了直呼专业。

基本用法:

import neptune

# 创建运行实例
run = neptune.init_run(
    project="my-team/my-project",
    api_token="your_api_token",
)

# 记录参数
run["hyperparameters"] = {
    "lr": 0.001,
    "batch_size": 32,
    "model": "UNet"
}

# 训练循环
for epoch in range(10):
    loss = 0.5 ** (epoch + 1)
    run["metrics/train/loss"].append(loss)
    run["metrics/val/iou"].append(0.8 - 0.1 * epoch)

# 记录代码版本
run["source_code"].upload("train.py")

run.stop()

Neptune 的强项在于可查询。你可以用类似 SQL 的语法:run["hyperparameters/lr"] > 0.001 and run["metrics/val/iou"] > 0.7 来筛选实验。我曾经用这个功能从 500 个实验中筛出了所有「大学习率 + 低 dropout」的组合,帮团队快速定位问题。

横向对比:怎么选?

维度 MLflow Weights & Biases Neptune
部署方式 完全自托管 or 托管版 云端为主,有自托管版 云端为主,有私有云版
可视化强度 ★★☆☆☆ (基本曲线) ★★★★★ (交互面板) ★★★★☆ (自定义仪表盘)
超参调优 需配合 Optuna 内置 Sweeps 内置阈值/搜索
模型注册 内置 Registry Model Registry (付费) 内置 Registry
团队协作 通过共享 SQL 数据库 原生协作 + 报告共享 原生团队 + 权限管理
代码/数据溯源 需手动记录 commit 自动记录 Git + DVC 集成 自动 Git + 数据版本快照
开源 是 (Apache 2.0) SDK 开源,后端闭源 SDK 开源,后端闭源
启动速度 极快 (纯 Python 无网络) 较快 (需网络验证) 较慢 (初始化检查多)

我的选择策略

说实话,没有万能工具。我一般按场景来:

  • 本地研究、快速验证 → MLflow。它够轻,不会干扰训练流程,而且完全离线。
  • 团队协作、需要汇报 → W&B。它的报告功能能直接把实验曲线嵌入到网页里,老板看了直点头。
  • 企业级合规、多维度元数据管理 → Neptune。它能把模型、数据、代码、指标都关联成图,审计时一查一个准。

核心原则:无论选哪个,第一件事是统一接口。我在团队里强制要求所有训练脚本必须同时记录参数、指标和模型文件,哪怕只跑一次实验。这样后续做版本锁定和复现时,才能从工具里直接拉出完整上下文。

版本锁定与实验记录的联动

别忘了,我们这章的主题是「实验跟踪工具」,它的终极目标是服务于版本锁定与复现。所以,我建议在每个实验记录里必须包含以下信息(无论用哪个工具):

# 理想实验记录结构
experiment:
  id: "exp_20250321_1245"
  timestamp: 2025-03-21 12:45:00
  git_commit: "a3b2c1d"
  dataset_version: "v2.1"
  environment:
    python: "3.10.12"
    torch: "2.1.0"
    cuda: "12.1"
  parameters:
    learning_rate: 0.001
    batch_size: 64
    optimizer: "AdamW"
  metrics:
    train_loss: 0.023
    val_accuracy: 0.971
  artifacts:
    model: "s3://bucket/v1/model.pt"
    config: "configs/exp_1245.yaml"
    logs: "runs/exp_1245/tensorboard"

你想想看,半年后你要复现这个实验,只需要拿出实验 ID,工具会自动拉出当时的所有参数、代码版本、依赖环境。这才是真正意义上的「一键复现」。

经验分享:我曾经接到一个紧急任务——复现三个月前客户认可的 baseline。客户报了个实验编号,我用 W&B 直接恢复了当时的 config 和模型,半小时内跑出了完全一致的精度曲线。老板愣了半天,说「这工具钱花得值」。

好了,实验跟踪工具就讲到这里。记住一句话:记录越完整,复现越轻松。别偷懒,每次实验都多花三十秒把参数和指标写进工具,将来你会感谢自己的。