Danh sách bài viết

Bài 43: Data Versioning với DVC

DVC (Data Version Control) là tool CLI mã nguồn mở để version dataset và model file lớn, định nghĩa pipeline ML dạng DAG, và reproduce experiment với cùng code + data state. Bài này trình bày cài đặt DVC 3.x, cơ chế track file, remote storage, pipeline dvc.yaml, experiment tracking, so sánh với Git LFS và MLflow Artifacts, cùng các pitfalls thường gặp.

27/05/2026
0 lượt xem
1

Mục Tiêu Bài Học

Sau bài này bạn sẽ:

  • ✅ Hiểu tại sao data versioning cần tool riêng ngoài git
  • ✅ Biết cách DVC track file lớn bằng metadata .dvc
  • ✅ Cấu hình remote storage (S3, GCS, Azure, Google Drive)
  • ✅ Định nghĩa pipeline ML bằng dvc.yaml và chạy với dvc repro
  • ✅ So sánh DVC với Git LFS và MLflow Artifacts để chọn đúng tool
  • ✅ Tránh các lỗi phổ biến khi dùng DVC trong team
2

Vấn Đề Data Versioning

Code ML đã có git để version. Nhưng dataset là file nhị phân hàng chục GB — git không thiết kế cho trường hợp đó.

Những câu hỏi không trả lời được nếu không version data

  • "Model v2 train với data nào?" — không ai nhớ, không có record.
  • "Accuracy tụt, data có thay đổi không?" — không biết, vì data không được track.
  • "Reproduce kết quả bài báo từ 6 tháng trước" — code có, nhưng data đã overwrite.

Tại sao không dùng git cho data?

  • Git lưu toàn bộ lịch sử dưới .git/objects/. File 1 GB × 10 version = 10 GB trong repo.
  • GitHub/GitLab giới hạn file < 100 MB (hard limit). Push sẽ bị từ chối.
  • Binary diff cho CSV / Parquet vô nghĩa — git không hiểu cấu trúc.
  • git clone chậm kinh khủng khi repo chứa data lớn.

Reproduce ML experiment cần gì?

Để reproduce một experiment, bạn cần đồng thời:

  • Code state: git commit hash
  • Data state: version dataset đúng
  • Config state: hyperparameters, feature list
  • Environment state: Python version, package versions

Git giải quyết code và config. DVC giải quyết data state — mỗi git commit có thể trỏ tới một version data cụ thể.

3

DVC Là Gì

DVC (Data Version Control) là tool CLI Python, mã nguồn mở (Apache 2.0), được thiết kế để bổ sung cho git — không thay thế nó.

Cơ chế cốt lõi

Khi bạn dvc add data/train.csv:

  • DVC tính MD5 hash của file thật.
  • File thật được copy vào .dvc/cache/ (local cache).
  • DVC tạo file data/train.csv.dvc — text file nhỏ < 1 KB chứa hash + path.
  • data/train.csv được thêm vào .gitignore tự động.

File .dvc nhỏ thì commit được vào git. Mỗi git commit trỏ tới một hash data — đó là cách DVC tạo ra "version" cho data.

Các tính năng chính

  • File tracking: Version dataset, model checkpoint, bất kỳ file/folder nào.
  • Remote storage: Đẩy data ra S3, GCS, Azure Blob, Google Drive, SSH, NFS.
  • Pipeline (DAG): Định nghĩa các stage trong dvc.yaml, DVC tự cache và chỉ re-run stage nào có input thay đổi.
  • Experiment tracking: Branch-less experiment với dvc exp run.
  • Metrics comparison: So sánh metric giữa các commit hoặc experiment.

Khi nào nên dùng DVC

  • Dataset > 100 MB (threshold thực tế để thấy lợi ích).
  • Team cần chia sẻ data qua remote storage mà không dùng manual download.
  • Reproducibility là yêu cầu — cần biết chắc model train với data version nào.
  • Pipeline có nhiều step (preprocessing → feature engineering → train → evaluate) cần cache từng bước.
4

Cài Đặt và Init

Cài đặt DVC 3.x

# Core DVC (không có remote backend)
pip install dvc  # hiện tại 3.x

# Hoặc cài kèm plugin cho remote backend
pip install "dvc[s3]"      # Amazon S3
pip install "dvc[gs]"      # Google Cloud Storage
pip install "dvc[azure]"   # Azure Blob Storage
pip install "dvc[gdrive]"  # Google Drive
pip install "dvc[ssh]"     # SSH/SFTP

