# Chọn embedding model cho dữ liệu tiếng Việt: dựng bộ test 50 câu hỏi trước khi tin leaderboard

> Model được fine-tune cho luật tiếng Việt tự báo Accuracy@1 tăng từ 0.5682 lên 0.7274. Nhưng con số đó chưa nói được gì về kho tài liệu bảo hiểm của khách hàng mà bạn đang phụ trách.

Bản gốc: https://fdetimes.net/vi/bach-khoa/chon-embedding-model-cho-tieng-viet/

Trên Hugging Face, model card của nrl-ai/vietnamese-embedding-bge-m3 báo một bước nhảy khá lớn. Trên bài toán truy xuất văn bản luật Legal Zalo, Accuracy@1 tăng từ 0.5682 của BGE-M3 gốc lên 0.7274, còn MRR@10 tăng từ 0.6822 lên 0.8181. Đây là số do chính tác giả công bố.

Thử hình dung tuần sau bạn ngồi ở văn phòng một công ty bảo hiểm. Họ có hàng nghìn trang điều khoản, quy trình bồi thường và email trả lời khách, tất cả đều viết tiếng Việt. Bạn có nên dùng ngay model fine-tune kia không?

Câu trả lời đúng là: chưa biết, cho tới khi đo trên dữ liệu của họ. Bài này hướng dẫn dựng một bộ test nhỏ để trả lời câu hỏi đó trong một buổi chiều. Ở customer site, đây là kỹ năng giúp FDE đưa ra khuyến nghị có số liệu, thay vì một ý kiến cảm tính.

## Leaderboard chỉ giúp bạn thu hẹp danh sách

Bài so sánh model embedding mã nguồn mở của Supermemory viết thẳng rằng không bảng xếp hạng nào xác định được model tốt nhất cho mọi kho dữ liệu. Leaderboard chỉ giúp rút gọn danh sách ứng viên.

Với tiếng Việt có VN-MTEB, một benchmark embedding gồm 41 dataset, sáu loại tác vụ là retrieval, classification, pair classification, clustering, reranking và STS, đánh giá 18 model.

VN-MTEB được xây bằng cách dùng LLM dịch MTEB sang tiếng Việt. Vì vậy đó là dữ liệu dịch, không phải câu hỏi khách hàng thật gõ vào ô tìm kiếm với từ viết tắt, lỗi chính tả và thuật ngữ nội bộ. Benchmark này cũng ghi nhận rằng các model lớn hơn, dùng Rotary Positional Embedding, vượt các model dùng Absolute Positional Embedding.

Đó là gợi ý tốt để chọn ứng viên, nhưng không thay được bước kiểm chứng.

## Bạn sẽ dựng gì, cần chuẩn bị gì

Sản phẩm cuối là một script chấm ba model ứng viên trên khoảng 50 câu hỏi, in ra Recall@5, MRR@10 và latency trung bình. Bạn cần Python, một máy cố định (cùng GPU hoặc cùng CPU cho mọi lần chạy), thư viện embedding mà team bạn đang dùng, và quan trọng nhất là một người phía khách sẵn lòng xác nhận đáp án.

Các đoạn code bên dưới đã được **đơn giản hóa**. Phần tính điểm viết bằng Python thuần. Hàm `embed()` để trống, bạn tự nối vào thư viện mình dùng theo hướng dẫn trên model card.

## Bước 1: viết câu hỏi bằng ngôn ngữ của khách

Lấy câu hỏi từ nguồn thật như log tổng đài, ticket hay email, đừng tự bịa câu "đẹp". Sau đó cắt tài liệu thành các đoạn (chunk) có id, rồi gắn với mỗi câu hỏi id của đoạn chứa câu trả lời. Ví dụ giả định cho khách bảo hiểm:

```json
{"qid": "q01", "query": "huỷ hđ trước hạn có được hoàn phí ko", "relevant": ["dk_12_3"]}
{"qid": "q02", "query": "nằm viện bao nhiêu ngày thì được trợ cấp", "relevant": ["dk_07_1", "dk_07_2"]}
```

