10. 实验比较与筛选

10.实验比较与筛选

当实验数量从几个增长到几十个甚至上百个时,如何快速定位有价值的实验结果就成了一个现实问题。DVC 提供了一套完整的实验比较与筛选工具,让我们能够在不污染 Git 历史的前提下,高效地管理这些实验产物。这套工具的核心思路很直接:先看清差异,再决定取舍。

实验差异比较

dvc exp diff 是实验比较的第一把钥匙。它的设计哲学类似于 git diff,但专注于机器学习场景下最关心的两个维度:参数(parameters)和指标(metrics)。这个命令能快速告诉我们,两个实验之间到底发生了什么变化,以及这些变化带来了什么影响。

基本比较模式

默认情况下,不带任何参数的 dvc exp diff 会对比当前 workspace 与最新 Git 提交(HEAD)之间的差异。这在刚跑完一个实验、想立即查看结果变化时特别有用。

$ dvc exp diff
Path         Metric   HEAD      workspace  Change
scores.json  roc_auc  0.915363  0.93345    0.018087
scores.json  grade    B         B-         —

Path         Param         HEAD  workspace  Change
params.yaml  max_features  1500  3000       1500

输出分为两个部分:Metrics 和 Params。每个部分都显示了文件路径、具体的指标或参数名称、两个版本的值,以及数值型数据的变更量。注意那个 grade 指标显示为 "—",这是因为字符串类型的变更无法计算数值差异。

这个表格告诉我们:max_features 参数从 1500 调到了 3000,roc_auc 指标提升了约 0.018,但 grade 从 B 降到了 B-。这种呈现方式让我们能一眼看出参数调整与结果变化之间的关联。

指定实验对比

更常见的场景是对比两个已经完成的实验。假设我们已经运行了多个实验,现在想比较其中两个特定版本:

$ dvc exp diff gluey-leak ochre-dook
Path         Metric  gluey-leak  ochre-dook   Change
scores.json  auc     0.577559    0.51676     -0.060799

Path         Param                   gluey-leak  ochre-dook  Change
params.yaml  featurize.max_features  2000        500        -1500

这里我们比较了名为 gluey-leak 和 ochre-dook 的两个实验。结果显示,gluey-leak 的 max_features 是 2000,ochre-dook 只有 500,对应的 auc 指标前者也高出约 0.06。这种对比让我们能基于数据做出判断:在当前的参数空间中,增加 max_features 似乎能带来性能提升。

灵活的版本指定

exp diff 接受多种版本标识方式:实验名称、Git 提交哈希、分支名、标签名,甚至可以是 Git 修订表达式。比如想对比某个实验与项目早期版本:

$ dvc exp diff gluey-leak 7-ml-pipeline
Path         Metric  gluey-leak  7-ml-pipeline  Change
scores.json  auc     0.577559    None           diff not supported

Path         Param                   gluey-leak  7-ml-pipeline  Change
params.yaml  featurize.max_features  2000        500            -1500
params.yaml  featurize.ngrams        2           1              -1

当对比的版本之间某些指标不存在时,DVC 会显示 "diff not supported" 而不是报错,这种容错设计让探索性分析更加顺畅。

输出格式控制

对于需要进一步处理的场景,JSON 格式是个不错的选择:

$ dvc exp diff --json gluey-leak ochre-dook

这会输出结构化的 JSON 数据,方便被其他工具解析。如果要在 GitHub 的 issue 或 PR 中展示对比结果,Markdown 表格格式更合适:

$ dvc exp diff --md gluey-leak ochre-dook

精度控制也很实用。默认情况下,数值会保留 5 位小数,但可以通过 --precision 调整:

$ dvc exp diff --precision 2 gluey-leak ochre-dook

这在处理大范围数值时能让表格更易读。

过滤显示内容

当项目包含大量参数和指标时,表格会变得臃肿。--param-deps 选项可以只显示作为 stage 依赖的参数,这在关注 pipeline 核心配置时很有用。--all 选项则相反,会显示所有内容,包括没有变化的项。

$ dvc exp diff --all gluey-leak

这个命令会列出所有被追踪的参数和指标,即使某些值在对比中没有变化。这在需要完整审计时很有价值。

实验列表管理

dvc exp list 提供了一个轻量级的实验清单视图。与 exp show 的详细表格不同,这个命令只列出实验名称,格式简洁,适合快速浏览或脚本处理。

本地实验列表

默认行为是列出基于当前 HEAD 的所有实验:

$ dvc exp list
10-bigrams-experiment:
    60b225a [bally-gude]
    b485671 [gluey-leak]
    5f30433 [lurid-lair]