# Cài nhiều backend cùng lúc
pip install "dvc[s3,gs]"

Init trong project

cd my-ml-project
git init          # Nếu chưa có git repo
dvc init          # Tạo .dvc/ và .dvcignore

# DVC tạo các file cần commit vào git
git add .dvc .dvcignore
git commit -m "init DVC"

Sau khi init, thư mục .dvc/ chứa:

  • .dvc/config — cấu hình remote, cache settings.
  • .dvc/cache/ — local cache cho data (không commit vào git).
  • .dvc/tmp/ — lock file và temp state.

Kiểm tra version

dvc version
# DVC version: 3.x.x
# Platform: Python 3.x on Linux/macOS/Windows
5

Track File Lớn

Thêm file vào DVC tracking

# Có file dataset 1 GB
dvc add data/train.csv

# DVC output:
# To track the changes with git, run:
#   git add data/train.csv.dvc data/.gitignore

Lệnh này thực hiện:

  • Copy data/train.csv vào .dvc/cache/ (symlink hoặc hardlink tuỳ OS).
  • Tạo data/train.csv.dvc với nội dung khoảng 5 dòng.
  • Cập nhật data/.gitignore để git bỏ qua train.csv.

Nội dung file .dvc

# data/train.csv.dvc (ví dụ)
outs:
- md5: a3b4c5d6e7f8...
  size: 1073741824
  path: train.csv

Commit metadata vào git

git add data/train.csv.dvc data/.gitignore
git commit -m "track train.csv with DVC"

Track cả thư mục

# Track cả folder images/
dvc add data/images/

# DVC tạo data/images.dvc (track toàn bộ thư mục)
git add data/images.dvc data/.gitignore
git commit -m "track images directory with DVC"

Lấy lại data

# Nếu xoá hoặc checkout git commit khác
# Lấy lại data tương ứng với git commit hiện tại
dvc checkout
6

Remote Storage — Push và Pull Data

Local cache chỉ trên máy bạn. Remote storage cho phép team chia sẻ data.

Cấu hình remote

# S3
dvc remote add -d storage s3://my-bucket/dvc-storage
dvc remote modify storage region us-east-1
# Credentials qua AWS CLI hoặc env: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY

# Google Cloud Storage
dvc remote add -d storage gs://my-gcs-bucket/dvc
# Credentials qua gcloud auth application-default login

# Azure Blob Storage
dvc remote add -d storage azure://mycontainer/dvc
dvc remote modify storage connection_string "DefaultEndpointsProtocol=https;..."

# Google Drive (cho team nhỏ không có cloud)
dvc remote add -d gdrive gdrive://folder-id-from-url

# Local path (NFS, network share, hay test)
dvc remote add -d localstore /mnt/data/dvc-cache

Flag -d đặt remote này làm default. Cấu hình lưu vào .dvc/config.

Push data lên remote

dvc push
# Uploads files in local cache to remote storage

# Push một file cụ thể
dvc push data/train.csv.dvc

Pull data từ remote

# Member mới clone repo
git clone https://github.com/your-org/ml-project
cd ml-project

# Lấy data version tương ứng với HEAD
dvc pull
# hoặc explicit:
dvc pull data/train.csv.dvc

Lưu ý về credentials

Không commit credentials vào .dvc/config. Dùng một trong:

  • Environment variable: AWS_ACCESS_KEY_ID, GOOGLE_APPLICATION_CREDENTIALS.
  • IAM Role / Workload Identity (production).
  • dvc remote modify --local storage access_key_id AKIA... — lưu vào .dvc/config.local (không commit).
7

Versioning Data Qua Thời Gian

Cập nhật dataset

# Thêm rows mới vào train.csv hoặc replace hoàn toàn
# Sau khi file thay đổi:

dvc add data/train.csv      # Tính lại hash mới, update .dvc file
git add data/train.csv.dvc
git commit -m "update dataset v2: thêm 5000 samples tháng 5/2026"
dvc push                    # Upload version mới lên remote

Mỗi git commit giờ có một hash data khác nhau trong train.csv.dvc. Remote lưu cả hai version — không xoá version cũ.

Quay lại version data cũ

# Checkout git commit cũ
git checkout HEAD~1

# Lấy data tương ứng với commit đó
dvc checkout

# Bây giờ data/train.csv = version trước khi thêm 5000 samples

Diff giữa 2 version

# Xem data hash thay đổi như thế nào giữa 2 commit
dvc diff HEAD~1