**Kiểm tra:** gửi 10 dòng ngẫu nhiên cho người phía khách duyệt. Nếu họ sửa nhiều hơn hai đáp án, phải làm lại cách gắn nhãn trước khi chấm bất kỳ model nào.

## Bước 2: chọn ba ứng viên và pin mọi thứ

Danh sách hợp lý có ba model. Thứ nhất là BAAI/bge-m3 làm baseline, vì nó hỗ trợ hơn 100 ngôn ngữ và nhận đầu vào tới 8192 token, phù hợp với hợp đồng dài. Thứ hai là bản fine-tune tiếng Việt của nrl-ai.

Thứ ba là một model từ họ khác để có góc nhìn đối chiếu. Cách chọn: mở bảng retrieval của VN-MTEB, lấy model E5 đa ngôn ngữ có điểm cao nhất; nếu bảng không có E5 phù hợp, lấy model đứng đầu không thuộc họ BGE. Sau đó chép đúng tên, commit và prefix từ model card của nó vào cấu hình.

```python
# Đơn giản hóa: thay các chuỗi VIET_HOA bằng giá trị thật từ model card
MODELS = {
"bge-m3":    {"name": "BAAI/bge-m3", "revision": "PIN_COMMIT_HASH", "q_prefix": "", "p_prefix": ""},
"vn-bge-m3": {"name": "nrl-ai/vietnamese-embedding-bge-m3", "revision": "PIN_COMMIT_HASH", "q_prefix": "", "p_prefix": ""},
"e5":        {"name": "TEN_MODEL_E5_CHON_TU_VN_MTEB", "revision": "PIN_COMMIT_HASH",
"q_prefix": "QUERY_PREFIX_THEO_CARD", "p_prefix": "PASSAGE_PREFIX_THEO_CARD"},
}
```

Supermemory khuyên cố định dataset, revision model, preprocessing và phần cứng. Chỉ khi đó chênh lệch về độ chính xác và latency mới có nghĩa. Vì vậy mọi model phải nhận cùng một bộ chunk, cùng cách chuẩn hóa dấu tiếng Việt và chạy trên cùng một máy.

**Điểm mấu chốt:** Một phép so sánh mà mỗi model chạy trong một điều kiện khác nhau chỉ đo được sự khác nhau của điều kiện.

## Bước 3: tính điểm sao cho đọc tay được

```python
def score(ranked, relevant, k=5):
hit = any(d in relevant for d in ranked[:k])          # Recall@k (có ít nhất 1 đoạn đúng)
rr = next((1/(i+1) for i, d in enumerate(ranked[:10]) if d in relevant), 0.0)  # cho MRR@10
return hit, rr

def evaluate(results, testset, k=5):
hits, rrs = zip(*(score(results[q["qid"]], q["relevant"], k) for q in testset))
return sum(hits)/len(hits), sum(rrs)/len(rrs)
```

`results` là danh sách id đoạn đã xếp theo cosine similarity giữa vector câu hỏi và vector đoạn văn, lấy từ hàm `embed()` của bạn. Tính MRR thử bằng tay với ba câu hỏi. Câu một có đoạn đúng ở hạng 1 (điểm 1), câu hai ở hạng 2 (điểm 0.5), câu ba không có đoạn đúng trong top 10 (điểm 0).

MRR bằng (1 + 0.5 + 0) / 3 = 0.5.

Hãy đọc con số này theo vị trí trên danh sách kết quả. MRR 0.8181 nghĩa là đoạn đúng thường nằm ở hạng 1 hoặc 2. MRR 0.5 nghĩa là người dùng thường phải đọc qua một đoạn sai mới tới đoạn đúng. Bạn đo latency bằng cách bấm giờ riêng phần `embed()` cho câu hỏi, chạy trên đúng máy đã cố định.

**Kiểm tra:** chạy lại hai lần với cùng một model. Nếu điểm thay đổi, preprocessing hoặc thứ tự dữ liệu chưa thật sự cố định.

