# Data lineage: lần ngược từng chặng khi khách báo dự đoán sai

> Khách gửi ảnh chụp một con số sai. Lần đường từ con số đó về đúng job và đúng lần chạy đã sinh ra nó giúp FDE tìm nguyên nhân gốc có định hướng, thay vì đoán mò ở model.

Bản gốc: https://fdetimes.net/vi/bach-khoa/data-lineage-truy-nguon-khi-so-lieu-sai/

Thứ Hai, khách gửi vào kênh chung một ảnh chụp màn hình kèm một câu: "Dự báo tuần này của cửa hàng số 12 thấp hơn thực tế, team kho đặt thiếu hàng." Model không đổi, code không ai deploy lại, nhưng con số thì sai. Bạn là FDE phụ trách dự án, và cả phòng đang chờ bạn chỉ ra lỗi nằm ở đâu.

Phản xạ hay gặp là nghi model trước rồi đi retrain, chỉnh hyperparameter, so lại metric. Nhưng khi model và code đều không đổi như trong tình huống này, hướng hợp lý hơn là nghi một thứ đã đổi ở đâu đó phía trên model. Kỹ năng giúp bạn tìm ra thứ đó một cách có hệ thống là data lineage.

## Lineage là bản đồ để đi ngược

Theo định nghĩa của IBM, data lineage là quá trình theo dõi dòng chảy của dữ liệu theo thời gian: dữ liệu bắt nguồn từ đâu, đã thay đổi thế nào và đi tới đâu. Một công dụng chính của nó là truy lỗi về tận nguyên nhân gốc, và mức chi tiết ấy cực kỳ hữu ích khi debug lỗi dữ liệu.

Lời phàn nàn về cửa hàng số 12 là đúng loại việc lineage sinh ra để giải quyết.

Để đi ngược được, bạn cần một mô hình tư duy đơn giản. OpenLineage, một framework và đặc tả mở để thu thập lineage, mô tả mọi pipeline bằng ba thực thể chung: dataset, job và run. Dataset là bảng hoặc file. Job là đoạn xử lý đọc dataset này để ghi ra dataset khác.

Run là một lần chạy cụ thể của job, có thời điểm và có thể đã nhận dữ liệu đầu vào khác với lần chạy trước.

Bạn nên tư duy theo ba thực thể này ngay cả khi pipeline của khách chưa có công cụ lineage nào. Mỗi bước lần ngược sẽ trả lời ba câu: con số nằm trong dataset nào, job nào đã ghi nó ra, và lần chạy nào.

## Lần theo con số của cửa hàng số 12

Dưới đây là một ví dụ giả định, được dựng lại đủ chi tiết để bạn làm theo. Hệ thống của khách có bốn chặng: dữ liệu bán hàng từ máy POS được nạp vào bảng `sales_daily`, một job tính feature `avg_sales_7d` (trung bình số lượng bán của 7 ngày), service dự báo đọc feature này lúc serving, và dashboard hiển thị cột `forecast_qty`.

Việc đầu tiên là chốt cho thật chính xác con số sai, vì "dự báo thấp" là quá mơ hồ. Bạn cần biết đó là dòng nào, cửa hàng 12, SKU nào, tuần nào, và do run nào của service dự báo sinh ra. Khi chưa có run id, mọi so sánh phía sau đều là đoán.

Sau đó đi ngược từng chặng và ở mỗi chặng so giá trị thật với giá trị bạn kỳ vọng:

| Chặng (đi ngược) | Câu hỏi | Kết quả trong ví dụ |
|---|---|---|
| Dashboard `forecast_qty` | Có khớp với output của service không? | Khớp, nên dashboard không có lỗi |
| Service dự báo, run thứ Hai | Input feature là bao nhiêu? | `avg_sales_7d` = 90 |
| Job tính feature | Tính lại tay từ `sales_daily` thì ra bao nhiêu? | Vẫn là 90, công thức không sai |
| Bảng `sales_daily`, cột `qty` | Cột này có nghĩa gì, đổi từ khi nào? | Từ tuần trước, `qty` đã trừ hàng trả lại |

Lúc này nguyên nhân hiện ra. Giả sử cửa hàng bán 100 đơn vị mỗi ngày và có 10 đơn vị bị trả lại. Khi model được huấn luyện, `qty` là số bán gộp nên feature xoay quanh 100. Team POS sau đó đổi `qty` thành số bán ròng, nên lúc serving feature chỉ còn 90.

Model vẫn đúng với những gì nó đã học, chỉ là đầu vào đã mang một ý nghĩa khác.

**Điểm mấu chốt:** Model không hỏng. Ý nghĩa của một cột ở thượng nguồn đã đổi trong khi model vẫn tin vào nghĩa cũ.

## Vì sao loại lỗi này khó thấy

Snowflake xếp training-serving skew vào nhóm những lỗi pipeline khó chẩn đoán, và ví dụ trên cho thấy lý do. Không có job nào báo lỗi, không có bảng nào trống. Mọi chặng đều chạy đúng như code được viết, chỉ có một định nghĩa đã lệch đi.