# Output:
# Modified:
#   data/train.csv
#   (old: a3b4c5d6..., new: f1e2d3c4...)
8

Pipeline — DVC Stages

DVC pipeline cho phép định nghĩa workflow ML dạng DAG (directed acyclic graph). Mỗi stage có cmd (lệnh chạy), deps (dependencies), và outs (outputs).

Cấu trúc dvc.yaml

# dvc.yaml
stages:
  prepare:
    cmd: python src/prepare.py
    deps:
      - data/raw.csv
      - src/prepare.py
    outs:
      - data/clean.csv

  featurize:
    cmd: python src/featurize.py
    deps:
      - data/clean.csv
      - src/featurize.py
    outs:
      - data/features.csv

  train:
    cmd: python src/train.py
    deps:
      - data/features.csv
      - src/train.py
    params:
      - params.yaml:
          - lr
          - epochs
          - batch_size
    outs:
      - model/model.pkl
    metrics:
      - metrics.json:
          cache: false   # Không cache, lưu plain vào git

Chạy pipeline

# Chạy toàn bộ pipeline (chỉ stage nào cần thiết)
dvc repro

# DVC kiểm tra: deps có thay đổi? params có đổi? code có đổi?
# Nếu không → skip stage (dùng cache)
# Nếu có → re-run stage đó và tất cả stage downstream

Ví dụ output khi chạy với cache hit:

Stage 'prepare' didn't change, skipping
Stage 'featurize' didn't change, skipping
Running stage 'train':
> python src/train.py
Generating lock file 'dvc.lock'
Updating lock file 'dvc.lock'

dvc.lock

Sau khi dvc repro, DVC tạo dvc.lock — ghi lại hash của tất cả deps, outs, params tại thời điểm chạy. Commit dvc.lock vào git để snapshot trạng thái pipeline.

git add dvc.lock
git commit -m "run pipeline: train với lr=0.01 epochs=10"
dvc push   # Push các output mới (model.pkl) lên remote

Thêm stage qua CLI (thay vì edit dvc.yaml tay)

dvc stage add -n prepare \
  -d data/raw.csv -d src/prepare.py \
  -o data/clean.csv \
  python src/prepare.py
9

Params và Metrics

params.yaml

# params.yaml
lr: 0.01
epochs: 10
batch_size: 32
model_type: random_forest
max_depth: 5

Script Python đọc params:

# src/train.py
import yaml
import json
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
import pandas as pd
import pickle

with open("params.yaml") as f:
    params = yaml.safe_load(f)

# Train
df = pd.read_csv("data/features.csv")
X, y = df.drop("label", axis=1), df["label"]

model = RandomForestClassifier(
    max_depth=params["max_depth"],
    random_state=42
)
model.fit(X, y)
acc = accuracy_score(y, model.predict(X))

# Log metrics
with open("metrics.json", "w") as f:
    json.dump({"accuracy": acc}, f, indent=2)

# Save model
with open("model/model.pkl", "wb") as f:
    pickle.dump(model, f)

So sánh metrics

# Xem metrics hiện tại
dvc metrics show
# Path         accuracy
# metrics.json  0.9234

# So sánh với commit trước
dvc metrics diff HEAD~1
# Path         Metric    Old    New    Change
# metrics.json accuracy  0.908  0.923  +0.015

# So sánh với branch khác
dvc metrics diff main
10

Experiment Tracking với DVC

DVC 2.x trở lên có dvc exp — experiment tracking không cần tạo branch. Phù hợp cho hyperparameter sweep nhanh.

Chạy experiment với param khác

# Giữ nguyên code, thay đổi param
dvc exp run --set-param lr=0.001
dvc exp run --set-param lr=0.0001 --set-param epochs=20
dvc exp run --set-param max_depth=10

Xem kết quả tất cả experiments

dvc exp show

# Kết quả dạng bảng:
# Experiment   lr      epochs  max_depth  accuracy
# workspace    0.01    10      5          0.9234
# exp-a1b2c3   0.001   10      5          0.9301
# exp-d4e5f6   0.0001  20      5          0.9287
# exp-g7h8i9   0.01    10      10         0.9356

Apply experiment tốt nhất vào workspace

dvc exp apply exp-g7h8i9
# Cập nhật params.yaml và dvc.lock theo experiment đó

git add params.yaml dvc.lock
git commit -m "use max_depth=10, accuracy=0.9356"
dvc push

Lưu experiment thành branch

