# Thực hành DVC: mỗi lần khách gửi dữ liệu mới là một commit truy ngược được

> Khi khách hỏi "model tháng trước học trên dữ liệu nào?", FDE cần trả lời bằng một commit hash, không phải bằng trí nhớ.

Bản gốc: https://fdetimes.net/vi/bach-khoa/dvc-version-du-lieu-va-model-cua-khach/

Thứ Hai khách gửi file dữ liệu tuần này. Thứ Tư họ gửi bản "đã sửa lỗi". Thứ Sáu, model mới cho kết quả tệ hơn và câu hỏi đầu tiên trong cuộc họp là: model đang chạy production được train trên bản nào?

Nếu câu trả lời của bạn là "chắc là bản thứ Tư", bạn đã mất một phần niềm tin của khách. Đây là tình huống FDE gặp liên tục khi triển khai ML tại hiện trường: dữ liệu không đứng yên, và code có Git nhưng dữ liệu thì thường nằm trong một thư mục tên `final_v3_moi.csv`.

Hướng dẫn này đi qua một quy trình bạn có thể dựng trên laptop trong một buổi tối, dùng DVC. Trang chủ DVC tóm tắt triết lý của nó bằng ý quản lý dữ liệu theo đúng cách người ta quản lý code, và tài liệu Get Started gọi thẳng nó là "Git for data".

## Bạn sẽ xây gì, và cần gì trước?

Thử hình dung một khách hàng bán lẻ gửi file `data/data.xml` mỗi tuần. Bài thực hành nhắm tới ba điều: mỗi đợt dữ liệu ứng với một Git commit, dữ liệu thật nằm trên kho lưu trữ khách cho phép, và bạn lấy lại được bất kỳ đợt nào khi cần.

Bạn cần Git, DVC đã cài, và một repo Git đã khởi tạo DVC theo trang Get Started. Điểm cần hiểu trước khi gõ lệnh: về mặt kỹ thuật, bản thân DVC không phải hệ thống quản lý version. Nó giao phần lịch sử cho Git; nội dung file `.dvc` quyết định phiên bản dữ liệu nào đang áp dụng.

## Bước 1–2: biến đợt dữ liệu đầu tiên thành một commit

Đưa file vào DVC:

```bash
dvc add data/data.xml
```

DVC tạo ra file metadata `data/data.xml.dvc`. Mở nó ra để kiểm tra: file này ghi kích thước, số lượng file và quan trọng nhất là một MD5 hash, chính hash này định danh phiên bản dữ liệu.

Sau đó commit file metadata vào Git, không phải bản thân dữ liệu:

```bash
git add data/data.xml.dvc
git commit -m "Dữ liệu khách: đợt tuần 1"
```

Từ lúc này, metadata của dữ liệu nằm trong lịch sử Git ngay cạnh source code. Ví dụ trên đã rút gọn: khi chạy `dvc add`, hãy đọc output của DVC vì nó có thể gợi ý thêm file cần `git add`.

## Bước 3: đặt dữ liệu ở nơi khách cho phép

Git chỉ có file nhỏ, vậy dữ liệu thật đi đâu? DVC dùng remote, tức kho lưu trữ bên ngoài cho dữ liệu và model, hỗ trợ S3, Azure, GCS, SSH và nhiều loại khác. Với FDE, điều này quan trọng vì khách thường quyết định dữ liệu được phép nằm ở đâu.

```bash
dvc remote add myremote s3://mybucket
dvc push
```

Lệnh `dvc push` tải dữ liệu trong cache lên remote đã cấu hình. Đây là ví dụ đơn giản hoá; trước khi cấu hình remote thật cho khách, hãy đọc kỹ trang Remote Storage trong tài liệu DVC.

Để chắc chắn mọi thứ chạy đúng, mở bucket ra xem: dữ liệu vừa push phải nằm ở đó. Nếu khách chỉ cho dùng máy chủ nội bộ qua SSH, bạn đổi URL remote chứ không đổi quy trình.

## Bước 4: đợt dữ liệu thứ hai đến

Khách gửi bản mới. Bạn ghi đè file và lặp lại đúng nhịp cũ:

```bash
dvc add data/data.xml
git add data/data.xml.dvc
git commit -m "Dữ liệu khách: đợt tuần 2"
dvc push
```

Mở lại `data/data.xml.dvc` và so với commit trước: MD5 đã đổi. Nếu khách bảo "bản này giống hệt tuần trước" mà hash khác, bạn vừa phát hiện một thay đổi thầm lặng trước khi nó làm hỏng model.

**Điểm mấu chốt:** Hash trong file .dvc là bằng chứng; trí nhớ của kỹ sư thì không.

## Bước 5: quay về đúng dữ liệu của một model cũ

Quay lại câu hỏi trong cuộc họp thứ Sáu. Bạn tìm commit của model production (dùng `git log`) và checkout nó. Trong ví dụ dưới, `a1b2c3d` chỉ là hash minh hoạ, hãy thay bằng hash thật trong repo của bạn:

