CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
MLOps 入门/课时/版本控制与 DVC
RawBlame
课时目录12 节

MLOps 入门

  1. 01MLOps 是什么
  2. 02数据工程基础
  3. 03版本控制与 DVC
  4. 04训练与实验跟踪
  5. 05MLflow 与血缘
  6. 06编排与调度
  7. 07CI/CD for ML
  8. 08容器化部署
  9. 09云原生 ML
  10. 10监控与可观测
  11. 11可解释 AI
  12. 12综合练习

版本控制与 DVC

Git、DVC 与可复现性。

30 分钟

版本控制与 DVC

这一课你将能做到

  1. 说清 Git 与 DVC 的分工:Git 管代码和小文件,DVC 管数据/模型的存储指针
  2. 用 dvc.yaml + params.yaml 把「造数据 → 清洗 → 特征 → 训练」定义成一条可重放管道
  3. 配置 remote 存储并完成 push/pull,掌握团队协作时数据与模型文件的流转方式

为什么要学

Git 只适合管文本:仓库超过几百 MB 就开始卡,二进制 diff 没有意义。而 ML 项目最大的两块资产恰恰是数据集(GB 级)和模型文件(百 MB 级)。没有数据版本控制,你永远回答不了「线上这个模型是用哪一版数据训的」这种致命问题。

DVC(Data Version Control)的思路:数据本体放在 Git 仓库之外的存储(本地盘/S3/OSS),Git 里只提交一个几字节的 .dvc 指针文件。于是「提交代码 v3 + 数据 v7」在 Git 历史里原子地绑定在一起,checkout 任意历史提交后 dvc checkout 就能还原对应的数据。

人话:Git 像仓库管理员只登记货单(指针),DVC 像仓库货架(cache + remote),货单和货架通过 MD5 编号对应。本课把上一课的判题日志数据集和特征表全部纳入版本管理。

先建立图像

你的工作区                          Git 仓库                DVC Remote
├─ src/train.py ────────────────▶ 存代码本体
├─ data/raw/judge_logs.parquet ──▶ data/raw.dvc (指针, ~100B)
│            数据本体 ───────────────────────────────────▶ .dvc/cache (MD5 编号)
└─ models/timeout.joblib ───────▶ models.dvc (指针)
             模型本体 ──────────────────────────────────▶ 远端存储 (S3/OSS/MinIO)

git checkout v1.2  →  指针文件回到 v1.2
dvc checkout       →  按 v1.2 的指针从 cache/remote 还原数据与模型

跟着做

步骤 1:初始化 Git 与 DVC

pip install dvc
git init
dvc init
git commit -m "init dvc"

步骤 2:把上一课的数据纳入 DVC

沿用上一课的 src/make_dataset.py、src/clean.py、src/features.py,先生成数据,再让 DVC 接管:

python src/make_dataset.py && python src/clean.py && python src/features.py
dvc add data/raw/judge_logs.jsonl data/processed/problem_daily.parquet
git add data/raw/judge_logs.jsonl.dvc data/processed/problem_daily.parquet.dvc .gitignore
git commit -m "data: 判题日志 v1 (5000 行)"

观察两件事:原始 jsonl 被写进了 .gitignore(DVC 自动加的);.dvc 文件内容是指针:

outs:
  - path: data/raw/judge_logs.jsonl
    hash: md5
    size: 1234567
    hash: 6f1a3c....    # 数据本体的 MD5,存在 .dvc/cache 里

步骤 3:配置 remote 并推送

# 团队共享存储:S3/OSS/MinIO 均可,本地练习用目录模拟
dvc remote add -d storage /tmp/dvcstore        # Windows 用 D:/dvcstore 这类绝对路径
dvc push

同事拿到你的仓库后:

git pull && dvc pull    # 代码 + 数据一步到位

步骤 4:用 dvc.yaml 定义管道(本课核心)

把「命令怎么跑」从口头/文档变成代码:

# dvc.yaml
stages:
  make_data:
    cmd: python src/make_dataset.py
    deps:
      - src/make_dataset.py
    outs:
      - data/raw/judge_logs.jsonl

  clean:
    cmd: python src/clean.py
    deps:
      - src/make_dataset.py
      - data/raw/judge_logs.jsonl
    outs:
      - data/processed/judge_clean.parquet

  features:
    cmd: python src/features.py
    deps:
      - src/clean.py
      - data/processed/judge_clean.parquet
    params:
      - features.tle_window
    outs:
      - data/processed/problem_daily.parquet
# params.yaml
features:
  tle_window: 7        # 滚动 TLE 率窗口(天)

src/features.py 相应改为读取参数(保持口径一处定义):

# src/features.py(关键改动)
import yaml
import pandas as pd

with open("params.yaml", encoding="utf-8") as f:
    WINDOW = yaml.safe_load(f)["features"]["tle_window"]

df = pd.read_parquet("data/processed/judge_clean.parquet")
# ...同上一课的聚合逻辑,把硬编码的 7 全部换成 WINDOW...

先清掉手动生成的产物,改由管道统一管理:

dvc remove data/processed/problem_daily.parquet.dvc 2>$null; dvc repro

步骤 5:改参数,体验增量重算

dvc dag                     # 查看管道依赖图
# 把 params.yaml 的 tle_window 改成 30,然后:
dvc repro
# 输出示意:
# Stage 'features' changed → rerunning
# Data and pipelines are up to date.(make_data/clean 的缓存命中,只重跑了 features)