dvc exp branch exp-g7h8i9 feat/max-depth-10
git checkout feat/max-depth-10

DVC experiment tracking nhẹ hơn MLflow nhưng không có UI web. Phù hợp cho individual hoặc team nhỏ. Với team lớn cần UI, MLflow (bài 40) phù hợp hơn — hai tool có thể dùng song song.

11

DVC + Git Workflow Thực Tế

Workflow phát triển tính năng mới

# 1. Tạo branch mới
git checkout -b feature/new-architecture

# 2. Cập nhật code và/hoặc data
# - Edit src/train.py
# - Thêm data mới: dvc add data/train_v2.csv

# 3. Chạy pipeline
dvc repro

# 4. Commit tất cả
git add dvc.lock src/train.py params.yaml
git commit -m "experiment: new architecture, accuracy=0.941"

# 5. Push data lên remote TRƯỚC khi push git
dvc push
git push -u origin feature/new-architecture

Lưu ý: Luôn dvc push trước git push. Nếu git commit có trên remote mà data chưa push, CI/CD hoặc teammate sẽ không dvc pull được.

Onboard member mới

git clone https://github.com/your-org/ml-project
cd ml-project

# Cấu hình credentials cho remote (một lần)
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...

# Lấy data version tương ứng HEAD
dvc pull

# Reproduce pipeline (optional, dùng cache nếu không cần re-train)
dvc repro

CML — Continuous ML (giới thiệu)

CML (Continuous Machine Learning) là tool của Iterative (cùng tổ chức với DVC) để tích hợp pipeline DVC vào GitHub Actions / GitLab CI. Khi mở Pull Request, CI tự chạy dvc repro và comment metric comparison vào PR. Chi tiết sẽ ở bài 44.

12

DVC vs Git LFS

Tiêu chí Git LFS DVC
Mục đích chính Lưu file lớn trong git repo Version data + model + định nghĩa pipeline ML
Storage backend LFS server (GitHub, GitLab tích hợp sẵn) S3, GCS, Azure, Drive, SSH, NFS, ...
Pipeline / DAG Không có Có (dvc.yaml, dvc repro)
Experiment tracking Không có Có (dvc exp run, dvc exp show)
Metrics comparison Không có Có (dvc metrics diff)
Chi phí storage Trả theo LFS bandwidth + storage (GitHub Free: 1 GB) Trả theo cloud storage backend bạn chọn
Dùng cho ML Không tối ưu — thiếu pipeline, thiếu metrics Thiết kế cho ML workflow
Dùng cho asset binary (font, image UI, ...) Phù hợp — tích hợp sâu vào git clone Có thể dùng nhưng overkill

Hướng dẫn chọn: nếu file là binary asset cho frontend/design (icon, font, video) → Git LFS. Nếu file là dataset hoặc model checkpoint → DVC.

13

DVC vs MLflow Artifacts

MLflow (bài 40) và DVC đều có thể lưu model artifact. Điểm khác biệt:

Tiêu chí MLflow Artifacts DVC
Versioning data (dataset lớn) Không trực tiếp — MLflow không thiết kế cho raw dataset Có, đây là use case chính
Model versioning Có (MLflow Model Registry) Có (track model file như DVC output)
Pipeline definition Có (MLflow Projects) nhưng ít phổ biến Có (dvc.yaml), phổ biến hơn
UI web Có (MLflow UI đẹp, nhiều chart) Không có built-in (dùng DVC Studio — bản cloud có phí)
Tích hợp với git Lỏng — không phụ thuộc git Chặt — metadata .dvc file lưu trong git
Dùng song song Được — DVC cho data + pipeline, MLflow cho experiment metrics + model registry

Pattern phổ biến trong production: DVC track dataset và define pipeline, MLflow log metrics và manage model lifecycle (staging → production). Hai tool không conflict nhau.

14

Ví Dụ End-to-End

Cấu trúc project

ml-project/
├── .dvc/
│   ├── config          # Remote storage config
│   └── cache/          # Local cache (không commit)
├── .dvcignore
├── data/
│   ├── raw.csv         # Không commit (trong .gitignore)
│   ├── raw.csv.dvc     # Commit vào git
│   └── .gitignore
├── model/
│   ├── model.pkl       # Không commit
│   └── .gitignore
├── src/
│   ├── prepare.py
│   └── train.py
├── dvc.yaml
├── dvc.lock            # Commit vào git
├── params.yaml
└── metrics.json

Quy trình từ đầu đến cuối

