Mục lục
- Mục tiêu bài học
- Recap Bài 6 — embedding là gì
- Sáu tiêu chí chọn embedding model
- API-based embedding (2024-2026)
- Open-source embedding
- Multilingual và tiếng Việt
- sentence-transformers — chạy local
- OpenAI Embeddings API
- Cohere API và input_type
- Dimensionality choice và Matryoshka
- MTEB — đo chất lượng
- Speed và cost so sánh
- Decision matrix theo case
- Domain-specific embedding
- Fine-tune embedding model
- Embedding consistency
- Normalize và batch encoding
- Inspect chất lượng embedding
- Hybrid sparse + dense
- Pitfall hay gặp
- Demo — so sánh 3 model cùng task
- Bài tập
Mục tiêu bài học
Sau bài này, bạn cần:
- Liệt kê 6 tiêu chí chọn embedding model và đánh giá được model cho use case của mình.
- Phân biệt API-based vs open-source về cost, latency, privacy, vendor lock-in.
- Biết ít nhất 3 lựa chọn cho tiếng Việt: BGE-M3, multilingual-e5, Cohere multilingual-v3.
- Viết code embed text bằng
sentence-transformers, OpenAI SDK, Cohere SDK. - Hiểu khái niệm input_type (Cohere / Voyage) và lý do nó tồn tại.
- Hiểu Matryoshka embedding: truncate dim mà vẫn giữ semantic.
- Biết MTEB là gì, dùng leaderboard để so sánh nhanh model.
- Lập được decision matrix nhanh: prototype, production English, multilingual, budget tight, best quality.
- Tránh các pitfall: đổi model giữa index và query, embed text quá dài, quên
input_type, không cache.
Recap Bài 6 — embedding là gì
Nhắc nhanh để bài này tự lập:
- Embedding = hàm
text → vector ∈ R^d, vớidthường 384, 768, 1024, 1536, 3072. - Similarity giữa 2 text đo bằng cosine similarity của 2 vector: \( \cos(u, v) = \dfrac{u \cdot v}{\lVert u \rVert\, \lVert v \rVert} \).
- Hai text gần nghĩa → vector gần nhau (cosine cao). Hai text khác nghĩa → vector xa nhau.
- Embedding model đã được huấn luyện trên corpus lớn (Common Crawl, Wikipedia, paper, code) bằng contrastive learning — kéo pair gần nghĩa lại gần, đẩy pair khác nghĩa ra xa.
Trong RAG, embedding làm "map" để vector DB tìm chunk liên quan với query. Chất lượng embedding ảnh hưởng trực tiếp đến retrieval — nếu vector của câu hỏi không gần vector của đoạn chứa câu trả lời, RAG sai từ gốc. Vì vậy chọn embedding là một trong những quyết định nặng ký nhất khi xây RAG.
Bài 6 đã cover khái niệm, code cơ bản và vector arithmetic. Bài này tập trung vào tiêu chí chọn trong bối cảnh RAG production 2025-2026.
Sáu tiêu chí chọn embedding model
- Quality — khả năng capture semantic. Đo bằng MTEB hoặc benchmark trên dữ liệu của chính bạn (golden set Q&A).
- Dimension — kích thước vector. Dim càng lớn càng tốn RAM / disk vector DB; ảnh hưởng cost storage và tốc độ similarity search.
- Speed — throughput embedding (text/giây). Quan trọng cho phase Index lớn và query latency.
- Cost — với API: USD / 1M token. Với open-source self-host: chi phí GPU / RAM / dev-ops.
- Multilingual — có hỗ trợ ngôn ngữ bạn cần (tiếng Việt, code, v.v.) không. Model "English-only" embed tiếng Việt thường rất kém.
- Domain — general vs specialized (code, medical, legal). Model general có thể vẫn ổn cho domain hẹp, nhưng model chuyên biệt thường tốt hơn nếu có sẵn.
Quy tắc thực tế: không có model "tốt nhất" cho mọi case. Phải benchmark trên golden set của chính bạn (~30-100 Q&A) trước khi chốt.
API-based embedding (2024-2026)
Provider Model Dim Giá (USD / 1M token)
─────────────────────────────────────────────────────────────────────────────
OpenAI text-embedding-3-small 1536 $0.02
OpenAI text-embedding-3-large 3072 $0.13
Cohere embed-english-v3.0 1024 $0.10
Cohere embed-multilingual-v3.0 1024 $0.10
Voyage AI voyage-3 1024 $0.06
Voyage AI voyage-3-lite 512 $0.02
Google text-embedding-004 768 $0.025
(Giá tham khảo tháng 05/2026, kiểm tra trang giá chính thức trước khi quyết định.)
Ưu điểm API:
- Không cần GPU hay infra ML.
- Auto-scale, throughput cao.
- Cập nhật model tự động (cẩn thận — xem phần consistency ở bước 16).
Nhược điểm API:
- Phụ thuộc network — mỗi embed là một HTTP call.
- Vendor lock-in: đổi nhà cung cấp = re-index toàn bộ vector DB.
- Data rời khỏi network của bạn — không phù hợp với data nhạy cảm.
- Cost tỉ lệ với volume; với corpus rất lớn (10M+ doc) self-host thường rẻ hơn.
Open-source embedding
Bốn họ phổ biến trên Hugging Face (xếp theo lượng dùng 2024-2026):
- BGE family (BAAI, Bắc Kinh) —
bge-small-en-v1.5(384 dim, ~30M params),bge-base-en-v1.5(768 dim, ~110M),bge-large-en-v1.5(1024 dim, ~335M), và BGE-M3 (multilingual, dense + sparse + multi-vector). - E5 family (Microsoft) —
e5-small-v2,e5-base-v2,e5-large-v2, vàmultilingual-e5-largehỗ trợ 100+ ngôn ngữ. Yêu cầu prefix"query: "/"passage: "tương ứng với input. - GTE family (Alibaba) —
gte-small,gte-base,gte-large; phiên bản mớigte-Qwen2-7B-instructdựa trên LLM Qwen2 cho chất lượng cao hơn nhưng nặng hơn. - Nomic embed —
nomic-embed-text-v1.5, hỗ trợ Matryoshka (truncate dim 64-768).
Ưu điểm open-source:
- Cost edge: setup xong embed gần như miễn phí mỗi call (chỉ điện + GPU amortized).
- Data privacy: chạy local, không gửi đi đâu.
- Latency thấp khi self-host trong cùng cluster với vector DB.
- Có quyền fine-tune trên domain riêng.
Nhược điểm: cần GPU (hoặc CPU mạnh cho model nhỏ), tự lo MLOps, quản lý version model.
Multilingual và tiếng Việt
Model "English-only" (bge-large-en, e5-large-v2, text-embedding-3-* ưu tiên English) khi embed tiếng Việt vẫn ra vector, nhưng chất lượng kém — câu hỏi tiếng Việt thường không retrieve được passage tiếng Việt đúng nghĩa. Cho RAG tiếng Việt, chọn từ danh sách multilingual:
- BGE-M3 (BAAI, arXiv 2402.03216) — multilingual 100+ ngôn ngữ; output 3 kiểu: dense (1024 dim), sparse (ColBERT-like), multi-vector. Tốt cho hybrid search. Tiếng Việt benchmark tốt.
- multilingual-e5-large (Microsoft) — 1024 dim, 100+ ngôn ngữ, yêu cầu prefix
query:/passage:. Phiên bảnmultilingual-e5-large-instructhỗ trợ instruction. - Cohere embed-multilingual-v3.0 — API, 1024 dim, 100+ ngôn ngữ, hỗ trợ
input_type. Production-ready, ít hassle deploy. - paraphrase-multilingual-mpnet-base-v2 — sentence-transformers baseline, 768 dim, 50+ ngôn ngữ. Nhỏ, chạy CPU OK, dùng làm baseline rất tốt.
- PhoBERT (VinAI, arXiv 2003.00744) — không phải sentence-embedding sẵn; là encoder tiếng Việt. Có thể tự build sentence embedding bằng mean pooling, hoặc dùng
vinai/phobert-base-v2wrap qua sentence-transformers + fine-tune. Bản thân nó không tối ưu cho retrieval ra-of-the-box.
Khuyến nghị mặc định cho RAG tiếng Việt: BGE-M3 (open-source, miễn phí, mạnh) hoặc Cohere multilingual-v3 (API, ít vận hành). Đánh giá thực tế bằng golden set tiếng Việt — không tin theo lời quảng cáo.
sentence-transformers — chạy local
Thư viện sentence-transformers (Reimers & Gurevych 2019, arXiv 1908.10084) là interface chuẩn cho embedding model local. Phiên bản 5.x (2025) hỗ trợ hầu hết model trên Hugging Face.
pip install sentence-transformers
from sentence_transformers import SentenceTransformer
# 1. Load model (lần đầu sẽ tải weight về cache local)
model = SentenceTransformer("BAAI/bge-base-en-v1.5")
texts = [
"RAG kết hợp retrieval với LLM.",
"Vector database lưu embedding.",
]
embeddings = model.encode(texts)
print(embeddings.shape) # (2, 768)
print(type(embeddings)) # numpy.ndarray
Một số tham số encode hay dùng:
embeddings = model.encode(
texts,
batch_size=64, # gom batch để tận dụng GPU
show_progress_bar=True,
normalize_embeddings=True, # L2 normalize (xem bước 17)
convert_to_numpy=True,
)
Với BGE / E5, kiểm tra README model: một số yêu cầu prefix riêng cho query vs passage để retrieval tối ưu.
OpenAI Embeddings API
pip install openai
export OPENAI_API_KEY="sk-..."
from openai import OpenAI
client = OpenAI()
response = client.embeddings.create(
model="text-embedding-3-small",
input=["text 1", "text 2"],
)
embeddings = [d.embedding for d in response.data]
print(len(embeddings[0])) # 1536
Tham số đáng chú ý của OpenAI v3:
dimensions— truncate vector về dim nhỏ hơn (nhờ Matryoshka). Ví dụdimensions=512chotext-embedding-3-largesẽ giảm 6× storage.encoding_format—"float"(mặc định) hoặc"base64"(tiết kiệm băng thông).
response = client.embeddings.create(
model="text-embedding-3-large",
input=texts,
dimensions=512, # cắt từ 3072 xuống 512
)
Lưu ý: input tối đa 8191 token cho v3. Text dài hơn sẽ bị API từ chối — phải chunk trước.
Cohere API và input_type
pip install cohere
export COHERE_API_KEY="..."
import cohere
co = cohere.Client()
# Khi index doc
response = co.embed(
texts=["RAG kết hợp retrieval với LLM.", "Vector DB lưu embedding."],
model="embed-multilingual-v3.0",
input_type="search_document",
)
doc_embeddings = response.embeddings
# Khi query
response = co.embed(
texts=["RAG là gì?"],
model="embed-multilingual-v3.0",
input_type="search_query",
)
query_embedding = response.embeddings[0]
input_type (Cohere v3 và Voyage):
search_document— đoạn doc trong corpus.search_query— query của user.classification,clustering— các use case khác.
Lý do: doc và query có phân phối khác nhau (doc thường dài, statement; query thường ngắn, dạng câu hỏi). Model áp dụng prefix nội bộ khác cho mỗi loại, đẩy hai phân phối lại gần nhau trong vector space. Nếu quên đặt input_type đúng → retrieval kém rõ rệt. Đây là pitfall hay gặp khi mới chuyển từ OpenAI sang Cohere.
Dimensionality choice và Matryoshka
Trade-off cơ bản:
- Dim lớn (1024-3072): chất lượng cao hơn, tốn RAM vector DB, similarity search chậm hơn.
- Dim nhỏ (256-512): search nhanh, ít storage, chất lượng giảm.
Ví dụ ước lượng storage 1M chunk (mỗi vector float32 = 4 bytes):
Dim Bytes / vector 1M vector
────────────────────────────────────────
384 1,536 1.5 GB
768 3,072 3.0 GB
1024 4,096 4.1 GB
1536 6,144 6.1 GB
3072 12,288 12.3 GB
Matryoshka Representation Learning (Kusupati et al. 2022, arXiv 2205.13147): train model sao cho prefix vector vẫn giữ semantic. Truncate v[:k] không phá hoàn toàn chất lượng.
embedding_full = embed(text) # ví dụ 3072 dim
embedding_512 = embedding_full[:512]
# embedding_512 vẫn semantically meaningful — chất lượng giảm
# ~3-5% trên MTEB chứ không sụp đổ.
Model hỗ trợ Matryoshka: text-embedding-3-small/large (OpenAI), nomic-embed-text-v1.5, một số phiên bản BGE và mxbai-embed-large-v1. Đối với model không train Matryoshka, truncate vector tùy ý sẽ giảm chất lượng đáng kể — đừng làm bừa.
MTEB — đo chất lượng
MTEB (Massive Text Embedding Benchmark, Muennighoff et al. 2022, arXiv 2210.07316) là benchmark chuẩn cho embedding model:
- 56-58 dataset (số đếm đã tăng theo phiên bản).
- 8 task: retrieval, reranking, classification, clustering, pair classification, STS, summarization, bitext mining.
- Leaderboard public trên Hugging Face:
huggingface.co/spaces/mteb/leaderboard.
Top model 2025 (thay đổi liên tục, kiểm tra leaderboard trước khi quyết định):
- NV-Embed-v2 (NVIDIA) — top open-source theo MTEB.
- BGE-M3 — top multilingual.
- voyage-3-large — top API.
- gte-Qwen2-7B-instruct — LLM-based embedding, chất lượng cao, nặng.
Lưu ý hai mặt: MTEB là benchmark chung, không nhất thiết khớp domain của bạn. Một số model "leak" dữ liệu test vào pretrain (đặc biệt với benchmark phổ biến). Quy tắc: dùng MTEB để thu hẹp danh sách ứng viên, sau đó benchmark trên dữ liệu thật của bạn.
Speed và cost so sánh
Throughput (ước lượng, phụ thuộc nhiều vào hardware và độ dài input):
Model size Hardware Throughput
─────────────────────────────────────────────────────────
Small (32-128M) 1× A100 (80GB) ~10,000 text/s
Base (~300M) 1× A100 ~3,000 text/s
Large (300-500M) 1× A100 ~1,000-2,000 text/s
7B LLM-embed 1× A100 ~100-300 text/s
API (batched) — latency 100-500ms / call
Cost so sánh trên 1M token (chỉ embedding, không tính LLM):
Lựa chọn Cost / 1M token
─────────────────────────────────────────────────
OpenAI text-embedding-3-small $0.02
Voyage-3-lite $0.02
Google text-embedding-004 $0.025
Voyage-3 $0.06
Cohere embed-v3.0 $0.10
OpenAI text-embedding-3-large $0.13
Self-host BGE-large (A100 g5) ~$0 mỗi call sau setup
(~$1-3/h GPU)
Ví dụ corpus 1M chunk × 300 token = 300M token. Với text-embedding-3-small: $6 cho phase Index một lần. Với 3-large: $39. Self-host BGE: gần $0 (vài giờ GPU). Quyết định phụ thuộc tần suất reindex và sensitivity của data.
Decision matrix theo case
Case Lựa chọn mặc định
─────────────────────────────────────────────────────────────────
Prototype / small project text-embedding-3-small (OpenAI)
Production English Voyage-3 hoặc BGE-large self-host
Multilingual / tiếng Việt BGE-M3 hoặc Cohere multilingual-v3
Budget tight, on-prem nomic-embed-text-v1.5 local
Best quality, tự host được NV-Embed-v2
Data nhạy cảm (medical, legal) Self-host BGE-M3 hoặc model domain
Code search jina-embeddings-v2-code,
voyage-code-3 (nếu API ok)
Mobile / edge all-MiniLM-L6-v2 (22M, 384 dim)
Quy tắc thực dụng:
- Bắt đầu prototype bằng
text-embedding-3-small— nhanh, rẻ, đủ tốt cho 80% case English. - Nếu RAG tiếng Việt, đổi sang
BGE-M3hoặcCohere multilingual-v3ngay từ đầu. - Khi scale lên (50K+ doc, tần suất reindex thường xuyên), cân nhắc self-host BGE/E5.
- Đo trước, đổi sau — đừng over-engineer ban đầu.
Domain-specific embedding
- Code —
microsoft/codebert-base,jinaai/jina-embeddings-v2-base-code, Voyagevoyage-code-3. Hiểu syntax, function signature, identifier. - Medical / biomedical —
dmis-lab/BioBERT,BiomedNLP/BiomedNLP-PubMedBERT,NeuML/pubmedbert-base-embeddings,MedEmbed. Train trên PubMed. - Legal —
nlpaueb/legal-bert-base-uncased,law-ai/InLegalBERT. Train trên contract, case law. - Finance —
FinBERT,finance-embeddings-investopedia. - Scientific —
allenai/specter2,SciBERT.
Khi nào dùng: domain có nhiều thuật ngữ chuyên ngành (drug name, statute reference, ticker), corpus đủ lớn để embedding general không bắt nghĩa được. Khi nào không cần: domain không quá hẹp, general embedding đã >85% recall trên golden set.
Nếu không có model domain sẵn → fine-tune (bước tiếp).
Fine-tune embedding model
Khi dữ liệu domain rất lệch khỏi pretrain corpus, fine-tune embedding có thể cải thiện đáng kể recall (5-20% trên MTEB-style benchmark domain).
Dữ liệu cần: triple (query, positive_doc, negative_doc). Có thể tạo bằng cách:
- Người gắn nhãn pair query ↔ doc đúng.
- Sinh tự động: dùng LLM viết câu hỏi cho mỗi doc; negative là doc khác trong batch (in-batch negative).
- Mining hard negative: chọn doc gần query nhưng không phải đáp án — khó học hơn, hiệu quả hơn.
Loss phổ biến: contrastive loss, triplet loss, MultipleNegativesRankingLoss (in-batch negative).
from sentence_transformers import SentenceTransformer, losses, InputExample
from torch.utils.data import DataLoader
model = SentenceTransformer("BAAI/bge-base-en-v1.5")
train_examples = [
InputExample(texts=["query A", "positive doc A"]),
InputExample(texts=["query B", "positive doc B"]),
# ...
]
loader = DataLoader(train_examples, shuffle=True, batch_size=32)
loss = losses.MultipleNegativesRankingLoss(model)
model.fit(
train_objectives=[(loader, loss)],
epochs=3,
warmup_steps=100,
)
model.save("./bge-base-domain-tuned")
Lưu ý: fine-tune không phải bước đầu tiên. Trước hết: chọn base tốt, hybrid + rerank, chunking đúng. Khi đã cạn các đòn dễ, mới fine-tune.
Embedding consistency
Quy tắc bất di bất dịch: vector trong DB phải được embed bởi CÙNG model (cùng version) như vector query. Nếu sai → vector ở "không gian khác", retrieval gần như ngẫu nhiên.
Hệ quả thực tế:
- Đổi embedding model (kể cả từ
3-smallsang3-large) → re-index toàn bộ. Không có "patch" cho vector cũ. - API provider có thể âm thầm update model. OpenAI gắn version vào tên (
text-embedding-ada-002,text-embedding-3-small) — không tự đổi giữa version. Nhưng vẫn nên pin tên rõ ràng trong code. - Đối với open-source: ghi snapshot revision (commit hash trên Hugging Face) khi load:
SentenceTransformer("BAAI/bge-base-en-v1.5", revision="abcd123"). - Lưu metadata embedding model + version + dim kèm collection trong vector DB. Khi query, verify khớp.
Plan upfront: trước khi index 1M doc, hãy hỏi: "6 tháng nữa tôi đổi model thì re-index có khả thi không?" Nếu corpus có khả năng đổi model trong tương lai, lưu raw text để dễ re-embed.
Normalize và batch encoding
L2 normalize = chia mỗi vector cho độ dài của nó: \( \hat{v} = v / \lVert v \rVert \). Sau normalize, cosine similarity = dot product → nhanh hơn cho vector DB.
import numpy as np
vec = model.encode("text")
norm = np.linalg.norm(vec)
print(norm) # nếu xấp xỉ 1.0 → đã normalized
# Normalize thủ công
vec_norm = vec / np.linalg.norm(vec)
Hầu hết model BGE / E5 / OpenAI v3 đã output normalized. Vẫn nên check bằng np.linalg.norm. Với sentence-transformers, ép bằng tham số:
embeddings = model.encode(texts, normalize_embeddings=True)
Batch encoding — embed một lần nhiều text nhanh hơn nhiều so với loop từng cái, vì tận dụng song song GPU:
# Chậm
embeddings = [model.encode(t) for t in texts] # tránh
# Nhanh
embeddings = model.encode(texts, batch_size=64)
Batch size tối ưu phụ thuộc VRAM: A100 (80GB) thường chạy được batch 128-256 cho model base; T4 (16GB) thường 32-64.
Inspect chất lượng embedding
Test nhanh chất lượng trước khi commit cho corpus lớn:
import numpy as np
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-base-en-v1.5")
pairs = [
# Cùng nghĩa, kỳ vọng cosine cao (> 0.7)
("How to reset password?", "What is the procedure for resetting my password?"),
# Khác nghĩa, kỳ vọng cosine thấp (< 0.5)
("How to reset password?", "Best Italian restaurant in Hanoi"),
]
for a, b in pairs:
va, vb = model.encode([a, b], normalize_embeddings=True)
cos = float(np.dot(va, vb))
print(f"{cos:+.3f} | {a} || {b}")
Heuristic rough:
- Paraphrase gần như giống nhau → cos > 0.8.
- Cùng chủ đề, diễn đạt khác → 0.6-0.8.
- Khác chủ đề rõ → < 0.5.
- Nếu paraphrase cho < 0.6 → model không phù hợp domain / ngôn ngữ.
Visualize 2D bằng UMAP để mắt thường nhìn cụm — chi tiết ở Bài 6.
Hybrid sparse + dense
Dense embedding (BGE, OpenAI) capture semantic tốt nhưng yếu cho keyword hiếm, mã sản phẩm, tên function. Sparse representation (BM25, SPLADE) match chính xác từ khoá. Hai cái bổ sung nhau.
- BM25 — sparse vector dựa trên term frequency, không cần học, chạy nhanh trên Lucene / Tantivy / Elasticsearch.
- SPLADE (Formal et al. 2021, arXiv 2107.05720) — sparse vector học được, mỗi text → vector vài nghìn chiều, hầu hết = 0, các chiều khác zero là term quan trọng (cả synonym / expansion).
- BGE-M3 — output cả dense + sparse trong một model duy nhất → tiện cho hybrid.
Cách kết hợp (chi tiết ở Bài 39 — similarity search và Bài 35 — hybrid search):
Query → Dense retrieve (top-K) ─┐
├─→ RRF / weighted merge → top-K final
Query → Sparse (BM25, SPLADE) ────┘
Khi chọn embedding cho RAG, nếu biết hybrid là yêu cầu (corpus nhiều keyword kỹ thuật), ưu tiên model output cả dense và sparse như BGE-M3.
Pitfall hay gặp
- Đổi model giữa indexing và querying — vector ở không gian khác, retrieval random. Lỗi này hay xảy ra khi dev đổi model "để test" mà quên reindex.
- Embed text quá dài — API/sentence-transformers truncate âm thầm.
text-embedding-3-*max 8191 token; BGE-base max 512 token. Truncate bỏ phần cuối → embedding bị thiếu thông tin. Phải chunk trước (Bài 38). - Quên input_type với Cohere/Voyage — chất lượng retrieval giảm 10-20%. Luôn set rõ
search_documentcho index,search_querycho query. - Quên prefix với E5 — model E5 yêu cầu prefix
"query: "/"passage: ". Quên → retrieval kém. - Compute embedding mỗi query mà không cache — query lặp lại nhiều (FAQ chatbot) → cache theo hash của query, tiết kiệm 50-90% gọi API.
- Lẫn vector dim — đổi
text-embedding-3-large dim=3072sangdim=512giữa chừng. Cộng/concat sai dim → bug khó debug. - Trust theo benchmark public — MTEB top không bảo đảm tốt nhất cho domain bạn. Benchmark trên golden set của chính bạn.
- Không monitor — embedding API có thể fail, rate limit, drift chất lượng. Log latency và error rate.
Demo — so sánh 3 model cùng task
Mini benchmark: cùng một corpus và query, so sánh OpenAI 3-small, BGE-base, multilingual-e5 trên semantic search.
import os, numpy as np
from sentence_transformers import SentenceTransformer
from openai import OpenAI
corpus = [
"RAG kết hợp LLM với knowledge base ngoài.",
"Vector database lưu embedding và hỗ trợ similarity search.",
"Best Italian restaurant in Hanoi near Hoan Kiem lake.",
"Chunk size 200-800 token thường là điểm khởi đầu hợp lý.",
"Đào tạo lái xe ô tô tại Hà Nội bằng B2.",
]
query = "Vector DB dùng để làm gì?"
def top1(query_vec, doc_vecs, docs):
sims = doc_vecs @ query_vec
i = int(np.argmax(sims))
return docs[i], float(sims[i])
# 1. OpenAI 3-small
client = OpenAI()
def embed_openai(texts):
r = client.embeddings.create(model="text-embedding-3-small", input=texts)
return np.array([d.embedding for d in r.data])
# 2. BGE-base-en (English chính)
bge = SentenceTransformer("BAAI/bge-base-en-v1.5")
def embed_bge(texts):
return bge.encode(texts, normalize_embeddings=True)
# 3. multilingual-e5-base (tiếng Việt ok)
e5 = SentenceTransformer("intfloat/multilingual-e5-base")
def embed_e5_query(text):
return e5.encode(["query: " + text], normalize_embeddings=True)[0]
def embed_e5_passages(texts):
return e5.encode(["passage: " + t for t in texts], normalize_embeddings=True)
for name, embed_doc, embed_query in [
("OpenAI 3-small", embed_openai, lambda q: embed_openai([q])[0]),
("BGE-base-en", embed_bge, lambda q: embed_bge([q])[0]),
("ml-e5-base", embed_e5_passages, embed_e5_query),
]:
doc_vecs = embed_doc(corpus)
q_vec = embed_query(query)
answer, sim = top1(q_vec, doc_vecs, corpus)
print(f"[{name:18s}] sim={sim:+.3f} → {answer}")
Trên corpus tiếng Việt, kỳ vọng: ml-e5-base và (nếu có) BGE-M3 chọn đúng câu về vector DB; BGE-en có thể chọn lệch sang câu English; OpenAI 3-small thường vẫn đúng do hỗ trợ multilingual tốt ở mức ổn. Kết quả thực tế phụ thuộc seed, version model — chạy thử và ghi nhận.
Bài tập
- Soạn 10 câu (5 tiếng Anh + 5 tiếng Việt), một số cặp đồng nghĩa, một số khác chủ đề. Compute cosine similarity matrix 10×10 bằng
multilingual-e5-basevà in ra. Nhận xét: cặp đồng nghĩa có cao hơn 0.7 không? Cặp khác chủ đề có < 0.5 không? - Lấy lại 10 câu ở câu 1. Embed bằng 3 model: OpenAI
3-small, BGE-base-en, multilingual-e5-base. Vẽ heatmap 3 ma trận similarity. So sánh: model nào tách cụm rõ nhất? - Test cross-lingual: viết một câu tiếng Anh và bản dịch tiếng Việt tương ứng (ví dụ "What is RAG?" và "RAG là gì?"). Embed bằng BGE-en (English-only) và bằng BGE-M3 hoặc multilingual-e5. Cosine similarity giữa hai bản dịch ở mỗi model bao nhiêu? Mong đợi: multilingual cao hơn rõ.
- Áp dụng Matryoshka: gọi OpenAI
text-embedding-3-largevớidimensions=3072rồi vớidimensions=512cho cùng 5 câu. So sánh top-1 retrieval với một query — kết quả giống nhau không? Đo % giảm chất lượng (nếu có). - (Tùy chọn) Đọc trang chủ MTEB leaderboard, chọn ra 3 model multilingual top, tải về với
sentence-transformers, benchmark trên golden set 20-30 cặp Q-A tiếng Việt của bạn (có thể tự soạn từ một bài blog cũ). Model nào recall@5 cao nhất?
- Reimers & Gurevych — Sentence-BERT (arXiv 1908.10084)
- Muennighoff et al. — MTEB: Massive Text Embedding Benchmark (arXiv 2210.07316)
- MTEB — Public leaderboard
- Chen et al. — BGE M3-Embedding (arXiv 2402.03216)
- BAAI/bge-m3 — Hugging Face
- intfloat/multilingual-e5-large — Hugging Face
- Kusupati et al. — Matryoshka Representation Learning (arXiv 2205.13147)
- OpenAI — Embeddings guide
- Cohere — Embed v3 documentation
- Voyage AI — Embeddings documentation
- Google — text-embedding-004 documentation
- sentence-transformers — Documentation
- nomic-ai/nomic-embed-text-v1.5 — Hugging Face
- Nguyen & Nguyen — PhoBERT (arXiv 2003.00744)
- Formal et al. — SPLADE (arXiv 2107.05720)
