3. 数据版本控制实战

3.数据版本控制实战

数据版本控制是 DVC 的核心功能,也是整个工具链中最常用的操作集合。在前两章理解了基本概念之后,现在需要掌握具体的实战技巧。这些命令构成了日常工作中最频繁使用的工具集,就像 Git 中的 add、status、checkout、commit 一样,构成了一个完整的工作闭环。

数据追踪 add 命令

dvc add 是整个数据版本控制流程的起点。这个命令告诉 DVC 哪些文件或目录需要被纳入版本管理。与 Git 直接存储文件内容不同,DVC 会创建轻量级的 .dvc 文件作为数据指针,而将实际数据存储在缓存中。

基本用法

使用 dvc add 非常简单,只需要指定要追踪的目标路径即可。目标可以是单个文件、整个目录,甚至是已经追踪的目录中的特定路径。

$ dvc add data.xml

执行这条命令后,DVC 会立即执行几个关键操作:首先计算文件的哈希值,然后将文件内容移动到 .dvc/cache 目录下,接着在当前位置创建一个链接指向缓存中的实际数据,最后生成一个 data.xml.dvc 文件。这个 .dvc 文件就是数据的"身份证",包含了追踪所需的全部元信息。

追踪单个文件

让我们看看追踪单个文件时会发生什么。假设有一个名为 data.xml 的数据文件:

$ dvc add data.xml

完成后,工作区会出现一个新的 data.xml.dvc 文件。查看其内容:

outs:
  - md5: 6137cde4893c59f76f005a8123d8e8e6
    path: data.xml

这个 YAML 文件结构清晰明了。outs 字段表示输出列表,每个输出包含哈希值和路径。DVC 使用 MD5 算法计算文件哈希,这个哈希值决定了文件在缓存中的存储位置。缓存目录采用两级结构,前两个字符作为子目录名,剩余字符作为文件名。例如,上述哈希对应的缓存路径是 .dvc/cache/files/md5/61/37cde4893c59f76f005a8123d8e8e6

需要特别注意,DVC 默认会将原始文件添加到 .gitignore 中,防止 Git 追踪大文件。这是自动化处理的过程,无需手动干预。如果项目初始化时使用了 dvc init --no-scm,则不会添加 .gitignore 规则。

追踪整个目录

在实际项目中,数据集通常以目录形式组织。比如图像分类任务可能有如下结构:

pics
├── train
│   ├── cats [大量图片文件]
│   └── dogs [大量图片文件]
└── validation
    ├── cats [更多图片文件]
    └── dogs [更多图片文件]

追踪整个目录同样简单:

$ dvc add pics

与单个文件不同,目录追踪不会为每个文件生成单独的 .dvc 文件。DVC 只创建一个 pics.dvc 文件,其内容略有特殊:

outs:
  - md5: ce57450aa92ab8f2b957c24b0df73edc.dir
    path: pics

注意到哈希值后面多了 .dir 后缀。这表示该输出是一个目录,DVC 在缓存中存储了一个特殊的 JSON 文件,记录了目录内所有文件的哈希信息。这种设计既节省了元文件数量,又保留了细粒度控制的能力。

目录追踪的优势在于,后续操作可以针对目录中的特定文件。例如 dvc pushdvc pulldvc getdvc import 等命令都支持精确到文件级别的操作,即使整个目录被作为一个整体追踪。

常用选项

dvc add 提供了几个实用选项来应对不同场景。

--no-commit 选项允许追踪文件但不立即提交到缓存。这在处理临时数据或进行快速迭代时很有用。数据会被添加到 .dvc 文件中,但内容不会进入缓存,直到执行 dvc commit 完成提交。

--glob 选项支持模式匹配,可以一次性追踪符合特定规则的文件集合。例如 dvc add --glob "*.csv" 会追踪所有 CSV 文件。

--to-remote 选项直接将数据上传到远程存储,而不经过本地缓存。这在本地磁盘空间有限但网络带宽充足的情况下非常实用。

状态检查 status

了解项目当前状态是版本控制的核心需求。DVC 提供了两个互补的状态检查命令:dvc status 用于检查流水线状态,dvc data status 专注于数据文件状态。

查看工作区状态

最基本的用法是不带任何参数执行:

$ dvc status

