版本控制基础:Git和DVC

说起版本控制,你可能第一反应就是 Git。没错,Git 在我们这行绝对是标配。但做 AI 训练,光有 Git 还不够。你想想看,一个大模型动辄几十 GB,Git 根本管不了。这就引出了今天的主角——Git 和 DVC。

我个人习惯把这两者看作一对搭档。Git 管代码,DVC 管数据和模型。各司其职,缺一不可。我带团队时发现,很多人只记命令,不理解底层逻辑。结果一出问题就抓瞎。今天咱们就把它讲透。

Git + DVC 协同工作流程 代码仓库 数据仓库 训练脚本 数据集 参数配置 模型权重 DVC 记录版本指针 Git 管理代码 + 配置 DVC 追踪数据变化

为什么非要用 Git?

Git 的核心价值,说白了就是「后悔药」。做实验改参数,加新模块,改训练策略…… 改来改去,你怎么知道哪个版本跑出了最好结果?没有 Git,你就只能在文件后面加 v1、v2、final、final_v3 这种后缀。别笑,我真见过。

我在一个老项目中遇到过这种事:一个关键实验跑了三天,结果因为没记版本,到汇报时谁也说不清用的是哪份代码。最后只能重跑。嗯,从那以后我强制所有人必须用 Git 打 tag 做版本锁定。

Git 的基本工作流

你不需要背所有命令。记住最核心的几个就好:

git init                 # 初始化仓库
git add .                # 暂存修改
git commit -m "msg"      # 提交版本
git tag v1.0.0           # 打标签锁定版本
git checkout v1.0.0      # 切换到指定版本

关键技巧:每次训练前先 git tag,训练后不改代码直接打新 tag。这样每个实验都有唯一对应版本。

我觉得大多数人学 Git 时都犯过一个错:只记 add 和 commit,忽略了分支和 tag。但你想想看,要复现一个三个月前的实验结果,没有 tag 怎么找?翻 commit log 一条条看?那不现实。

Git 的底层逻辑——懂这点就够了

Git 存储的是文件的快照。每次 commit 都是一张照片。不是记录差异,是拍下整个目录的样子。所以每次 commit 都保存了代码全貌。这让你可以任意跳转版本。

这里要注意一个「坑」:Git 对二进制文件很无力。数据集、模型权重这些文件,Git 把你仓库撑爆。我亲眼见过有人把 50GB 的数据集推到 Git 仓库,结果拉代码要等半天。

避坑指南:在 .gitignore 里加上 *.pth、*.h5、data/ 这些规则。数据文件千万别进 Git。

# .gitignore 示例
*.pth
*.pt
*.h5
*.onnx
*.bin
data/
saved_models/
__pycache__/

Git 的常用场景和命令

场景 命令 说明
查看历史 git log --oneline 简洁版提交记录
回滚文件 git checkout -- file 撤销未暂存的修改
回滚提交 git reset --hard HEAD~1 退回上一个版本
创建分支 git branch exp-01 开新实验分支
查看差异 git diff v1.0.0 v2.0.0 对比两版本改了啥

我曾经吃过这个亏:用 git reset --hard 之前没看清楚,直接把一周的工作丢了。建议多用 git stash 暂存。命令:git stash save "xxx"。

为什么要加上 DVC?

Git 管不了大文件这件事,不是 Git 的错。它本就不是为这个设计的。做 AI 训练,你需要数据版本控制。DVC 就是干这个的。

DVC 的思路很巧妙:它不存文件本身,而是存文件的「指针」和「哈希值」。你把大型数据放到云存储或 NAS 上,DVC 只记下它在哪、哈希是多少。代码库就只存了一个 tiny 指针文件。每次 git commit、git checkout,这个指针文件跟着走,对应的数据文件自动同步。

核心概念:DVC 的 .dvc 文件只有几百字节,相当于一个书签。Git 管理书签,DVC 管理真正的书。

DVC 的基本使用流程

第一步:初始化 DVC。

pip install dvc
dvc init                    # 在Git仓库内初始化
git add .dvc/
git commit -m "init dvc"

第二步:添加数据文件,告诉 DVC 你盯上它了。

dvc add data/train.csv     # 生成 data/train.csv.dvc
git add data/.gitignore data/train.csv.dvc
git commit -m "track train.csv with dvc"

第三步:设置远程存储。可以是 S3、GCS、NAS、甚至本地硬盘。

dvc remote add -d myremote /mnt/nfs/shared
dvc push                    # 上传实际数据

第四步:别人想复现,只需要 pull。

git pull
dvc pull                    # 自动下载对应版本的数据

提示:DVC 的远程存储可以配置权限隔离。生产环境里,我一般把数据放在公司内部的对象存储上,不让数据出内网。

Git 和 DVC 如何协同工作?

现在你看这张协作图就明白了。Git 管代码仓库,DVC 管数据仓库。每一次实验,Git 锁定代码 + 配置的版本,DVC 锁定数据和模型权重的版本。两者结合,你就能精准复现任何一次训练。

工作流就是:

  • 改代码 → git add + git commit
  • 改数据 → dvc add + git commit (提交 .dvc 文件)
  • 跑完训练 → dvc add 模型权重 + git commit (记录权重版本)
  • 打标签 → git tag exp-v3.2
  • 回到这个实验 → git checkout exp-v3.2 + dvc checkout

你看,是不是很顺?一旦跑通这个流程,你就不再怕「忘了我用的哪个数据集」这种蠢问题了。我在项目中落地这个流程后,团队少扯了至少一半的皮。

常见错误和避坑指南

  • 别把数据集放在 Git 管理的目录外:DVC 只能在 Git 仓库内追踪文件。数据文件得放在项目目录下。
  • 别忘记 push:git push 和 dvc push 是两码事。代码提交了,数据没上传,别人还是拉不到。
  • 别忘了 .gitignore:数据文件排到 Git 里,不仅冗余,还会让 dvc pull 时出现冲突。
  • 小心 .dvc 文件的冲突:多人同时修改同一个数据文件,合并 .dvc 文件时可能冲突。我遇到过,解决方法是手动确认哈希值。

我曾经在项目中没做 dvc push,结果第二天同事 dvc pull 只拿到一个旧版本。从那以后我写了个脚本,每次 git push 之前自动执行 dvc push。再也不忘了。

好,现在你应该清楚 Git 和 DVC 的角色分工了。一个管代码,一个管数据。两者配合,你才能做到真正的版本锁定。后面的章节会讲怎么把这些概念落实到训练参数的锁定上,那时候你会发现,今天的铺垫非常必要。