## Bước 4: đọc những câu trượt, không chỉ đọc bảng điểm

Khi có bảng điểm, đừng vội chọn model đứng đầu. Hãy mở những câu mà cả ba model đều trượt. Thường nguyên nhân nằm ở chunk cắt ngang một điều khoản, ở từ viết tắt như "hđ" hay "ko", hoặc ở đáp án gắn sai. Lỗi kiểu này thì đổi model cũng không sửa được.

BGE-M3 còn có ba chế độ truy xuất trong cùng một model: dense, sparse và multi-vector. Khi đã chạy xong bản dense, bạn có thể dùng chính bộ test này để xem chế độ sparse có cứu được những câu chứa mã sản phẩm hay số điều khoản hay không.

## Ba lỗi làm hỏng phép so sánh mà không ai báo

Lỗi đầu tiên là quên prefix. Model E5 cần prefix riêng cho query và cho passage. Bài của Supermemory cảnh báo rằng thiếu prefix có thể làm phép so sánh mất giá trị. Script vẫn chạy, không báo lỗi, và E5 chỉ trông tệ hơn thực tế.

Lỗi thứ hai là tin số liệu tự báo. Model card của nrl-ai cho biết model được huấn luyện trên khoảng 300.000 bộ ba gồm truy vấn, tài liệu đúng và tài liệu sai, và dữ liệu đánh giá Legal Zalo 2021 không nằm trong tập huấn luyện.

Kể cả khi đúng, con số đo trên văn bản luật vẫn chưa cho biết model sẽ làm tốt đến đâu với kho điều khoản bảo hiểm trong ví dụ trên. Bộ test của khách mới là nơi kiểm chứng.

Lỗi thứ ba là coi open weights là miễn phí. Supermemory nhắc rằng inference, hosting và bảo trì đều có chi phí, và license phải được kiểm tra theo từng revision. Hãy đưa cột license và latency vào bảng kết quả gửi khách, đừng chỉ ghi độ chính xác.

## Kỹ năng này xuất hiện thế nào trong công việc FDE

Thử hình dung câu hỏi "nên dùng model nào" được đặt ra trong một buổi họp với khách. Một FDE mang tới bộ test 50 câu đã được khách duyệt sẽ thuyết phục hơn nhiều so với người chỉ chiếu ảnh chụp leaderboard. Bộ test ấy vẫn dùng được khi model hiện tại bị thay: mỗi lần có model mới, bạn chỉ cần chạy lại.

Khi đọc JD, hãy tìm các cụm như "retrieval evaluation", "RAG quality" hay "customer data". Trong CV, đừng chỉ ghi "đã làm RAG". Hãy viết rằng bạn đã dựng bộ đánh giá N câu hỏi tiếng Việt, so sánh ba model và tăng MRR@10 từ bao nhiêu lên bao nhiêu, kèm lý do chọn.

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

- Chọn một bộ tài liệu tiếng Việt công khai (quy chế, điều khoản dịch vụ), tự viết 30-50 câu hỏi và gắn id đoạn đúng cho từng câu
- Chạy BAAI/bge-m3 và nrl-ai/vietnamese-embedding-bge-m3 trên bộ đó với revision được pin, rồi ghi lại Recall@5, MRR@10 và latency
- Đọc 10 câu mà cả hai model đều trượt, phân loại nguyên nhân và viết thành một trang báo cáo ngắn để đưa vào portfolio

## Nguồn

- [Best Open-Source Embedding Models Benchmarked and Ranked](https://supermemory.ai/blog/best-open-source-embedding-models-benchmarked-and-ranked/)

- [VN-MTEB: Vietnamese Massive Text Embedding Benchmark](https://arxiv.org/abs/2507.21500)

- [BAAI/bge-m3 model card](https://huggingface.co/BAAI/bge-m3)

- [nrl-ai/vietnamese-embedding-bge-m3 model card](https://huggingface.co/nrl-ai/vietnamese-embedding-bge-m3)
