Mục lục
- Mục tiêu bài học
- Vì sao cần embedding
- Embedding table — lookup từ token ID sang vector
- Embed dim của các model phổ biến
- Embedding được học end-to-end
- Pre-trained word embedding cổ điển
- Static vs contextual embedding
- Vector arithmetic — king - man + woman ≈ queen
- Sentence và document embedding
- OpenAI Embeddings API
- Anthropic, Google và open-source
- Họ embedding model 2024-2025
- Similarity metric
- Code Python — so sánh ba câu
- Use case: semantic search, RAG, clustering
- Visualize embedding 2D bằng UMAP
- Multilingual embedding
- Cost và latency
- Token embedding vs sentence embedding
- Bài tập
- Tóm tắt
Mục tiêu bài học
Sau bài này, bạn cần trả lời được:
- Vì sao không thể đưa thẳng token ID vào model — phải qua bước embedding.
- Embedding table trong LLM có shape gì, được khởi tạo và học ra sao.
- Static embedding (Word2Vec, GloVe) khác contextual embedding (BERT, GPT) ở chỗ nào.
- Sentence embedding cho RAG / semantic search lấy từ đâu, OpenAI và BGE / E5 cho dim bao nhiêu.
- Tính cosine similarity giữa hai vector và đọc kết quả.
Bài 5 (Vocabulary và token ID) đã chỉ ra rằng model làm việc với token ID là số nguyên. Bài này giải thích bước kế tiếp: dùng embedding table biến mỗi ID thành một vector dày đặc trong không gian \( \mathbb{R}^d \).
Vì sao cần embedding
Token ID là chỉ số arbitrary: ID 15.496 ("Hello") và ID 15.497 ("Helloworld") là hai số liền nhau nhưng không có quan hệ "gần" về ngữ nghĩa. Đưa thẳng số nguyên vào mạng neural không hợp lý — neural network học hàm liên tục, ID phải được map sang biểu diễn liên tục trước.
Hai lựa chọn naive và lý do bị loại:
- One-hot encoding: mỗi token là vector
[0, 0, ..., 1, ..., 0]dài \( V \). Khoảng cách giữa mọi cặp one-hot vector bằng nhau → không capture được similarity. Hơn nữa, \( V \) tới 100k-256k → vector quá thưa, không khả thi. - ID trực tiếp như scalar: model phải tự "đoán" ý nghĩa từ con số ID, vốn được gán tùy theo thứ tự merge trong BPE — không có signal hữu ích.
Dense embedding giải quyết cả hai vấn đề:
- Mỗi token thành vector \( \mathbf{e} \in \mathbb{R}^d \) với \( d \) trong khoảng vài trăm tới vài nghìn (so với \( V \) hàng chục nghìn).
- Vector dày đặc (mọi chiều đều có giá trị float).
- Vector được học sao cho token gần nhau về nghĩa thì gần nhau trong không gian \( \mathbb{R}^d \).
Embedding table — lookup từ token ID sang vector
Embedding table là một ma trận tham số:
\[ E \in \mathbb{R}^{V \times d} \]
Với mỗi token ID \( i \in \{0, 1, \dots, V-1\} \), embedding của token là hàng thứ \( i \):
\[ \mathbf{e}_i = E[i, :] \in \mathbb{R}^{d} \]
Đây là phép tra bảng (lookup), không phải nhân ma trận đầy đủ — implement trong PyTorch là nn.Embedding:
import torch
import torch.nn as nn
V, d = 50257, 768
embed = nn.Embedding(num_embeddings=V, embedding_dim=d)
ids = torch.tensor([15496, 11, 995]) # "Hello, world"
vectors = embed(ids) # shape (3, 768)
print(vectors.shape) # torch.Size([3, 768])
Phía dưới, nn.Embedding chính là E[ids] — gather theo chỉ số. Một câu có \( n \) token sẽ thành tensor shape \( (n, d) \). Một batch \( B \) câu padding tới độ dài \( L \) sẽ là tensor \( (B, L, d) \) — đầu vào của lớp Transformer đầu tiên.
Embed dim của các model phổ biến
\( d \) còn gọi là hidden size, dùng xuyên suốt mọi lớp Transformer. Một số model tham chiếu:
Model d (hidden / embed dim)
─────────────────────────────────────────
BERT base 768
BERT large 1024
GPT-2 small 768
GPT-2 medium 1024
GPT-2 large 1280
GPT-2 XL 1600
GPT-3 175B 12288
Llama 2 7B 4096
Llama 3 8B 4096
Llama 3 70B 8192
Llama 3 405B 16384
Mistral 7B 4096
Gemma 2 9B 3584
Qwen2 7B 3584
\( d \) tăng theo kích thước model, nhưng không tuyến tính — model lớn ưu tiên tăng số layer và số head trước, \( d \) tăng chậm hơn. Quan hệ kinh nghiệm \( d \approx \sqrt{\#params / \#layers} \) (Hoffmann et al. 2022, Chinchilla).
Chú ý phân biệt với embedding dim của embedding model (text-embedding-3-small: 1536) — đó là kích thước sentence embedding ở output, khác với hidden size nội bộ của LLM.
Embedding được học end-to-end
Trong LLM hiện đại, embedding table \( E \) là tham số học chung với phần còn lại của model:
- Khởi tạo \( E \) ngẫu nhiên (truncated normal, std nhỏ).
- Forward pass: lookup vector, đi qua Transformer, ra logit.
- Cross-entropy loss giữa logit và token kế tiếp.
- Backprop tới \( E \) — mỗi token ID xuất hiện trong batch sẽ được cập nhật gradient cho hàng tương ứng.
Token xuất hiện thường xuyên (" the", ",") được cập nhật hàng tỷ lần qua training → embedding hội tụ tới biểu diễn ổn định. Token hiếm cập nhật ít → biểu diễn nhiễu, đôi khi trở thành anomalous token (xem bài 5, mục 15).
Hệ quả quan trọng: embedding của LLM hiện đại không độc lập. Vector \( \mathbf{e}_i \) chỉ có ý nghĩa khi đi cùng phần còn lại của model — không thể trích embedding table của Llama 3 ra dùng riêng cho semantic search; muốn vector cho retrieval phải dùng embedding model dành riêng (mục 9-12).
Pre-trained word embedding cổ điển
Trước khi Transformer phổ biến, word embedding là kết quả của các thuật toán dành riêng, train trên corpus rất lớn rồi dùng làm input feature cho model downstream:
- Word2Vec (Mikolov et al. 2013): hai biến thể.
- CBOW (Continuous Bag of Words): từ các từ ngữ cảnh xung quanh, dự đoán từ trung tâm.
- Skip-gram: từ từ trung tâm, dự đoán các từ ngữ cảnh.
- GloVe (Pennington et al. 2014, Stanford): factorize ma trận thống kê đồng xuất hiện toàn cục (global co-occurrence matrix). Output: 50, 100, 200, 300 chiều train trên Wikipedia + Gigaword hoặc Common Crawl.
- FastText (Bojanowski et al. 2017, Facebook): mở rộng Word2Vec để embed cả subword (character n-gram). Một từ có thể không nằm trong vocab nhưng vẫn ra vector từ tổ hợp n-gram → xử lý được OOV và morphological language (tiếng Phần Lan, tiếng Đức).
Đặc điểm chung: train một lần, dùng lại. Mỗi từ ánh xạ tới đúng một vector, không phụ thuộc câu. Đây là static embedding — đối lập với contextual embedding mà các model dựa trên Transformer cho.
Static vs contextual embedding
Static embedding (Word2Vec, GloVe, FastText): một từ — một vector. Từ "bank" trong "river bank" và "bank account" được biểu diễn bởi cùng một vector duy nhất, dù nghĩa hoàn toàn khác.
Contextual embedding (ELMo 2018, BERT 2018, GPT, mọi LLM 2024-2026): vector phụ thuộc câu chứa từ đó. Cùng từ "bank" sinh hai vector khác nhau khi đi với "river" và với "account". Cụ thể: lấy hidden state đầu ra của lớp Transformer cuối cho vị trí token tương ứng.
from transformers import AutoTokenizer, AutoModel
import torch
tok = AutoTokenizer.from_pretrained("bert-base-uncased")
model = AutoModel.from_pretrained("bert-base-uncased").eval()
def bank_vector(sentence):
inputs = tok(sentence, return_tensors="pt")
with torch.no_grad():
out = model(**inputs).last_hidden_state # (1, L, 768)
# Tìm vị trí token "bank"
tokens = tok.convert_ids_to_tokens(inputs["input_ids"][0])
idx = tokens.index("bank")
return out[0, idx] # (768,)
v1 = bank_vector("I deposited money at the bank.")
v2 = bank_vector("We sat by the river bank.")
print(torch.cosine_similarity(v1, v2, dim=0).item()) # ~ 0.5 (không cao)
Với static embedding, hai câu trên cho cùng vector cho "bank" → cosine = 1. Với contextual embedding, cosine ~ 0.5 thể hiện model nhận biết hai nghĩa khác nhau. Đây là lý do mọi NLP task hiện đại đều dùng contextual embedding.
Vector arithmetic — king - man + woman ≈ queen
Phát hiện nổi tiếng của Mikolov 2013: vector Word2Vec có tính chất số học encode quan hệ ngữ nghĩa:
\[ \mathbf{v}_{\text{king}} - \mathbf{v}_{\text{man}} + \mathbf{v}_{\text{woman}} \approx \mathbf{v}_{\text{queen}} \]
Diễn giải: chiều "giới tính" tồn tại như một hướng nhất quán trong không gian vector. Hiệu \( \mathbf{v}_{\text{king}} - \mathbf{v}_{\text{man}} \) tách ra phần "hoàng gia trừ giới tính nam", cộng \( \mathbf{v}_{\text{woman}} \) trở thành "hoàng gia + nữ" = queen.
Quan hệ tương tự cho nhiều cặp:
Paris - France + Italy ≈ Rome (thủ đô)
walking - walk + swim ≈ swimming (V-ing)
better - good + bad ≈ worse (so sánh hơn)
biggest - big + small ≈ smallest (so sánh nhất)
Lưu ý: hiệu ứng này rõ với Word2Vec / GloVe train trên corpus rất lớn (Google News, Wikipedia). Với embedding chiết từ BERT hay LLM hiện đại, quan hệ không còn "sạch" như vậy vì vector encode rất nhiều chiều tín hiệu khác (cú pháp, ngữ cảnh, hậu tố BPE).
Bản chất tính chất này phản ánh hypothesis phân phối (Harris 1954, Firth 1957): "you shall know a word by the company it keeps" — từ có ngữ cảnh giống nhau thì biểu diễn gần nhau, và quan hệ ngữ nghĩa biểu hiện thành các hướng tuyến tính trong không gian.
Sentence và document embedding
Token embedding cho 1 vector / token. Để biểu diễn một câu hoặc đoạn văn bằng 1 vector duy nhất (dùng cho semantic search, clustering), cần bước aggregation:
- Mean pooling: trung bình các token embedding của câu (chỉ tính token thật, bỏ qua padding theo
attention_mask). Đơn giản, dùng phổ biến nhất. - CLS token (BERT): dùng hidden state tại vị trí
[CLS]ở đầu chuỗi. BERT được pretrain với task NSP nên[CLS]được khuyến khích "tóm tắt" cả câu. - Sentence-BERT (SBERT) (Reimers & Gurevych 2019): fine-tune BERT thêm bước dùng siamese network và contrastive loss trên cặp câu (NLI, STS). Output là vector chất lượng cao cho similarity, nhanh hơn cross-encoder hàng nghìn lần.
SBERT mở đầu kỷ nguyên sentence embedding hiện đại. Hệ thống "embedding model" gọi qua API hôm nay (OpenAI text-embedding-3, BGE, E5, Cohere embed) đều dựa trên ý tưởng tương tự: train encoder với contrastive loss trên hàng tỷ cặp câu để nhúng câu giống nhau gần nhau.
Use case sentence / document embedding:
- Semantic search — tìm document gần query nhất theo nghĩa, không theo keyword.
- RAG — retrieve top-k chunk context trước khi đưa vào LLM (xem bài 36 — RAG Pipeline).
- Clustering — gom document tương tự.
- Classification — dùng vector làm feature cho logistic regression / kNN.
- Recommendation — gợi ý item gần với item đã thích.
- Deduplication — phát hiện cặp text gần trùng.
OpenAI Embeddings API
OpenAI cung cấp endpoint embedding riêng, không phải qua chat completion. Gọi qua SDK openai v1.x:
from openai import OpenAI
client = OpenAI()
response = client.embeddings.create(
model="text-embedding-3-small",
input="Hello, AI Engineer!",
)
vector = response.data[0].embedding
print(len(vector)) # 1536 cho text-embedding-3-small
Model embedding của OpenAI (cập nhật tới 2026):
Model Dim Cost ($/1M token)
──────────────────────────────────────────────────────
text-embedding-3-small 1536 $0.02
text-embedding-3-large 3072 $0.13
text-embedding-ada-002 1536 $0.10 (legacy)
Hai feature mới so với ada-002:
- Dimensions parameter: có thể yêu cầu chỉ trả vector ngắn hơn (ví dụ 256, 512) — kỹ thuật Matryoshka embedding (Kusupati et al. 2022) cho phép cắt đầu cuối vector mà vẫn giữ phần lớn thông tin. Tiết kiệm bộ nhớ vector DB.
- Batch tới 2048 input mỗi request → throughput cao.
resp = client.embeddings.create(
model="text-embedding-3-large",
input=["Câu 1", "Câu 2", "Câu 3"],
dimensions=512, # cắt từ 3072 xuống 512
)
vectors = [d.embedding for d in resp.data]
print(len(vectors), len(vectors[0])) # 3, 512
Anthropic, Google và open-source
Anthropic: tính tới 2025-2026 chưa có embedding API native. Anthropic recommend dùng Voyage AI (đối tác) hoặc OpenAI / open-source cho retrieval, còn Claude chỉ làm phần generation trong pipeline RAG.
Google: Vertex AI cung cấp text-embedding-004, text-embedding-005 và gemini-embedding-001 (3072 dim). Dùng qua client google-generativeai.
import google.generativeai as genai
genai.configure(api_key="...")
result = genai.embed_content(
model="models/text-embedding-004",
content="Hello, AI Engineer!",
)
print(len(result["embedding"])) # 768
Open-source qua sentence-transformers: chạy local, không qua API. Dim và chất lượng đa dạng.
from sentence_transformers import SentenceTransformer
# BGE từ BAAI, top MTEB leaderboard
model = SentenceTransformer("BAAI/bge-large-en-v1.5")
vectors = model.encode([
"Tôi yêu lập trình",
"Mình thích coding",
"Hôm nay trời đẹp",
])
print(vectors.shape) # (3, 1024)
Lựa chọn API hay open-source phụ thuộc yêu cầu privacy, latency, cost. Pipeline RAG production thường mix: dev / prototype dùng OpenAI cho nhanh; scale lên dùng BGE / E5 self-hosted để giảm chi phí.
Họ embedding model 2024-2025
Một số embedding model phổ biến trong giai đoạn 2024-2026 (số xếp hạng tương đối, MTEB cập nhật liên tục):
Model Dim Nhà phát triển
─────────────────────────────────────────────────────────────
text-embedding-3-large 3072 OpenAI
text-embedding-3-small 1536 OpenAI
voyage-3-large 1024 Voyage AI (Anthropic recommend)
gemini-embedding-001 3072 Google
embed-english-v3.0 1024 Cohere
embed-multilingual-v3.0 1024 Cohere
BAAI/bge-large-en-v1.5 1024 BAAI (open)
BAAI/bge-m3 1024 BAAI multilingual (open)
intfloat/e5-large-v2 1024 Microsoft (open)
intfloat/multilingual-e5-large 1024 Microsoft multilingual (open)
nvidia/NV-Embed-v2 4096 NVIDIA (open)
mixedbread-ai/mxbai-embed-large-v1 1024 Mixedbread (open)
So sánh trên MTEB (Massive Text Embedding Benchmark, Muennighoff et al. 2023): bộ benchmark gộp 56 dataset trên 8 task (classification, clustering, retrieval, reranking, STS, summarization, pair classification, bitext mining). Open-source BGE / E5 / NV-Embed thường vượt OpenAI ở subset retrieval, nhưng OpenAI vẫn dẫn ở một số task khác. Khi chọn model, đo trên dữ liệu của chính use case quan trọng hơn xếp hạng tổng thể.
Một số embedding model (E5, BGE, Cohere) yêu cầu prefix khác nhau cho query và document:
model = SentenceTransformer("intfloat/multilingual-e5-large")
q_vec = model.encode("query: cách train tokenizer")
d_vec = model.encode("passage: Tokenizer được train bằng thuật toán BPE...")
# Cosine giữa hai prefix khác nhau sẽ cho kết quả đúng;
# nếu cùng prefix "passage:" thì sai semantic ý đồ.
Similarity metric
Đo "gần nhau" giữa hai vector \( \mathbf{u}, \mathbf{v} \in \mathbb{R}^d \):
Cosine similarity — chuẩn de facto cho text embedding:
\[ \cos\theta = \frac{\mathbf{u} \cdot \mathbf{v}}{\|\mathbf{u}\|\,\|\mathbf{v}\|} \in [-1, 1] \]
Đo góc giữa hai vector, không phụ thuộc độ dài. Giá trị 1 = trùng hướng, 0 = vuông góc, -1 = ngược hướng.
Dot product: \( \mathbf{u} \cdot \mathbf{v} \). Nếu vector đã L2-normalize (chuẩn 1) thì dot product trùng cosine. Nhiều embedding model trả về vector đã normalize → vector DB chỉ cần lưu raw vector và so dot product (nhanh hơn).
Euclidean distance: \( \|\mathbf{u} - \mathbf{v}\|_2 \). Ít dùng cho text embedding vì nhạy với độ dài vector và không tương đương cosine khi vector chưa normalize.
Khoảng giá trị cosine giữa hai câu khi embed bằng OpenAI / BGE thường nằm trong [0.2, 0.95]:
- Diễn đạt lại cùng ý: ~ 0.85 - 0.95.
- Cùng chủ đề khác chi tiết: ~ 0.60 - 0.80.
- Không liên quan: ~ 0.20 - 0.40.
- Hiếm khi xuống dưới 0.1 — vector embedding thường nằm trong cone hẹp của không gian.
Code Python — so sánh ba câu
Tính cosine giữa các câu bằng sentence-transformers local:
import numpy as np
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-small-en-v1.5")
def cosine_sim(a, b):
return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
sentences = [
"I love programming.",
"Coding is my passion.",
"The weather is nice today.",
]
e1, e2, e3 = model.encode(sentences)
print(f"sim(1, 2) = {cosine_sim(e1, e2):.3f}") # cao ~ 0.85+
print(f"sim(1, 3) = {cosine_sim(e1, e3):.3f}") # thấp ~ 0.30
print(f"sim(2, 3) = {cosine_sim(e2, e3):.3f}") # thấp ~ 0.30
Phiên bản dùng OpenAI:
from openai import OpenAI
import numpy as np
client = OpenAI()
def embed(text):
r = client.embeddings.create(model="text-embedding-3-small", input=text)
return np.array(r.data[0].embedding)
e1 = embed("Tôi yêu lập trình")
e2 = embed("Mình thích coding")
e3 = embed("Hôm nay trời đẹp")
def cos(a, b):
return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
print(cos(e1, e2), cos(e1, e3)) # ~ 0.6+ và ~ 0.2
Số cụ thể thay đổi tùy model và phiên bản, nhưng quy luật "câu cùng ý gần nhau hơn câu khác chủ đề" luôn đúng.
Use case: semantic search, RAG, clustering
Mẫu chung của ứng dụng embedding:
- Index: tính embedding cho mọi document trong corpus, lưu vào vector DB (FAISS, Qdrant, Pinecone, Milvus, pgvector).
- Query: tính embedding cho câu hỏi của user.
- Retrieve: tìm top-k document có cosine cao nhất với query (approximate nearest neighbor, HNSW / IVF).
- Sử dụng: trả về cho user (semantic search), đưa làm context vào LLM (RAG), hoặc làm feature cho task downstream.
import numpy as np
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-small-en-v1.5")
corpus = [
"Python is a high-level programming language.",
"Machine learning models require large datasets.",
"Pizza is a popular Italian dish.",
"Neural networks consist of layers of neurons.",
"Football is the most popular sport in the world.",
]
corpus_vecs = model.encode(corpus, normalize_embeddings=True)
query = "What is deep learning?"
q_vec = model.encode(query, normalize_embeddings=True)
# Vì normalized → dot product = cosine
scores = corpus_vecs @ q_vec
top_k = np.argsort(scores)[::-1][:3]
for i in top_k:
print(f"{scores[i]:.3f} {corpus[i]}")
Đầu ra: hai câu liên quan ML / neural network được ưu tiên, "pizza" và "football" rớt hẳn xuống. Đây chính xác là backbone của một pipeline RAG (bài 35-41 sẽ đi chi tiết với vector DB, chunking và reranker).
Các use case khác dùng cùng kỹ thuật vector:
- Clustering bằng KMeans / HDBSCAN trên ma trận embedding để gom document theo chủ đề.
- Classification zero-shot: embed label name, embed document, gán label theo cosine cao nhất.
- Recommendation: embed item description, gợi ý item gần với item user đã thích.
- Deduplication: cosine > 0.95 thường là duplicate gần đúng.
Visualize embedding 2D bằng UMAP
Vector \( d \approx 1024 \) không xem được trực tiếp. Hai thuật toán giảm chiều hay dùng:
- t-SNE (van der Maaten & Hinton 2008): bảo toàn cấu trúc local, tốt cho visualize cluster nhỏ. Chậm với dataset lớn (10k+).
- UMAP (McInnes et al. 2018): bảo toàn cả local và global structure, nhanh hơn t-SNE 5-10×, scale tốt hơn. Khuyến nghị mặc định.
import umap
import matplotlib.pyplot as plt
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-small-en-v1.5")
texts = [
# AI / ML
"Neural networks have many layers.",
"Deep learning needs large datasets.",
"Transformers revolutionized NLP.",
# Cooking
"Pasta is boiled in salted water.",
"Italian pizza uses tomato and mozzarella.",
"Bread dough rises with yeast.",
# Sport
"Football is the world's most popular sport.",
"The marathon distance is 42.195 km.",
"Tennis matches use a best-of-five format.",
# Weather
"It will rain heavily tonight.",
]
vecs = model.encode(texts)
reducer = umap.UMAP(n_components=2, random_state=42)
xy = reducer.fit_transform(vecs)
plt.figure(figsize=(8, 6))
plt.scatter(xy[:, 0], xy[:, 1])
for i, t in enumerate(texts):
plt.annotate(t[:30], (xy[i, 0], xy[i, 1]), fontsize=8)
plt.tight_layout()
plt.savefig("umap.png", dpi=120)
Câu cùng chủ đề (AI, cooking, sport) sẽ tụ thành cụm tách biệt trên mặt phẳng. Đây là cách trực quan đầu tiên để kiểm tra chất lượng embedding model cho corpus cụ thể.
Multilingual embedding
Embedding model multilingual nhúng các ngôn ngữ khác nhau vào cùng không gian vector, sao cho câu cùng nghĩa nhưng khác ngôn ngữ vẫn gần nhau. Một số lựa chọn:
- LaBSE (Feng et al. 2022, Google): hỗ trợ 109 ngôn ngữ, 768 dim. Train với cặp song ngữ.
- multilingual-E5 (Wang et al. 2024, Microsoft): family
multilingual-e5-base/large, hỗ trợ 100+ ngôn ngữ. - BGE-M3 (BAAI 2024): 100+ ngôn ngữ, đồng thời cho dense / sparse / multi-vector retrieval trong một model.
- text-embedding-3-large (OpenAI): hỗ trợ tiếng Việt và các ngôn ngữ chính, dù tài liệu OpenAI không liệt kê đầy đủ.
- embed-multilingual-v3.0 (Cohere): 100+ ngôn ngữ.
from sentence_transformers import SentenceTransformer
import numpy as np
model = SentenceTransformer("intfloat/multilingual-e5-large")
def cos(a, b):
return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))
v_vi = model.encode("query: Tôi yêu lập trình")
v_en = model.encode("query: I love programming")
v_unrelated = model.encode("query: The weather is nice")
print(cos(v_vi, v_en)) # cao — cross-lingual hiểu cùng nghĩa
print(cos(v_vi, v_unrelated)) # thấp
Hệ quả thực tế: có thể build search hỏi tiếng Việt và truy hồi document tiếng Anh trong cùng một index — vector DB không cần biết ngôn ngữ.
Cost và latency
Embedding API rẻ hơn LLM chat completion vài bậc:
Service $/1M token input (tháng 5/2026)
──────────────────────────────────────────────────────────────────
text-embedding-3-small $0.02
text-embedding-3-large $0.13
GPT-4o (input) $2.50 — chat completion
GPT-4o mini (input) $0.15 — chat completion
Cohere embed-english-v3.0 $0.10
Voyage-3-large $0.18
Ước tính: embed toàn bộ Wikipedia tiếng Anh (~ 4 tỷ token) bằng text-embedding-3-small chỉ tốn ~ $80. Vì vậy index lớn hợp lý cho production.
Latency thấp (50-200 ms / request), batch tới 2048 input mỗi call → embed 1 triệu document trong vài giờ. Self-host BGE / E5 trên 1 GPU H100 / L4 cho throughput vài nghìn câu/giây — chi phí biên gần như bằng tiền điện.
Lưu ý chi phí ẩn:
- Vector DB lưu trữ: 1536 dim × 4 byte = 6 KB / vector. 1 tỷ vector ~ 6 TB → cân nhắc Matryoshka cắt dim hoặc product quantization.
- Re-embedding khi đổi model: phải tính lại toàn bộ corpus. Tránh đổi model embedding tuỳ tiện.
Token embedding vs sentence embedding
Hai khái niệm dễ nhầm:
Token embedding Sentence embedding
─────────────────────────────────────────────────────────────────────────────
Granularity 1 vector / token 1 vector / câu hoặc đoạn
Output ở đâu Layer đầu LLM (lookup E) Encoder model riêng + pooling
Mục đích Input cho Transformer layer Retrieval, clustering, RAG
Ví dụ model Llama 3 (E), GPT-2 (E) text-embedding-3, BGE, E5
Dim Hidden size LLM (768-16384) 768-4096 tùy model
Cùng cơ sở toán học — đều là vector trong \( \mathbb{R}^d \), đều có thể đo cosine — nhưng dùng cho hai mục đích khác nhau, không thay thế cho nhau:
- Không thể lấy embedding table của Llama 3 (token embedding) trung bình các token rồi dùng cho semantic search. Kết quả tệ vì \( E \) không được tối ưu cho mục tiêu này.
- Ngược lại, không thể nhồi vector của text-embedding-3 vào input của Llama 3 — dim không khớp, semantics không khớp.
Một bridge thú vị: bài Encoder-only Transformer (bài 18) sẽ chỉ ra rằng các sentence embedding model về bản chất là BERT / RoBERTa fine-tune với contrastive loss — tức là chính LLM nhỏ với pooling, được tối ưu cho mục tiêu khác.
Bài tập
- Compute embedding cho 5 câu (3 cùng chủ đề lập trình, 2 chủ đề khác). In ma trận cosine 5×5. Xác minh khối 3×3 phía trái trên có giá trị cao hơn các ô còn lại.
- Compare OpenAI
text-embedding-3-smallvớiBAAI/bge-small-en-v1.5: encode cùng 20 câu, tính cosine của mọi cặp, so sánh phân phối giá trị (mean, std, max, min). Embedding nào tách biệt cluster tốt hơn? - Cho 100 câu corpus và 1 query, find top-5 câu gần nhất với query bằng cosine. Đo thời gian: dùng vòng for vs dùng matmul với numpy.
- Visualize embedding cho 20 câu (4 chủ đề × 5 câu) bằng UMAP 2D. Tô màu theo chủ đề. Bình luận về độ tách cluster.
- (Tuỳ chọn) Implement Matryoshka manual: lấy text-embedding-3-large (3072 dim), cắt còn 256, 512, 1024, 2048 dim đầu tiên. Đo accuracy retrieval top-1 trên một bộ query / passage. Quan sát accuracy giảm bao nhiêu khi cắt sâu.
- (Tuỳ chọn) Với multilingual-e5-large, kiểm tra cross-lingual: 10 cặp câu Việt - Anh cùng nghĩa, 10 cặp không cùng nghĩa. Cosine của cặp đồng nghĩa có cao hơn rõ rệt không?
Tóm tắt
- Embedding biến token ID thành vector liên tục \( \mathbf{e} \in \mathbb{R}^d \). Embedding table \( E \in \mathbb{R}^{V \times d} \), lookup bằng
nn.Embedding. - \( d \) (hidden size): BERT/GPT-2 small 768, Llama 3 8B 4096, GPT-3 12288, Llama 3 405B 16384.
- LLM hiện đại học embedding end-to-end. Vector chỉ có ý nghĩa khi đi kèm phần còn lại của model.
- Static embedding (Word2Vec, GloVe, FastText): 1 từ 1 vector. Contextual embedding (BERT, GPT): vector phụ thuộc câu — chuẩn hiện đại.
- Sentence embedding cho retrieval: SBERT, text-embedding-3, BGE, E5, Cohere, Voyage. Aggregation thường là mean pooling hoặc CLS.
- Similarity metric phổ biến: cosine \( = \mathbf{u} \cdot \mathbf{v} / (\|\mathbf{u}\| \|\mathbf{v}\|) \). Vector đã normalize → dot product = cosine.
- Use case: semantic search, RAG, clustering, classification, recommendation, deduplication.
- Cost embedding rẻ hơn LLM chat hai bậc; latency thấp; visualize 2D bằng UMAP / t-SNE.
- Token embedding (lookup trong LLM) và sentence embedding (cho retrieval) là hai khái niệm khác nhau, không thay thế cho nhau.
Bài 7 (Context window) sẽ bàn về giới hạn số token mà mỗi LLM xử lý được trong một lượt forward, các kỹ thuật mở rộng context (RoPE, ALiBi, sliding window) và đánh đổi chất lượng / chi phí.
- Mikolov et al. 2013 — Efficient Estimation of Word Representations (Word2Vec, arXiv:1301.3781)
- Pennington et al. 2014 — GloVe: Global Vectors for Word Representation
- Bojanowski et al. 2017 — Enriching Word Vectors with Subword Information (FastText, arXiv:1607.04606)
- Devlin et al. 2018 — BERT: Pre-training of Deep Bidirectional Transformers (arXiv:1810.04805)
- Reimers & Gurevych 2019 — Sentence-BERT (arXiv:1908.10084)
- Muennighoff et al. 2023 — MTEB: Massive Text Embedding Benchmark (arXiv:2210.07316)
- Kusupati et al. 2022 — Matryoshka Representation Learning (arXiv:2205.13147)
- Feng et al. 2022 — Language-agnostic BERT Sentence Embedding (LaBSE, arXiv:2007.01852)
- Wang et al. 2024 — Multilingual E5 Text Embeddings (arXiv:2402.05672)
- Chen et al. 2024 — BGE M3-Embedding (arXiv:2402.03216)
- OpenAI — Embeddings guide
- sentence-transformers documentation
- MTEB Leaderboard (Hugging Face)
- UMAP documentation
