4. 分支策略与实验管理:用 Git 分支管好你的每个想法

做可复现实验,最怕什么?

我个人觉得,最怕的是「改完代码,跑了个结果,然后忘了刚才改了什么」。你想想看,一个实验项目,少说几十个参数组合,多则上百个模型变体。如果没有一套清晰的分支管理策略,最后就是一团乱麻。

这一章,我们就聊聊怎么用 Git 分支来管理不同实验方案,再用 Tag 把关键结果钉死。嗯,这套方法我在好几个深度学习项目里都用过,效果不错。

4.1 为什么实验需要分支管理?

很多同学做实验,习惯直接在 master 或 main 分支上改代码。跑完一个实验,改几个参数,再跑一次。结果呢?

  • 想回退到上一个实验配置,发现已经找不到原来的代码了。
  • 两个实验方案并行推进,代码改来改去,冲突不断。
  • 好不容易跑出一个好结果,却记不清当时用了哪些超参数。

说白了,这就是把实验记录和代码管理混在一起了。Git 分支天然就是干这个的——每个分支代表一个独立的实验方案,互不干扰。

核心原则:每个实验方案一个分支,每个关键结果一个 Tag。

分支管「过程」,Tag 管「结果」。

4.2 分支策略:给每个实验一个独立空间

我建议你采用一种简单的「三层次分支模型」。别搞太复杂,实验管理不是软件发布,够用就行。

4.2.1 主分支:main(或 master)

这是项目的稳定基线。只有经过验证的、可复现的实验配置才会合并到这里。说白了,main 分支上的代码,应该能随时跑出一个已知的好结果。

4.2.2 实验分支:exp/<实验名称>

每个实验方案,从 main 切出一个独立分支。命名规则我习惯用 exp/ 前缀,后面跟上实验的简短描述。

# 从 main 创建实验分支
git checkout -b exp/lr-scheduler-comparison main

# 或者从某个 Tag 创建(后面会讲 Tag)
git checkout -b exp/attention-variants v1.0-baseline

在这个分支上,你可以随意修改代码、调参数、跑实验。就算改坏了,也不会影响其他分支。

4.2.3 特性分支:feat/<功能名称>

有时候,实验需要引入新的功能模块(比如一个新的数据增强方法)。这时候我会先开一个 feat/ 分支,开发测试完成后,再合并到实验分支。

一个小技巧:实验分支和特性分支分开管理。实验分支只管「调参数、改配置」,特性分支只管「加功能、修 bug」。这样回溯的时候,一眼就能看出改动的原因。

4.3 用 Tag 钉住关键实验结果

分支是流动的,你可以在上面不断提交。但实验跑出好结果的那一刻,需要被「钉死」。Git 的 Tag 就是干这个的。

我一般会在以下时机打 Tag:

  • 基线结果:第一个可复现的 baseline 结果。
  • 最佳结果:当前实验分支上最好的验证集指标。
  • 提交版本:用于生成中所有图表的代码版本。
  • 重大发现:某个实验现象第一次被观察到的时候。
# 打一个轻量级 Tag(只有名称)
git tag v1.0-baseline

# 打一个附注 Tag(推荐,包含作者、日期、说明)
git tag -a v2.0-best-result -m "ResNet50 + CosineAnnealing, val_acc=76.3%"

# 推送到远程
git push origin --tags

注意:Tag 一旦推送,尽量不要删除或修改。它是实验历史的锚点。我曾经因为删了一个 Tag,导致合作者找不到当时用的代码版本……从那以后,我再也不敢乱动 Tag 了。

4.4 分支与 Tag 的配合使用

光有分支和 Tag 还不够,你得把它们串起来用。下面这张图展示了我的典型工作流:

分支与 Tag 配合实验管理流程 main 分支 v1.0-baseline exp/lr-scheduler v2.0-lr-best exp/attention-v2 v3.0-attn-best v4.0-merged 流程说明: 1. 从 main 基线(v1.0)创建实验分支 → 2. 在实验分支上调参跑实验 → 3. 跑出好结果后打 Tag 记录 4. 将最佳实验分支合并回 main → 5. 在 main 上打合并 Tag,作为新的基线

4.5 实验记录与 Git 的联动

光有代码版本还不够,你还需要记录每次实验的配置和结果。我习惯在实验分支的根目录放一个 experiments/ 文件夹,里面按日期或实验编号存放记录。

experiments/
├── 2025-01-10_lr-scheduler/
│   ├── config.yaml          # 实验配置
│   ├── results.json         # 实验结果指标
│   └── notes.md             # 实验笔记
├── 2025-01-12_attention-v2/
│   ├── config.yaml
│   ├── results.json
│   └── notes.md
└── ...

每次提交实验分支时,把对应的实验记录也一并提交。这样,Git 历史里就完整保留了「代码 + 配置 + 结果 + 笔记」的完整快照。

我的习惯:每次打 Tag 之前,先确保 experiments/ 目录下的记录是最新的。Tag 打下去的那一刻,整个实验就是可复现的——任何人 checkout 这个 Tag,都能看到当时的代码、配置和结果。

4.6 常见问题与避坑指南

问题 原因 解决方案
实验分支太多,记不清哪个是哪个 分支命名不规范 统一使用 exp/<日期>-<描述> 格式
合并时冲突不断 实验分支和 main 差异过大 定期从 main 合并到实验分支,保持同步
Tag 打错了位置 忘记先 checkout 到正确 commit 使用 git tag -d 删除本地 Tag,重新打
实验记录和代码版本对不上 实验记录没有提交到 Git 把 experiments/ 纳入版本控制

我曾经犯过一个低级错误:在实验分支上跑了一个好结果,兴冲冲地打了 Tag,然后发现 Tag 打在了 main 分支上……结果那个 Tag 指向的代码根本不是实验用的版本。嗯,从那以后,我每次打 Tag 前都会先跑一遍 git branch 确认当前分支。

4.7 小结

分支和 Tag 是 Git 给实验管理最好的礼物。用好它们,你的实验就不再是一团乱麻,而是一条清晰的时间线。

  • 分支:给每个实验方案一个独立空间,互不干扰。
  • Tag:钉住每个关键结果,随时可以回溯。
  • 实验记录:和代码一起提交,形成完整快照。