```bash
git checkout a1b2c3d
dvc checkout
```

Quy tắc đáng thuộc lòng: mỗi lần gọi `git checkout`, luôn gọi thêm `dvc checkout`. Lý do quay về điểm đã nói từ đầu: DVC không tự giữ lịch sử, Git giữ, và Git chỉ biết tới file `.dvc`. Vì thế lệnh đầu chỉ đổi file `.dvc` về bản cũ; lệnh sau mới đưa dữ liệu trong thư mục làm việc về đúng phiên bản mà file đó trỏ tới.

Thời gian chờ phụ thuộc vào dung lượng dữ liệu, nên với dataset lớn đừng hứa với khách một con số trước khi tự đo.

## Bước 6: chỉ train lại phần cần train lại

Để `dvc repro` có việc mà làm, repo cần một pipeline. Dưới đây là một file `dvc.yaml` tối giản với một stage tên `filter`, đọc dữ liệu khách và ghi ra file đã lọc. Đây là ví dụ đơn giản hoá: `filter.py` là script bạn tự viết, và cú pháp đầy đủ của `dvc.yaml` nằm trong tài liệu DVC.

```yaml
stages:
filter:
cmd: python filter.py
deps:
- data/data.xml
- filter.py
outs:
- data/filtered.csv
```

Pipeline DVC hoạt động như một build system. Bạn chạy:

```bash
dvc repro
```

DVC chỉ chạy lại stage có input thay đổi, và in ra những dòng như "Stage 'filter' didn't change, skipping" cho phần còn lại. Thử ngay: chạy `dvc repro` hai lần liền, lần thứ hai sẽ bỏ qua `filter`; sau đó `dvc add` đợt dữ liệu mới và chạy lại, stage sẽ chạy thật.

Sau mỗi lần chạy, hash của các dependency và output thay đổi được cập nhật vào `dvc.lock`. Commit file này vào Git, vì nó ghim lại trạng thái pipeline của một lần chạy tái lập được. Khi đã quen với một stage, bạn thêm dần các stage tạo feature và train theo đúng mẫu trên.

## Những lỗi hay gặp

Lỗi phổ biến nhất là chỉ `git checkout` rồi train, trong khi thư mục `data/` vẫn là bản mới nhất. Model "cũ" bạn tái lập thực chất là model mới với code cũ.

Lỗi thứ hai là commit file `.dvc` nhưng quên `dvc push`. Đồng nghiệp hoặc server của khách có commit nhưng không có dữ liệu tương ứng. Hãy coi `git commit` và `dvc push` là một cặp không tách rời.

Lỗi thứ ba là không commit `dvc.lock`. Khi đó bạn có dữ liệu đã version nhưng không chứng minh được lần train nào dùng đầu vào nào.

## Ở site khách hàng, kỹ năng này trông ra sao?

Ở hiện trường, giá trị của DVC không nằm ở lệnh mà ở câu trả lời bạn đưa ra được. Khi khách hỏi vì sao model tuần này khác tuần trước, bạn chỉ vào hai commit, hai MD5 hash và `dvc.lock` của mỗi lần chạy. Cuộc tranh luận chuyển từ cảm giác sang bằng chứng.

Việc đầu tiên nên làm khi nhận một dự án mới: hỏi khách dữ liệu được phép lưu ở đâu, rồi chọn remote theo đó trước khi nhận đợt dữ liệu đầu tiên. Dựng quy trình sau khi đã có ba bản "final" thì muộn hơn nhiều.

Với developer Việt Nam muốn chuyển sang FDE, hãy để ý các JD nhắc tới "reproducibility", "data versioning" hay "MLOps tại site khách hàng". Một dòng CV như "version 12 đợt dữ liệu khách hàng bằng DVC trên S3, tái lập model bất kỳ từ commit" nói nhiều hơn một danh sách công cụ.

Khách sẽ luôn gửi thêm dữ liệu. Câu hỏi duy nhất là đến lần thứ mười, bạn còn biết model nào học từ bản nào hay không.

**Thử ngay tuần này:**

- Lấy một dataset công khai, chia thành 3 'đợt giao hàng' giả lập và commit từng đợt bằng dvc add + git commit.
- Checkout về đợt đầu tiên, chạy dvc checkout và kiểm tra MD5 trong file .dvc khớp với commit.
- Viết vào CV một dòng cụ thể: số đợt dữ liệu đã version, loại remote đã dùng và cách bạn tái lập một lần train cũ.

## Nguồn

- [DVC](https://dvc.org/)

- [Get Started with DVC](https://doc.dvc.org/start)

- [Data Versioning (DVC Get Started)](https://doc.dvc.org/start/data-management/data-versioning)

- [Remote Storage (DVC User Guide)](https://doc.dvc.org/user-guide/data-management/remote-storage)

- [The Complete Guide to Data Version Control With DVC](https://www.datacamp.com/tutorial/data-version-control-dvc)

- [dvc repro (DVC Command Reference)](https://doc.dvc.org/command-reference/repro)

- [Git](https://git-scm.com/)