DVC 会对比每个 stage 的 deps/params/outs 的哈希:没变的直接用缓存,变了的重跑。这就是「数据管道的 makefile」。最后把管道定义提交:

git add dvc.yaml params.yaml data && git commit -m "pipeline: 特征窗口参数化"

讲清楚要点

  1. DVC 的缓存是内容寻址的:文件按 MD5 存进 .dvc/cache,同一份内容全仓库只存一份。改一个字节,哈希就变,所以「版本」天然精确到字节。
  2. .dvc 指针文件才是 Git 的主角:永远 git add xxx.dvc 而不是数据本体。反过来,DVC 自动把大文件写进 .gitignore 防止你手滑。
  3. dvc.yaml 让管道可执行、可增量:dvc repro 依据哈希对比决定重跑范围;改了窗口参数,只会重算下游 stage。
  4. 模型文件同样用 DVC 管:dvc add models/timeout-v1.joblib。DVC 管「训练产出的物理文件」,MLflow 管「实验元数据」——第 5 课两者会打通。
  5. 团队协作流程固定为三步:拉代码(git pull)→ 拉数据(dvc pull)→ 复现管道(dvc repro)。谁改了数据或参数,Git 历史里一目了然。

你可能会懵的地方

  • DVC 和 Git LFS 有什么区别? Git LFS 把大文件塞进 Git 服务器生态,历史里每个版本都要占远端空间,仓库越拉越大;DVC 的 cache 按内容寻址去重,且能接任意对象存储(S3/OSS/SSH/HTTP),与 Git 仓库解耦。大数据集、模型文件场景 DVC 更合适。
  • dvc repro 什么时候会重跑一个 stage? 它声明的 cmd、任何 deps 文件、params 参数、outs 产物哈希有任一变化就重跑;全部一致则直接复用缓存。所以把输入写全(包括依赖的 .py 文件)是管道正确的前提。
  • 数据和模型都该用 DVC,那 MLflow 还要吗? 要,分工不同。DVC 回答「这份模型文件怎么来的」(文件级血缘),MLflow 回答「哪个实验/哪组参数/哪个指标最优」(实验元数据与注册中心)。生产上常见组合:MLflow Registry 登记逻辑版本,artifact 指向 DVC/S3。
  • 缓存误删了怎么办? dvc fetch 从 remote 把哈希对应的文件拉回本地 cache。这也是 remote 除了团队共享外的第二个作用——本地灾难恢复。
  • 为什么不用 cron 直接跑 make/clean/features? cron 不知道依赖关系,任何一环失败都会产出半成品;dvc repro 保证顺序、增量与一致性。调度(什么时候跑)是第 6 课编排工具的职责,两者互补。

错法对照

错法问题正解
git add data/train.csv 把大文件进 Git仓库膨胀、clone 卡死只提交 .dvc 指针,数据走 dvc push
手工编辑 .dvc 文件里的 md5指针与 cache 对不上,checkout 失败永远用 dvc add / dvc repro 生成
超参硬编码在 train.py 里改参数触发全量重算,且历史不可查写进 params.yaml,dvc repro 增量感知
dvc push 之前换电脑数据只存在本机,团队拿不到remote 配好后每次 repro 完即 push
每次全量重新生成所有 stage白白浪费几十分钟依赖声明写全,让缓存命中

卡点急救

  1. dvc add 提示文件已被 Git 追踪:先 git rm --cached 文件名,再重新 dvc add。
  2. dvc repro 报 stage 的依赖未声明:报错里会指出哪个文件变了但不在 deps 里;把它补进对应 stage 的 deps 列表。
  3. dvc pull 找不到 remote:dvc remote list 检查配置;本地路径 remote 在 Windows 上要用盘符绝对路径,团队协作建议直接上 S3/OSS。

面试常问

  • DVC 的版本控制原理? 内容寻址:数据本体按 MD5 存入本地 cache,Git 提交的 .dvc 指针记录哈希与大小;remote 存全量哈希对象。checkout 历史指针 + dvc checkout 即可精确还原任意版本,同一内容自动去重。
  • ML 项目如何做到「一键复现三个月前的结果」? Git 锁代码与 dvc.yaml/params.yaml,DVC 锁数据版本,训练记录里保存 run_id 与数据哈希的关联(MLflow tag);三者取同一历史提交,dvc repro 或重放记录的命令即可复现。
  • dvc.yaml 管道和 Airflow DAG 的边界? dvc.yaml 描述「数据变换的依赖与增量」(可重放性),适合离线特征与训练产物;Airflow/Prefect 描述「任务何时跑、失败怎么救」(调度与运维),负责把 dvc repro 作为其中一个任务定时触发。

自测三问

  1. Git 仓库里关于数据的那 100 字节 .dvc 文件包含什么信息?
  2. dvc repro 判断一个 stage 是否重跑,对比哪些东西的哈希?
  3. DVC 与 MLflow 的职责如何划分?各自回答什么问题?

小结一句话

DVC 用「Git 管指针 + 内容寻址缓存 + remote 存储」给数据和模型补上版本控制,并用 dvc.yaml 把数据管道变成可增量重放的代码。

延伸

  • DVC 官方文档:Get Started 与 User Guide——本课所有命令的权威出处,重点读 dvc.yaml 与 remote 两章。
  • 下一课:训练与实验跟踪——MLflow 怎么把每次训练的超参、指标、模型文件自动记录下来。
加载本节练习…
← 上一课数据工程基础下一课 →训练与实验跟踪