Dạy RAG nhìn hình trong một buổi chiều

RAG của bạn chỉ đọc text? Tài liệu có bảng biểu, chart thì coi như mù. Đây là cách fine-tune multimodal embedding để RAG thấy cả hình.

Kịch bản quen mà đau

Giả sử team bạn 4 người, vừa deploy xong hệ thống RAG cho 500 trang tài liệu nội bộ. Demo với sếp, hỏi câu text thuần — trả lời ngon lành. Sếp gật gù, rồi chỉ vào biểu đồ trang 14: "Doanh thu Q3 theo chart này thế nào?" Hệ thống im lặng hai giây, rồi nhả ra một đoạn văn hoàn toàn lạc đề. Phòng họp đột nhiên rất yên.

Lý do thì đơn giản đến đau lòng: RAG truyền thống chỉ "nhìn" text. Bảng biểu, chart, hình minh họa, layout phức tạp — với nó đều là vùng tối. Bạn có OCR cũng chỉ cứu được phần nào, vì cấu trúc bảng bị phá nát khi flatten thành text thuần. Cột nào thuộc hàng nào, model không biết — và nó sẽ đoán bừa.

Multimodal embedding — mở đèn cho vùng tối

Đây là lúc Visual Document Retrieval (VDR) vào cuộc. Thay vì ép tài liệu qua pipeline OCR → chunk text → encode, VDR encode nguyên cả trang tài liệu dạng ảnh thành vector. Khi user hỏi bằng text, query cũng được encode vào cùng không gian — và retrieval diễn ra trên cả nội dung chữ lẫn visual.

Nói thẳng ra thì: nếu RAG text-only là lái xe chỉ nhìn biển báo chữ, thì multimodal embedding là bật thêm camera 360 — bạn thấy cả vạch kẻ đường, biển hình, và cái ổ gà phía trước.

Sentence Transformers — thư viện mà dân làm semantic search đã quen — giờ hỗ trợ multimodal đầy đủ. Cụ thể, bạn có thể fine-tune model Qwen3-VL-Embedding-2B để nó hiểu tài liệu dạng ảnh theo đúng domain của mình. Kết quả từ Hugging Face: model sau fine-tune đạt NDCG@10 là 0.947, so với base model chỉ 0.888 — và vượt cả các model lớn gấp 4 lần kích thước. Nhỏ mà võ công thâm hậu.

Trước khi fine-tune — chuẩn bị gì?

Ba thứ bạn cần sắp xếp trước khi bắt tay vào:

1. Dataset đúng format

Sentence Transformers nhận dạng (query, positive) hoặc (query, positive, negative). Với VDR, "positive" là ảnh trang tài liệu chứa câu trả lời, "query" là câu hỏi text.

from datasets import Dataset

dataset = Dataset.from_dict({
    "query": ["Doanh thu Q3 2025?", "Cấu trúc team AI?"],
    "positive": ["path/to/page_14.png", "path/to/page_7.png"],
})

2. Loss function phù hợp

Hai cái tên cần nhớ:

3. Model base

Qwen3-VL-Embedding-2B là lựa chọn cân bằng — đủ nhỏ để fine-tune trên single GPU, đủ mạnh để cho kết quả production-ready.

Thử ngay trong một buổi chiều

Bước 1 — Cài đặt:

pip install sentence-transformers[vision] datasets

Bước 2 — Load model:

from sentence_transformers import SentenceTransformer

model = SentenceTransformer("Qwen/Qwen3-VL-Embedding-2B")

Bước 3 — Cấu hình loss (đây là phần quan trọng nhất):

from sentence_transformers.losses import (
    CachedMultipleNegativesRankingLoss,
    MatryoshkaLoss,
)

base_loss = CachedMultipleNegativesRankingLoss(model, mini_batch_size=16)
loss = MatryoshkaLoss(model, base_loss, matryoshka_dims=[256, 512, 1024])

Bước 4 — Train:

from sentence_transformers import SentenceTransformerTrainer

trainer = SentenceTransformerTrainer(
    model=model,
    train_dataset=dataset,
    loss=loss,
)
trainer.train()

Bước 5 — Đo NDCG@10 trên tập test, so sánh trước và sau. Với khoảng 500–1000 cặp query-document chất lượng, bạn có thể thấy cải thiện rõ rệt chỉ sau vài epoch.

Mấy cái bẫy mình từng thấy

Bẫy 1: "50 cặp data, fine-tune luôn cho nóng!"

50 samples thì model chưa kịp học đã overfit. Tối thiểu nên có vài trăm cặp chất lượng. Chưa đủ data? Dùng base model trước — nó đã khá ngon rồi, đừng vội.

Bẫy 2: Quên MatryoshkaLoss rồi deploy bị chậm

Train xong embedding 1024 chiều, lúc serve mới thấy latency cao. Muốn giảm chiều? Train lại. MatryoshkaLoss giải quyết chuyện này từ đầu — cho phép cắt chiều lúc inference mà không mất nhiều chất lượng. Thiếu một dòng config, tốn thêm mấy ngày.

Bẫy 3: Fine-tune xong quên optimize inference

Model embedding ngon rồi, nhưng nếu pipeline của bạn còn có LLM generation phía sau, đừng quên phần serve. Với workload sinh nhiều token, speculative decoding — dùng draft model nhỏ đề xuất token, model lớn verify một lượt — có thể giảm latency đáng kể. Như dân đã dùng vLLM biết: tắc ở inference thì fine-tune bao đẹp cũng vô nghĩa.

Khi nào nên — và không nên — làm?

Nên fine-tune khi:

Chưa cần khi:

Đừng tin mình, thử đi rồi biết — chạy base model trên đúng dữ liệu của bạn, đo NDCG@10, rồi hẵng quyết có fine-tune hay không. Đôi khi câu trả lời là "base model đủ rồi" — và đó cũng là một thắng lợi.

---

Bụi Wire — nghiện đọc release notes lúc 2 giờ sáng

Nguồn tham khảo