Lineage ở mức bảng chỉ cho bạn biết `sales_daily` nằm phía trên model, tức là chưa đủ. Bạn cần lineage ở mức cột: lần theo các phụ thuộc xuyên suốt pipeline, nối một thay đổi ở thượng nguồn với feature và dữ liệu huấn luyện ở hạ nguồn.

Chỉ ở mức cột bạn mới thấy được đường đi từ `qty` sang `avg_sales_7d` rồi sang `forecast_qty`.

Cách phòng lâu dài là một định nghĩa feature chuẩn, dùng nhất quán cho cả training lẫn serving. Feature store phát huy tác dụng nhiều nhất khi nhiều model dùng chung feature hoặc khi inference đòi hỏi độ trễ thấp.

Nếu khách chỉ có một model chạy batch hằng tuần, một module định nghĩa feature dùng chung có khi đã đủ.

## Sửa nguồn thì ai khác bị ảnh hưởng?

Đây là chỗ FDE mới vào nghề hay trượt. Bạn đã tìm ra nguyên nhân và muốn yêu cầu team POS trả `qty` về nghĩa cũ. Có thể team tài chính đang cần đúng con số ròng đó cho báo cáo của họ.

Lineage còn có công dụng thứ hai: cho thấy tác động của một thay đổi cụ thể, tức impact analysis. Trước khi đề xuất sửa, hãy đi theo chiều xuôi từ cột `qty` để liệt kê mọi model, dashboard và bảng đang đọc nó. Phương án hợp lý thường là thêm một cột mới như `qty_gross` rồi trỏ feature sang cột đó, thay vì đổi nghĩa thêm một lần nữa.

## Tự làm: năm bước cho lần sự cố tới

1. **Chốt tọa độ.** Biến lời phàn nàn thành một vị trí cụ thể: dòng nào, thời điểm nào, run nào.
2. **Đi ngược từng chặng.** Ở mỗi chặng, ghi lại dataset, job và run.
3. **So với kỳ vọng.** Tính lại giá trị kỳ vọng ở từng chặng rồi so với giá trị thật.

Chặng đầu tiên bị lệch là nơi cần đào sâu.
4. **So hai bản định nghĩa feature.** Đặt định nghĩa lúc training cạnh định nghĩa lúc serving và so từng dòng code, vì skew thường nằm ngay khoảng cách giữa hai bản đó.
5. **Đi xuôi rồi mới sửa.** Chạy impact analysis trước khi đổi bất cứ thứ gì, và sau khi sửa thì để lại lineage cho lần sau.

Về công cụ, Airflow có thể theo dõi lineage giữa các task và gom lineage ở mức hook về một collector trung tâm. Tuy vậy, chính tài liệu của Airflow ghi rằng tính năng này còn rất thử nghiệm và có thể thay đổi.

Vì thế đừng coi đồ thị Airflow sinh ra là đầy đủ, và nếu khách dùng nhiều công cụ khác nhau thì OpenLineage là một chuẩn trung lập đáng cân nhắc.

## Những cái bẫy quen thuộc

Bẫy phổ biến nhất là retrain trước khi lần ngược. Nếu đầu vào đã đổi nghĩa, model mới sẽ chỉ học theo nghĩa mới và che mất nguyên nhân thật. Gần đó là thói quen dừng ở lineage mức bảng: bạn biết bảng nào liên quan nhưng không biết cột nào đã đổi.

Quên ghi run id thì bạn sẽ so dữ liệu hôm nay với một dự đoán sinh ra từ dữ liệu của hôm qua. Còn sửa nguồn mà không đi xuôi thì gỡ được sự cố này nhưng gây ra sự cố khác ở team bên cạnh.

Một sự cố kiểu cửa hàng số 12 cũng là chất liệu tốt nhất cho CV nếu bạn đang muốn chuyển sang FDE. Thay vì viết "có kinh nghiệm debug pipeline", hãy kể rằng bạn đã lần ngược bốn chặng từ dashboard về cột `qty`, phát hiện cột đổi từ số gộp sang số ròng, và kiểm tra downstream trước khi đề xuất thêm `qty_gross`.

Lần tới khi khách gửi một ảnh chụp màn hình, đừng mở notebook để retrain mà mở bản đồ lineage. Thứ khách cần là biết con số sai từ đâu ra, còn retrain thường chưa trả lời được điều đó.

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

- Chọn một con số trên dashboard hoặc một dự đoán mà team bạn đang chạy rồi vẽ tay chuỗi lineage của nó ở mức cột, ghi rõ tên job và cách lấy run id của từng chặng.
- Với mỗi feature của một model, so định nghĩa ở phía training với phía serving. Ghi lại mọi chỗ hai bên được tính bằng hai đoạn code khác nhau.
- Đọc phần dataset/job/run trong tài liệu OpenLineage và thử ánh xạ một pipeline Airflow có sẵn vào ba khái niệm này.

## Nguồn

- [What Is Data Lineage? | IBM](https://www.ibm.com/think/topics/data-lineage)

- [AI Data Pipelines: Why Data Consistency Matters as Much as the Model (Snowflake)](https://www.snowflake.com/guides/what-feature-store-machine-learning/)

- [Lineage — Airflow 3.3.2 Documentation](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/lineage.html)

- [About OpenLineage | OpenLineage](https://openlineage.io/docs/)