输出显示实验所属的基准提交(10-bigrams-experiment)和具体的实验条目。每个条目包含简短的哈希值和实验名称。这种层级结构让我们能快速了解实验的归属关系。

查看所有提交相关的实验

如果项目有较长的开发历史,实验可能分散在不同的提交上。--all-commits(或 -A)选项能列出仓库中的所有实验:

$ dvc exp list --all-commits

这会遍历整个 Git 历史,找出所有关联的实验。虽然输出可能很长,但配合 grep 等工具能快速定位特定实验。

仅显示实验名称

在自动化脚本中,可能只需要纯粹的实验名称列表。--name-only 选项正是为此设计:

$ dvc exp list --name-only --all-commits
bally-gude
gluey-leak
lurid-lair

每行一个名称,这种格式非常适合管道操作。比如想删除所有包含 "test" 的实验:

$ dvc exp list --name-only --all-commits | grep test | xargs dvc exp remove

远程实验探索

exp list 的强大之处在于能直接查看远程仓库的实验,无需先拉取到本地:

$ dvc exp list --all-commits git@github.com:iterative/example-get-started.git
10-bigrams-experiment:
    bally-gude
    conic-ease

这在团队协作中特别有用。可以先查看同事分享的实验,再决定是否需要拉取到本地。如果本地已经配置了 Git remote,可以直接使用 remote 名称:

$ git remote -v
origin  git@github.com:iterative/example-get-started.git
$ dvc exp list --all-commits origin

这种设计让实验的分享和发现变得异常简单,不需要复杂的配置或额外的服务。

实验结果应用

找到心仪的实验后,下一步通常是将结果应用到 workspace 进行验证或进一步开发。dvc exp apply 就是为此而生。

应用实验到工作区

假设经过比较,我们发现 gluey-leak 实验的 AUC 指标最高,想深入分析其模型和数据:

$ dvc exp apply gluey-leak
Changes for experiment 'gluey-leak' have been applied...

这个命令会将实验的所有文件状态恢复到当前 workspace,包括代码、数据、参数、指标和模型等。DVC 会同时处理 Git 追踪的文件和 DVC 追踪的数据文件,确保 workspace 与实验状态完全一致。

检查应用后的变更

应用完成后,建议立即检查具体发生了哪些变化:

$ git status
On branch master
Changes not staged for commit:
        modified:   dvc.lock
        modified:   params.yaml
        modified:   scores.json

Git 状态显示参数文件和指标文件已被修改。查看具体差异:

$ git diff params.yaml
@@ -3,7 +3,7 @@ prepare:
 featurize:
-  max_features: 1500
+  max_features: 2000
   ngrams: 2

参数确实从 1500 改为了 2000。再看 DVC 追踪的数据文件:

$ dvc status
Data and pipelines are up to date.
$ dvc diff
Modified:
    data/features/
    data/features/test.pkl
    data/features/train.pkl
    model.pkl
files summary: 0 added, 0 deleted, 3 modified, 0 not in cache

dvc status 显示数据与缓存同步,说明实验的数据文件已经正确恢复。dvc diff 则列出了具体哪些数据文件发生了变化。

应用的风险与恢复

需要特别注意,exp apply 会覆盖 workspace 中同路径的文件。如果当前 workspace 有未提交的修改,这些修改会被实验状态覆盖。不过 DVC 提供了恢复机制:

$ git stash
$ git stash apply refs/exps/apply/stash

这个操作会将应用实验前的 workspace 状态保存到 Git stash,并在需要时恢复。但有个限制:如果在 exp apply 后执行了 git commit 或 git checkout 等修改 HEAD 的操作,stash 恢复可能会失败。因此建议在应用实验前确保 workspace 是干净的,或者先提交重要修改。

从应用到持久化

应用实验的最终目的通常是将其纳入正式的版本控制。确认实验结果符合预期后,可以提交到 Git:

$ git add .
$ git commit -m "persist gluey-leak"

提交后,这个实验就变成了项目历史的一部分。此时再查看实验列表:

$ dvc exp show
 ─────────────────────────────────────────────────────────────────────────────
  neutral:**Experiment**   neutral:**Created**        metric:**auc**   param:**featurize.max_features**   param:**featurize.ngrams**
 ─────────────────────────────────────────────────────────────────────────────
  workspace    -          0.69830   2000                     2
  master       04:31 PM   0.69830   2000                     2
 ─────────────────────────────────────────────────────────────────────────────

