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 push、dvc pull、dvc get、dvc 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 add 和 dvc 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_Store 或 Thumbs.db。
需要注意的是,在 DVC 追踪的目录内部放置 .dvcignore 文件会导致错误。忽略规则应该放在项目根目录或父目录中统一管理。
另一个重要警告是:dvc repro 和 dvc exp run 可能会删除被忽略的文件。如果这些文件不是流水线输出产生的,可能会永久丢失。因此,重要的数据不应依赖 .dvcignore 排除,而应该明确追踪或放在追踪目录之外。
本章介绍了 DVC 数据版本控制的核心实战命令。dvc add 让追踪数据变得简单高效,status 系列命令提供了全面的状态洞察,checkout 实现了快速版本切换,commit 完成了变更的正式记录,而 .dvcignore 则提供了灵活的过滤机制。
这些命令构成了数据版本控制的基础工作流。熟练掌握它们后,就能在日常项目中自如地管理数据集和模型文件。下一章将探讨远程存储配置和团队协作流程,了解如何将本地版本控制扩展到分布式环境,实现数据的艺术级共享与同步。