# Bước 1: Setup
git init && dvc init
dvc remote add -d storage s3://my-bucket/dvc
git add .dvc .dvcignore
git commit -m "init DVC"

# Bước 2: Track raw data
dvc add data/raw.csv
git add data/raw.csv.dvc data/.gitignore
git commit -m "add raw dataset (50k samples)"
dvc push

# Bước 3: Define pipeline
# → Tạo dvc.yaml (như ví dụ section 8)
# → Tạo params.yaml
# → Viết src/prepare.py và src/train.py

# Bước 4: Run pipeline
dvc repro

# Bước 5: Commit kết quả
git add dvc.lock dvc.yaml params.yaml src/ metrics.json
git commit -m "pipeline: prepare + train, accuracy=0.923"
dvc push

# === Reproduce trên máy khác ===
git clone https://github.com/your-org/ml-project
cd ml-project
dvc pull          # Download data/raw.csv và model/model.pkl
dvc repro         # Chạy lại pipeline, kết quả y hệt
15

Common Pitfalls

1. git add data thực thay vì .dvc file

Sau dvc add data/train.csv, file thật đã được add vào .gitignore. Nếu bạn vẫn cố git add data/train.csv, git sẽ ignore vì entry trong .gitignore. Nhưng nếu đã từng commit file thật trước khi init DVC, cần git rm --cached data/train.csv.

2. Commit credentials vào .dvc/config

# SAI: credentials trong config → commit lên git → lộ
dvc remote modify storage access_key_id AKIA...

# ĐÚNG: dùng --local để lưu vào .dvc/config.local (gitignore)
dvc remote modify --local storage access_key_id AKIA...

# Hoặc dùng environment variable
export AWS_ACCESS_KEY_ID=AKIA...

3. Quên dvc push trước git push

Khi teammate chạy git pull rồi dvc pull, DVC cần tìm data hash tương ứng trên remote. Nếu chưa push, dvc pull thất bại với lỗi ERROR: failed to pull data. CI/CD cũng sẽ fail tương tự.

Thứ tự bắt buộc: dvc pushgit push.

4. .dvcignore không setup

.dvcignore tương tự .gitignore nhưng cho DVC cache. Nếu không cấu hình, DVC có thể cache cả file log, temp, hoặc thư mục không cần thiết.

# .dvcignore ví dụ
logs/
*.log
__pycache__/
.DS_Store

5. Pipeline DAG có cycle

Nếu stage A depend vào output của stage B, và B depend vào output của A → DVC sẽ báo lỗi khi dvc repro. Kiểm tra bằng dvc dag để visualize graph trước khi chạy.

dvc dag        # ASCII art DAG trong terminal
dvc dag --md   # Markdown format (cho README)

6. Local cache quá lớn

Mỗi lần dvc add hoặc dvc repro, file được copy vào .dvc/cache/. Sau nhiều version, cache có thể chiếm hàng chục GB. Dùng dvc gc để cleanup:

# Xoá cache entries không dùng bởi bất kỳ branch/tag nào
dvc gc --all-branches --all-tags

# Xem size cache trước khi gc
du -sh .dvc/cache/

7. dvc repro không chạy lại stage dù code đổi

DVC track deps bằng file path. Nếu script import module không nằm trong deps, DVC không biết. Thêm tất cả Python file liên quan vào deps:

stages:
  train:
    cmd: python src/train.py
    deps:
      - src/train.py
      - src/model.py      # Module import trong train.py
      - src/utils.py      # Cần thêm vào deps
      - data/features.csv
16

Tóm Tắt

  • ✅ DVC track file lớn bằng file .dvc nhỏ (metadata) trong git, data thật ở local cache và remote storage.
  • ✅ Mỗi git commit trỏ tới một version data cụ thể — git checkout + dvc checkout để quay về trạng thái cũ.
  • ✅ Remote storage: dvc remote add, dvc push, dvc pull. Thứ tự: push data trước khi push git.
  • dvc.yaml định nghĩa pipeline DAG, dvc repro chạy stage nào cần thiết và cache stage không đổi.
  • params.yaml + metrics.json + dvc metrics diff để so sánh kết quả giữa các run.
  • dvc exp run cho hyperparameter sweep không cần tạo branch.
  • ✅ DVC và MLflow có thể dùng song song: DVC cho data + pipeline, MLflow cho experiment UI + model registry.
  • ✅ Credentials không commit vào .dvc/config — dùng --local hoặc env variable.