3. 版本控制入门:Git 基础操作
说到可复现实验,版本控制是绕不开的一环。你想想看,一个实验跑完,代码改了三版,数据换了五版,最后连自己都分不清哪个是最终版——这种经历,我猜你也有过。
Git 就是来解决这个问题的。它不复杂,核心就几个命令。但用好它,你的实验就有了「后悔药」和「时光机」。
3.1 为什么实验代码需要版本控制?
我刚开始做研究时,代码管理全靠文件夹命名:实验_v1、实验_v2_最终、实验_v3_真的最终……结果有一次,我把一个跑了两天的模型代码改崩了,想回退却找不到原始版本。嗯,从那以后,我再也不敢裸写代码了。
版本控制能给你三样东西:
- 历史回溯:每一次修改都有记录,随时可以回到任意时间点
- 变更追踪:谁、在什么时候、改了哪些文件,一目了然
- 协作基础:多人同时开发,互不干扰,最后合并
核心原则:实验代码的每一次「可复现」状态,都应该对应一个 Git 提交。跑通一个实验,就 commit 一次。
3.2 Git 的三大区域
理解 Git,先要搞懂它的三个区域。说白了,就是文件在三个地方流转:
| 区域 | 作用 | 类比 |
|---|---|---|
| 工作区(Working Directory) | 你实际编辑文件的地方 | 你的实验台 |
| 暂存区(Staging Area) | 临时存放准备提交的修改 | 你的待办清单 |
| 仓库(Repository) | 永久保存所有历史版本 | 你的实验档案柜 |
流程很简单:你在工作区改代码,改好了用 git add 放到暂存区,确认无误后用 git commit 存入仓库。就这么三步。
3.3 初始化仓库:git init
一切从 git init 开始。这个命令会在当前目录创建一个隐藏的 .git 文件夹,里面装着 Git 需要的所有元数据。
cd ~/projects/my_experiment
git init
执行后,你会看到类似 Initialized empty Git repository in ... 的提示。这就意味着你的实验代码有了「户口」。
我的习惯:每个实验项目都单独建一个仓库,不要把所有实验塞到一个仓库里。否则历史记录会乱成一锅粥。
3.4 添加文件到暂存区:git add
改完代码后,用 git add 把文件加入暂存区。你可以指定单个文件,也可以一次加所有文件。
# 添加单个文件
git add train.py
# 添加所有变更文件
git add .
# 添加指定目录
git add utils/
我个人建议用 git add . 之前,先跑一下 git status 看看哪些文件被改了。避免把不该提交的文件(比如数据集、日志文件)也加进去。
我曾经踩过的坑:有一次不小心把 2GB 的数据集 add 进去了,提交时卡了半天。后来我学会了用 .gitignore 文件来排除这些大文件。
3.5 提交到仓库:git commit
git commit 是把暂存区的修改永久保存到仓库。每次提交都要写一条消息,说明这次改了啥。
git commit -m "完成数据预处理模块,修复缺失值处理逻辑"
提交消息的写法有讲究。我总结了一个简单模板:
| 类型 | 格式 | 示例 |
|---|---|---|
| 新增功能 | feat: 简短描述 | feat: 添加模型评估函数 |
| 修复 bug | fix: 简短描述 | fix: 修复数据加载路径错误 |
| 重构代码 | refactor: 简短描述 | refactor: 拆分训练脚本 |
| 文档更新 | docs: 简短描述 | docs: 更新 README 使用说明 |
为什么要这么写?因为几个月后你回头看提交历史,一眼就能知道每个版本做了什么。说白了,这是写给未来的自己看的。
3.6 推送到远程仓库:git push
本地仓库的代码,只有推送到远程(比如 GitHub、GitLab),才算真正安全。万一电脑坏了,代码还能从远程恢复。
# 添加远程仓库
git remote add origin https://github.com/yourname/experiment.git
# 推送代码
git push -u origin main
-u 参数的意思是设置上游分支,以后直接 git push 就行,不用每次都指定远程和分支。
我的建议:每次实验跑通一个关键节点,就 push 一次。这样即使本地代码丢了,远程还有备份。我一般每天至少 push 一次。
3.7 拉取远程更新:git pull
如果你在多台电脑上工作,或者和同事协作,就需要用 git pull 把远程的最新代码拉下来。
git pull origin main
这个命令相当于 git fetch + git merge。它会先下载远程的更新,然后合并到你的当前分支。
注意:pull 之前,最好先 commit 或 stash 你本地的修改。否则可能会遇到合并冲突。我遇到过几次冲突,解决起来挺麻烦的。
3.8 实验代码的版本历史管理
有了 Git,你的实验就有了完整的版本历史。用 git log 可以查看所有提交记录。
git log --oneline --graph --all
这个命令会以图形化的方式展示分支和提交历史。看起来就像一棵树,每个节点就是一个版本。
我一般这样管理实验版本:
- v0.1:初始代码框架,能跑通但结果不对
- v0.2:修复数据预处理 bug,结果接近预期
- v0.3:调参后效果提升,记录超参数配置
- v1.0:实验复现成功,提交最终版本
每个版本都对应一个 commit。这样,你可以随时用 git checkout 回到任意版本,重新跑实验。
小技巧:在 commit 消息里加上实验的关键参数,比如 feat: 添加模型,学习率 0.001,batch_size 32。这样看历史就知道每个版本用了什么配置。
3.9 避坑指南
最后,分享几个我踩过的坑:
- 不要提交大文件:数据集、模型权重文件用 Git LFS 管理,或者单独存放
- 不要提交敏感信息:、密码等用环境变量,别写死在代码里
- 不要频繁 commit:每次提交应该是一个完整的逻辑单元,改一行字就 commit 没必要
- 不要忘记写 commit 消息:空消息的提交,以后根本看不懂
嗯,Git 的基础操作就这些。说白了,就是 init、add、commit、push、pull 五个命令。但用好它们,你的实验代码就有了完整的版本历史,可复现性也就有了基础保障。