可以看到 workspace 和 master 分支现在处于同一状态,auc 指标也更新为 0.69830。原来的实验仍然保存在 DVC 的实验存储中,但默认不再显示,因为它们基于的是旧的提交。

实验分支化

有时我们不想直接修改 workspace,而是希望基于某个实验继续开发。这时 dvc exp branch 是更好的选择。

创建实验分支

假设我们想基于 gluey-leak 实验开发新功能:

$ dvc exp branch gluey-leak
Git branch 'gluey-leak-branch' has been created from experiment 'gluey-leak'.
To switch to the new branch run:
        git checkout gluey-leak-branch

DVC 创建了一个名为 gluey-leak-branch 的新 Git 分支,内容完全来自实验。注意 DVC 不会自动切换分支,需要手动执行 git checkout。

分支的灵活性

创建分支后,可以像普通 Git 分支一样操作:

$ git branch
* master
  gluey-leak-branch

$ git checkout gluey-leak-branch
Switched to branch 'gluey-leak-branch'

$ dvc checkout

dvc checkout 确保 DVC 追踪的数据文件也切换到对应版本。现在可以在这个分支上继续修改、提交,甚至推送到远程仓库与团队分享。

分支与应用的对比

exp apply 和 exp branch 的选择取决于使用场景:

  • 想快速查看实验结果,或准备将实验合并到当前分支 → 使用 exp apply
  • 想基于实验继续开发,或需要独立维护实验代码 → 使用 exp branch

两者都不会删除原始实验,所以可以放心尝试。实验分支化后,实验本身仍然保留在 DVC 的实验存储中,直到显式删除。

实验清理

随着时间推移,实验数量会不断累积。虽然 DVC 的实验存储机制很高效,但清理无用实验能让列表更整洁,也减少存储占用。

按名称删除实验

最简单的清理方式是按名称删除特定实验:

$ dvc exp list
master:
        major-mela
        conic-ease
        lucid-lair

$ dvc exp remove conic-ease lucid-lair

$ dvc exp list
master:
        major-mela

可以一次指定多个实验名称,用空格分隔。删除后,实验从列表中消失,但注意这不会删除 DVC 缓存中的数据文件。如果确定不再需要实验相关的数据,可以后续用 dvc gc 清理缓存。

批量清理选项

当实验数量很多时,逐个删除很麻烦。--all(或 -A)选项可以删除所有已运行的实验:

$ dvc exp remove --all

这会清空所有实验,但保留队列中等待运行的实验。如果想清理队列,使用 --queue:

$ dvc exp remove --queue

基于提交范围清理

有时只想清理某个时间段或某个功能分支相关的实验。--rev 和 --num 选项提供了这种能力:

$ dvc exp remove --rev HEAD~5 --num 3

这会删除从 HEAD~5 提交开始,往后 3 个提交范围内的所有实验。--num 接受负值表示包含所有父提交,类似于 git log -n。

反向选择:保留特定实验

--keep 选项提供了反向选择的能力:保留指定实验,删除其余所有:

$ dvc exp list
master:
        major-mela
        conic-ease
        lucid-lair

$ dvc exp remove --keep major-mela

$ dvc exp list
master:
        major-mela

在这个例子中,conic-ease 和 lucid-lair 被删除,只保留了 major-mela。--keep 可以与 --num 和 --rev 组合使用,但不能与 --queue 一起使用。

清理远程实验

如果实验已经推送到远程,也可以从远程删除:

$ dvc exp push myremote

$ dvc exp list myremote
master:
        conic-ease
        urban-sign
        major-mela

$ dvc exp remove -g myremote urban-sign major-mela

$ dvc exp list myremote
master:
        conic-ease

-g(或 --git-remote)选项指定远程仓库。这在实验分享后发现错误或不再需要时很有用,可以避免远程仓库被无用实验污染。

总结

本章介绍了 DVC 实验管理的核心筛选工具:exp diff 让我们看清实验间的差异,exp list 提供快速清单,exp apply 和 exp branch 提供了两种将实验结果纳入工作流的方式,而 exp remove 则帮助保持实验列表的整洁。

这些命令共同构成了一个完整的实验比较与筛选闭环。从运行实验到分析结果,从选择优胜者到清理无用产物,每个环节都有对应的工具支持。更重要的是,这些操作都不会污染 Git 历史,让实验阶段的快速迭代与正式版本的严谨管理可以并存。

下一章将探讨更高级的实验工作流,包括实验队列管理、超参数调优以及与 Hydra 配置的深度集成,这些能力将进一步提升大规模实验管理的效率。