Re-Ranking
Re-ranking - Xếp hạng lại kết quả
Chatbot pháp luật tìm 'quyền lao động Việt Nam'. Stage 1 (BM25 + Semantic) trả về 50 tài liệu. Nhưng tài liệu xếp hạng 1 chứa chữ 'lao động' xuất hiện nhiều lần mà không thực sự trả lời câu hỏi. Cần làm gì?
Hãy tưởng tượng bạn đang tổ chức một cuộc thi tài năng 2 vòng cho một đài truyền hình. Có 10 triệu người đăng ký. Bạn không thể để giám khảo chính chấm từng người — ông ấy sẽ mất cả đời. Giải pháp: dùng một vòng sơ loại nhanh để rút xuống 100 thí sinh, rồi giám khảo chính chỉ cần dành thời gian cho 100 người đó.
Vòng sơ loại ở đây tương ứng với stage 1 của search pipeline — BM25 hoặc semantic search bằng bi-encoder. Nhanh nhưng hơi hời hợt: nhìn tổng thể người đó có ở đúng thể loại không, có từ khóa không, giọng có tương đồng không. Thí sinh xếp đầu vòng sơ loại chưa chắc là xuất sắc nhất — có thể chỉ là người có "profile" khớp tốt với yêu cầu.
Vòng chung kết là stage 2 — re-ranking bằng cross-encoder. Giám khảo chính ngồi xem từng thí sinh trình diễn, đối chiếu chi tiết với yêu cầu, đánh giá kỹ năng ẩn mà vòng sơ loại không phát hiện ra. Vì dành nhiều thời gian hơn, giám khảo chấm chính xác hơn nhiều — và xếp hạng có thể đảo lộn hoàn toàn so với vòng 1.
Trong AI/ML, điều này có nghĩa là một pipeline 2 tầng: tầng đầu tối ưu cho tốc độ và recall (không được bỏ sót), tầng hai tối ưu cho độ chính xác (xếp đúng thứ tự). Đây là kiến trúc chuẩn mà hầu hết hệ thống search hiện đại (Google, Bing, Perplexity, Cohere) đều dùng.
Nếu không có re-ranking, hệ thống của bạn sẽ bị "lừa" bởi các tài liệu trông liên quan (nhiều từ khóa, embedding tương tự) nhưng không thực sự trả lời câu hỏi. Với RAG, điều đó dẫn tới LLM nhận context kém chất lượng, từ đó sinh ra hallucination hoặc câu trả lời lạc đề. Re-ranking là van chất lượng — lọc lại trước khi đưa vào LLM.
Hình minh họa
Score shifts: Bi-encoder → Cross-encoder
| ID | Tiêu đề | Bi | Cross | Δ hạng | Gold? |
|---|---|---|---|---|---|
| D1 | Luật lao động 2019: chương quyền lao động | 0.82 | 0.94 | 0 | ✓ |
| D2 | Lao động nước ngoài xin giấy phép thế nào | 0.78 | 0.21 | -5 | |
| D3 | Quyền lợi và nghĩa vụ người lao động — Bộ luật 45/2019 | 0.73 | 0.91 | +1 | ✓ |
| D4 | Lao động trẻ em ở Việt Nam giảm 30% trong 5 năm | 0.71 | 0.18 | -4 | |
| D5 | Quyền đình công và tranh chấp lao động tập thể | 0.68 | 0.79 | 0 | ✓ |
| D6 | Giá vàng hôm nay tăng mạnh — người lao động khóc ròng | 0.66 | 0.04 | -3 | |
| D7 | Hợp đồng lao động — mẫu và hướng dẫn ký kết | 0.64 | 0.47 | +1 | |
| D8 | Công ước ILO về quyền tự do hiệp hội ở Việt Nam | 0.61 | 0.83 | +4 | ✓ |
| D9 | Tin tức thể thao: cầu thủ lao động trên sân cỏ | 0.58 | 0.02 | -1 | |
| D10 | Hỏi đáp: tôi có quyền từ chối tăng ca quá 4 giờ/ngày không? | 0.55 | 0.88 | +7 | ✓ |
D6 ("giá vàng") và D9 ("cầu thủ lao động") rơi sâu vì cross-encoder phát hiện chúng không thực sự liên quan tới quyền lao động. Ngược lại D8 và D10 leo lên top nhờ nội dung đúng chủ đề.
Chọn reranker: latency × chất lượng
Cohere Rerank v3.5 · top_k=10
Đa ngôn ngữ 100+, bao gồm tiếng Việt. Không cần quản lý GPU. Thích hợp cho production khởi động nhanh.
Ước lượng latency: 80 ms (10 cặp × 8 ms)
Chất lượng NDCG@10: 0.768 (MS MARCO dev)
Bi-Encoder (Stage 1)
Mã hóa query và doc RIÊNG RẼ. Precompute doc embeddings. Cosine + ANN. Rất nhanh (ms), tối ưu cho recall.
Cross-Encoder (Stage 2)
Mã hóa CÙNG LÚC (query, doc). Attention qua cả hai → chính xác hơn 15–25% NDCG, nhưng O(N) mỗi query. Chỉ chạy trên top-K.
Re-ranking giống cuộc thi tài năng 2 vòng: vòng sơ loại (stage 1) lọc nhanh 10M xuống 100, vòng chung kết (stage 2) để giám khảo chính đánh giá kỹ từng người trong 100. Kết quả: thí sinh thực sự giỏi nhất lên đầu — dù vòng sơ loại có thể xếp hạng khác hoàn toàn. Bí quyết không phải là "mô hình mạnh hơn" mà là chia nhỏ vấn đề: tốc độ cho việc dễ, độ chính xác cho việc quan trọng.
Stage 1 trả về top-20. Re-ranker chấm xong, tài liệu tốt nhất đứng hạng 18. Nếu stage 1 chỉ trả top-10, re-ranker có tìm được tài liệu đó không?
Bạn đang xây một chatbot nội bộ bằng RAG, latency yêu cầu < 500 ms, corpus 2 triệu tài liệu, chủ yếu tiếng Việt. Pipeline nào hợp lý nhất?
Giải thích
Re-ranking là bước xếp hạng lại kết quả từ stage 1 (thường là semantic search hoặc hybrid search) bằng một mô hình mạnh hơn — gần như luôn là một cross-encoder Transformer. Nó đưa kết quả liên quan nhất lên đầu và là bước chuẩn trong mọi pipeline RAG chất lượng cao.
Định nghĩa toán học. Cho query q và document d, cross-encoder tính một score thực:
Query và document được concat rồi đi cùng lúc qua Transformer. Mỗi token query có thể attend đến mỗi token document (và ngược lại), nắm bắt tương tác chi tiết mà bi-encoder bỏ lỡ — ví dụ: q "quyền lao động" × d "Điều 5 quy định các quyền cơ bản của người lao động" sẽ match rất mạnh, trong khi d "giá vàng tăng mạnh — người lao động khóc ròng" match yếu.
Bi-encoder, ngược lại, mã hóa riêng:
Vì độc lập, ta có thể precompute Enc(d) cho cả corpus, rồi lookup cực nhanh bằng ANN (HNSW, IVF-PQ). Giá phải trả: không có tương tác token-to-token, dẫn tới sai lệch khi nhiều từ khóa bề mặt nhưng ngữ nghĩa lệch.
Bi-Encoder: Encode riêng. Precompute doc vectors. Cosine / dot-product. O(1) cho mỗi (query-doc) sau precompute. Tối ưu cho retrieval quy mô lớn.
Cross-Encoder: Encode cùng. 1 forward pass mỗi cặp. Chính xác hơn 15–25% NDCG nhưng O(N) mỗi query. Dùng cho re-ranking top-K.
Trade-off cốt lõi: precompute ⇔ interaction. Không có free lunch — muốn tương tác chi tiết, phải trả bằng compute lúc query.
- Top-K stage 1 quá nhỏ. Nếu lấy top-10 mà tài liệu vàng ở hạng 18, re-ranker không thể cứu. Mặc định an toàn: top-100.
- Dùng reranker tiếng Anh cho tiếng Việt. ms-marco-MiniLM được train chủ yếu trên MS MARCO tiếng Anh. Dùng cho tiếng Việt sẽ cho kết quả tồi tệ. Chọn BGE-Reranker-v2-m3 hoặc Cohere Rerank.
- Không batch. Gọi cross-encoder 100 lần riêng lẻ = hàng giây. Batch 100 cặp vào 1 forward pass = hàng trăm ms. GPU luôn thích batch lớn.
- Generative (MonoT5, RankT5): Tái dụng encoder-decoder để sinh token "true"/"false", lấy log-prob làm score. Thường có chất lượng rất cao nhưng nặng; gần đây hay dùng với LLM như ranker (RankGPT, RankLLaMA).
- Cross-encoder classic (BGE-Reranker, MiniLM): Encoder-only, pool [CLS], 1 linear head → 1 logit. Là mặc định của sentence-transformers, rất thân thiện để tự host.
- API managed (Cohere Rerank, Jina Reranker): Không quản lý GPU, giá theo lượng doc. Khởi động nhanh, phù hợp prototype và team nhỏ.
Code mẫu 1 — Cohere Rerank (API managed). Đây là cách nhanh nhất để có một pipeline rerank hoạt động. Không cần GPU, không cần tải model; bạn chỉ truyền query và danh sách doc, service trả về score:
# pip install cohere
import cohere
co = cohere.Client("YOUR_COHERE_API_KEY")
query = "quyền lao động Việt Nam"
documents = [
"Luật lao động 2019 quy định quyền cơ bản của người lao động...",
"Giá vàng hôm nay tăng mạnh — người lao động khóc ròng",
"Hợp đồng lao động — mẫu và hướng dẫn ký kết",
"Công ước ILO 87 và 98 về quyền tự do hiệp hội ở Việt Nam",
# ... top-100 từ stage 1 (bi-encoder / BM25 / hybrid)
]
result = co.rerank(
query=query,
documents=documents,
model="rerank-v3.5", # đa ngôn ngữ, bao gồm tiếng Việt
top_n=5, # chỉ lấy top-5 sau rerank
)
for hit in result.results:
score = hit.relevance_score
text = documents[hit.index]
print(f"{score:.3f}\t{text[:60]}...")
# Output (ví dụ):
# 0.942 Luật lao động 2019 quy định quyền cơ bản của người...
# 0.834 Công ước ILO 87 và 98 về quyền tự do hiệp hội...
# 0.471 Hợp đồng lao động — mẫu và hướng dẫn ký kết
# 0.041 Giá vàng hôm nay tăng mạnh — người lao động khóc...Code mẫu 2 — BGE-Reranker local (open-source). Khi bạn muốn tự kiểm soát (on-prem, nhạy cảm dữ liệu, chi phí dài hạn), một cross-encoder open-source như BGE-Reranker-v2-m3 là lựa chọn cân bằng giữa chất lượng và tốc độ. Chú ý batch để tận dụng GPU:
# pip install sentence-transformers torch
from sentence_transformers import CrossEncoder
import torch
model = CrossEncoder(
"BAAI/bge-reranker-v2-m3",
max_length=512,
device="cuda" if torch.cuda.is_available() else "cpu",
)
query = "quyền lao động Việt Nam"
candidates = [
"Luật lao động 2019 quy định quyền cơ bản của người lao động...",
"Giá vàng hôm nay tăng mạnh — người lao động khóc ròng",
"Công ước ILO 87 và 98 về quyền tự do hiệp hội ở Việt Nam",
# ... top-100 từ stage 1
]
# Tạo các cặp (query, doc) — batch tất cả vào 1 forward pass
pairs = [(query, doc) for doc in candidates]
# predict trả về logit thực. Sigmoid để về xác suất.
scores = model.predict(pairs, batch_size=32, show_progress_bar=False)
# Sắp xếp giảm dần theo score, lấy top-5
ranked = sorted(
zip(candidates, scores),
key=lambda x: x[1],
reverse=True,
)[:5]
for doc, s in ranked:
print(f"{s:+.3f}\t{doc[:60]}...")
# Gợi ý production:
# 1. Luôn batch (batch_size 16–64 trên T4, 64–128 trên A100).
# 2. Cache score cho (query, doc_id) nếu query lặp.
# 3. Với max_length ngắn hơn (256), tốc độ tăng ~2x, chất lượng
# giảm nhẹ trên tài liệu dài — hãy A/B test trên data của bạn.Khi nào dùng. Hãy bật re-ranking khi:
- RAG với LLM — context chất lượng trực tiếp quyết định chất lượng câu trả lời.
- Ngôn ngữ hoặc domain đặc thù (tiếng Việt, y khoa, pháp luật) khiến embedding thuần không đủ tinh tế.
- Bi-encoder của bạn chưa fine-tune — rerank có thể cứu 10–20% NDCG ngay mà không cần train.
Khi nào bỏ qua. Không phải lúc nào cũng cần rerank. Bỏ qua khi:
- Latency SLA rất chặt (< 50 ms) và bạn không thể batch đủ lớn.
- Corpus bé (< 10K) và bi-encoder đã fine-tune tốt trên domain.
- Use case là autocomplete / suggest — người dùng gõ liên tục, chi phí rerank nhân lên nhanh.
Trong thực tế: một pipeline RAG điển hình năm 2024–2025 trông như sau: query rewrite → hybrid search (BM25 + bi-encoder) → top-100 → cross-encoder rerank → top-5 → LLM. Chỉ chi phí và latency từ rerank thường chiếm 20–40% toàn pipeline, nhưng chất lượng câu trả lời cuối cùng cải thiện rõ rệt — và đó là lý do nó trở thành mặc định.
- Re-ranking là stage 2 của pipeline: bi-encoder retrieve top-100 → cross-encoder rerank → top-5 cho LLM.
- Cross-encoder mã hóa (query, doc) CÙNG LÚC nên chính xác hơn 15–25% NDCG, đổi lại O(N) mỗi query.
- Top-K stage 1 quyết định trần recall — quá nhỏ (10) sẽ đánh mất tài liệu tốt; mặc định an toàn là 50–100.
- Ba họ reranker chính: generative (MonoT5), cross-encoder mở (BGE-Reranker, MiniLM), API managed (Cohere Rerank).
- Với tiếng Việt: dùng BGE-Reranker-v2-m3 hoặc Cohere Rerank — tránh các model tiếng Anh thuần.
- Luôn batch các cặp (q, d) trên GPU — đây là đòn bẩy đơn giản nhất để giảm latency 5–10 lần.
Kiểm tra hiểu biết
Tại sao không dùng Cross-Encoder cho toàn bộ 10 triệu tài liệu trong kho?