这个命令会扫描所有 dvc.yaml.dvc 文件,重建流水线结构,然后比较定义的输出与实际工作区中的数据。如果一切同步,会显示 "Data and pipelines are up to date"。

当检测到变化时,输出会明确指出问题所在。例如:

$ dvc status
featurize:
	changed outs:
		modified:           data/features
train:
	changed outs:
		not in cache:       model.pkl

这个输出告诉我们 featurize 阶段的输出 data/features 被修改了,而 train 阶段的输出 model.pkl 不在缓存中。状态描述包括 "modified"、"not in cache"、"deleted" 等多种情况,帮助快速定位问题。

检查缓存状态

dvc data status 提供了更详细的数据状态视图,类似于 git status 但专注于 DVC 追踪的文件。默认输出可能包含多个部分:

$ dvc data status
Not in cache:
  (use "dvc fetch <file>..." to download files)
        data/data.xml

DVC committed changes:
  (git commit the corresponding dvc files to update the repo)
        modified: data/features/

DVC uncommitted changes:
  (use "dvc commit <file>..." to track changes)
  (use "dvc checkout <file>..." to discard changes)
        deleted: model.pkl

输出分为几个类别:缓存缺失的文件、已提交到 DVC 但尚未 Git 提交的变化、已追踪但尚未提交到 DVC 的变化。这种分层展示让数据管理状态一目了然。

--granular 选项可以显示目录内部的文件级变化:

$ dvc data status --granular
DVC committed changes:
        added: data/features/foo

这在调试大型数据集时特别有用,能精确知道哪些文件发生了变化。

远程存储状态

在团队协作中,经常需要检查本地缓存与远程存储的同步状态。使用 --remote--cloud 选项可以比较缓存与远程存储的差异:

$ dvc status --remote mystorage
...
	new:      data/model.p
	new:      data/eval.txt

输出显示这些文件存在于本地缓存但尚未推送到远程。这提醒我们需要执行 dvc push 来共享数据。

对于导入的数据,dvc status 还能检测源数据是否有更新。如果原始数据源发生变化,会显示 "update available" 提示,可以通过 dvc update 同步最新版本。

版本切换 checkout

版本切换是数据版本控制的核心价值所在。DVC 的 checkout 命令与 Git 的 checkout 配合使用,实现数据和代码的同步切换。

切换完整版本

典型的工作流程是先切换 Git 版本,然后同步 DVC 数据:

$ git checkout baseline-experiment
$ dvc checkout

Git checkout 会更新 .dvc 文件和 dvc.lock 文件,这些文件包含了数据版本的指针信息。但 Git 不会触碰实际的数据文件,因为数据存储在 .gitignore 中。dvc checkout 读取这些指针文件,从缓存中恢复对应版本的数据到工作区。

这个过程非常高效。DVC 使用 reflink、硬链接或符号链接技术,避免复制大文件内容。即使数据文件达到数十 GB,切换版本也能在瞬间完成。

切换特定文件

有时只需要切换某个特定数据文件,而不改变代码版本。这可以通过指定目标实现:

$ git checkout baseline-experiment -- dvc.lock
$ dvc checkout model.pkl

第一条命令只更新 dvc.lock 文件,第二条命令根据该文件中的哈希信息恢复 model.pkl 的特定版本。这种方式允许灵活组合不同版本的代码和数据。

甚至可以切换目录中的特定文件:

$ dvc checkout data/features/test.pkl

即使 data/features 是整个目录被追踪的,也可以精确到文件级别进行操作。

自动化切换

手动执行两个命令容易忘记,DVC 提供了自动化方案。dvc install 命令会安装 Git 钩子,自动在 git checkout 后执行 dvc checkout

$ dvc install

安装后,.git/hooks/post-checkout 文件会被创建,内容通常是简单的脚本调用 dvc checkout。这样切换 Git 分支时,数据会自动同步到对应版本,无需手动干预。

类似地,dvc install 还会安装 pre-commit 钩子,在提交前检查 DVC 状态,以及 pre-push 钩子,在推送代码时自动推送数据。这些自动化机制大大减少了操作失误的可能性。

变更提交 commit

dvc commit 用于将工作区的数据变更正式记录到 DVC 中。这个命令在多种场景下都发挥着重要作用。

基本提交

当修改了已被 DVC 追踪的文件后,需要提交变更:

$ echo "new data" >> data.csv
$ dvc commit data.csv.dvc

这会重新计算文件哈希,更新 .dvc 文件,并将新内容存入缓存。与 dvc add 不同,commit 不需要指定目标数据文件,只需要 .dvc 文件即可。

如果省略目标,dvc commit 会提交所有已追踪文件的变更:

$ dvc commit

这在批量更新多个数据文件时非常方便。

处理未完成的数据

--no-commit 选项在 dvc adddvc repro 中提供了延迟提交的能力。这在进行快速迭代时特别有用,可以避免缓存中充满中间结果。

例如,在调整模型参数时:

$ dvc repro --no-commit

这条命令会执行流水线,但不会将结果存入缓存。可以多次运行不同参数配置,DVC 会记录每次的变化,但缓存保持不变。当找到最佳配置后,再执行:

$ dvc commit

一次性将所有有价值的变更提交到缓存。这样既节省了存储空间,又保持了工作区的整洁。

强制提交

有时需要强制提交,即使依赖或输出没有变化。-f--force 选项允许这样做:

$ dvc commit -f

这在处理某些边界情况时很有用,比如修复了缓存中的损坏文件,或者需要重新链接文件时。

另一个常见场景是修改了依赖文件(如源代码),但修改不影响输出结果。例如添加了注释或调整了代码格式。DVC 会检测到依赖变化并提示需要重现流水线,但如果确定修改不影响结果,可以直接提交:

$ dvc commit
dependencies ['src/train.py'] of 'train.dvc' changed.
Are you sure you commit it? [y/n] y

这样既避免了耗时的重现过程,又保持了追踪信息的准确性。

忽略文件 dvcignore

.dvcignore 文件机制让 DVC 可以像 Git 忽略文件一样,排除不需要追踪的内容。这对于提高操作性能和保持工作区整洁非常重要。

基本语法

.dvcignore 文件采用与 .gitignore 相同的模式匹配规则。每行一个模式,支持通配符和路径匹配。

创建 .dvcignore 文件:

$ echo "temp/*.tmp" >> .dvcignore
$ echo "logs/" >> .dvcignore

这会忽略 temp 目录下所有 .tmp 文件以及 logs 目录的所有内容。

可以使用 dvc check-ignore 验证忽略规则:

$ dvc check-ignore temp/file.tmp
temp/file.tmp

被忽略的文件对 DVC 来说就像不存在一样,不会进入缓存,也不会出现在状态检查中。

忽略文件的影响

.dvcignore 影响已追踪的目录时,DVC 会将其视为文件删除。例如,追踪 data 目录后,将其中某个文件加入 .dvcignore

$ echo "data/file1" >> .dvcignore
$ dvc status
data.dvc:
	changed outs:
		modified:           data

DVC 认为 data 目录被修改了,因为其中的文件被有效删除。提交变更后,被忽略的文件不会进入缓存。

修改被忽略的文件不会影响 DVC 状态:

$ echo "changed" >> data/file1
$ dvc status
Data and pipelines are up to date.

这验证了 DVC 完全忽略了该文件。

实际应用场景

.dvcignore 在多种场景下都很有用。处理大型数据集时,可以忽略临时文件或元数据文件,避免不必要的缓存操作。在跨平台协作时,可以忽略系统特定文件如 .DS_StoreThumbs.db

需要注意的是,在 DVC 追踪的目录内部放置 .dvcignore 文件会导致错误。忽略规则应该放在项目根目录或父目录中统一管理。

另一个重要警告是:dvc reprodvc exp run 可能会删除被忽略的文件。如果这些文件不是流水线输出产生的,可能会永久丢失。因此,重要的数据不应依赖 .dvcignore 排除,而应该明确追踪或放在追踪目录之外。


本章介绍了 DVC 数据版本控制的核心实战命令。dvc add 让追踪数据变得简单高效,status 系列命令提供了全面的状态洞察,checkout 实现了快速版本切换,commit 完成了变更的正式记录,而 .dvcignore 则提供了灵活的过滤机制。

这些命令构成了数据版本控制的基础工作流。熟练掌握它们后,就能在日常项目中自如地管理数据集和模型文件。下一章将探讨远程存储配置和团队协作流程,了解如何将本地版本控制扩展到分布式环境,实现数据的艺术级共享与同步。