Bài 4: Embeddings & Vector Databases¶
Tổng quan¶
Embeddings chuyển đổi text thành vector số để có thể tính toán semantic similarity. Vector databases lưu trữ và tìm kiếm hàng triệu vectors hiệu quả. Đây là nền tảng kỹ thuật của RAG.
1. Embeddings¶
Khái niệm¶
Embedding là quá trình ánh xạ text → vector trong không gian nhiều chiều, sao cho các text có nghĩa tương đồng thì vector gần nhau.
"con chó" → [0.2, 0.8, 0.1, ..., 0.4] (768 dimensions)
"chó cưng" → [0.2, 0.7, 0.2, ..., 0.3] ← Gần với "con chó"
"xe hơi" → [0.9, 0.1, 0.8, ..., 0.2] ← Xa với "con chó"
Sentence-transformers¶
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("sentence-transformers/paraphrase-multilingual-mpnet-base-v2")
sentences = [
"Hợp đồng lao động phải được ký kết bằng văn bản.",
"Thỏa thuận làm việc cần có chữ ký giấy tờ.",
"Giá cổ phiếu VNM hôm nay tăng 2%."
]
embeddings = model.encode(sentences)
print(embeddings.shape) # (3, 768)
OpenAI Embeddings¶
from openai import OpenAI
client = OpenAI()
response = client.embeddings.create(
model="text-embedding-3-small", # 1536 dims, rẻ hơn
# model="text-embedding-3-large", # 3072 dims, chất lượng cao hơn
input="Điều kiện thành lập công ty TNHH là gì?"
)
vector = response.data[0].embedding
print(len(vector)) # 1536
Vietnamese Models¶
| Model | Dimensions | Đặc điểm |
|---|---|---|
intfloat/multilingual-e5-large |
1024 | Tốt cho tiếng Việt, open-source |
BAAI/bge-m3 |
1024 | State-of-the-art multilingual, hỗ trợ dense + sparse |
keepitreal/vietnamese-sbert |
768 | Fine-tuned riêng cho tiếng Việt |
text-embedding-3-small (OpenAI) |
1536 | Trả phí, tiện lợi |
# BGE-M3 - recommended cho tiếng Việt
from FlagEmbedding import BGEM3FlagModel
model = BGEM3FlagModel("BAAI/bge-m3", use_fp16=True)
# Dense embeddings
embeddings = model.encode(
["Luật doanh nghiệp Việt Nam 2020"],
batch_size=12,
max_length=8192,
)["dense_vecs"]
2. Similarity Metrics¶
Cosine Similarity¶
Đo góc giữa 2 vectors - không phụ thuộc vào magnitude (độ dài vector).
cosine_sim(A, B) = (A · B) / (|A| × |B|)
Kết quả: -1 đến 1
1.0 → Giống hệt nhau
0.0 → Vuông góc (không liên quan)
-1.0 → Đối lập
import numpy as np
def cosine_similarity(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
# Với normalized vectors: cosine_sim = dot product
# → Hầu hết embedding models normalize output → chỉ cần dot product
Dùng khi: Embedding không được normalize theo magnitude - muốn so sánh hướng, không phải độ lớn.
Dot Product¶
Dùng khi: Vectors đã được L2-normalized (hầu hết embedding models modern). Nhanh hơn cosine.
Euclidean Distance¶
Dùng khi: Magnitude quan trọng; ít dùng trong NLP hơn cosine/dot.
Tóm tắt¶
| Metric | Khi nào dùng | Lưu ý |
|---|---|---|
| Cosine | Default choice cho text | Normalize trước nếu chưa normalize |
| Dot Product | Vectors đã L2-normalized | Nhanh nhất |
| Euclidean | Khi magnitude có ý nghĩa | Ít dùng cho NLP |
3. Approximate Nearest Neighbor (ANN)¶
Vấn đề với Exact Search¶
Tìm kiếm chính xác (brute-force) trong 1 triệu vectors 768 chiều:
ANN hi sinh một chút accuracy để đổi lấy tốc độ tìm kiếm.
HNSW - Hierarchical Navigable Small World¶
HNSW xây dựng đồ thị phân cấp: lớp trên ít nodes (highway), lớp dưới nhiều nodes (local roads).
Layer 2 (thưa nhất): A ------- F
| |
Layer 1: A - C - D - F - H
| | | | |
Layer 0 (dày nhất): A-B-C-D-E-F-G-H-I
Tìm kiếm: Bắt đầu từ entry point trên cùng → greedy search xuống → O(log n)
Đặc điểm HNSW:
- Rất nhanh (sub-millisecond với hàng triệu vectors)
- RAM-based → cần load vào memory
- Phù hợp: Chroma, Qdrant, Weaviate
IVF - Inverted File Index¶
Chia vectors thành các clusters (Voronoi cells). Khi tìm kiếm, chỉ search trong nprobe clusters gần nhất.
Build time: K-means clustering → centroids
Search: 1. Tìm nprobe clusters gần query nhất
2. Brute-force search trong các clusters đó
Đặc điểm IVF:
- Disk-friendly - phù hợp dataset rất lớn (billions of vectors)
- Cần tune
nlist(số clusters) vànprobe(số clusters search) - Phù hợp: FAISS, pgvector
4. Vector Databases¶
So sánh tổng quan¶
| DB | Index | Deployment | Ưu điểm | Nhược điểm |
|---|---|---|---|---|
| Chroma | HNSW | Local/Docker | Đơn giản nhất, tốt cho dev | Không scale tốt |
| FAISS | HNSW/IVF | In-memory/Library | Meta-backed, cực nhanh | Không có UI, không persist tự động |
| Qdrant | HNSW | Local/Cloud | Filtering mạnh, Rust backend | Setup phức tạp hơn Chroma |
| Pinecone | Managed | Cloud only | Serverless, zero ops | Phí cao, vendor lock-in |
| pgvector | HNSW/IVF | PostgreSQL | Tích hợp SQL, ACID | Chậm hơn specialized DBs |
Chroma - Quickstart¶
import chromadb
from sentence_transformers import SentenceTransformer
# Setup
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_or_create_collection(
name="legal_docs",
metadata={"hnsw:space": "cosine"}
)
# Embed function tùy chỉnh
model = SentenceTransformer("intfloat/multilingual-e5-large")
# Thêm documents
documents = [
"Điều 14 Luật Doanh nghiệp: Công ty TNHH có từ 2 đến 50 thành viên.",
"Điều 46: Thành viên góp vốn chịu trách nhiệm trong phạm vi vốn góp.",
]
embeddings = model.encode(documents).tolist()
collection.add(
ids=["doc_1", "doc_2"],
embeddings=embeddings,
documents=documents,
metadatas=[{"source": "luat-dn-2020", "dieu": "14"},
{"source": "luat-dn-2020", "dieu": "46"}],
)
# Query
query = "Số lượng thành viên tối đa của công ty TNHH?"
query_embedding = model.encode([query]).tolist()
results = collection.query(
query_embeddings=query_embedding,
n_results=3,
where={"source": "luat-dn-2020"}, # Metadata filter
)
for doc, meta, dist in zip(
results["documents"][0],
results["metadatas"][0],
results["distances"][0]
):
print(f"[{dist:.3f}] {doc[:80]}...")
FAISS - Hiệu năng cao¶
import faiss
import numpy as np
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("intfloat/multilingual-e5-large")
dim = 1024 # Dimension của multilingual-e5-large
# Tạo index
index = faiss.IndexFlatIP(dim) # Inner Product (dot product)
# index = faiss.IndexHNSWFlat(dim, 32) # HNSW với M=32
# Index vectors
corpus = ["văn bản 1", "văn bản 2", ...]
vectors = model.encode(corpus, normalize_embeddings=True)
index.add(vectors.astype(np.float32))
# Save/Load
faiss.write_index(index, "legal.faiss")
index = faiss.read_index("legal.faiss")
# Search
query_vec = model.encode(["câu truy vấn"], normalize_embeddings=True)
distances, indices = index.search(query_vec.astype(np.float32), k=5)
Qdrant - Production-ready¶
from qdrant_client import QdrantClient
from qdrant_client.models import Distance, VectorParams, PointStruct
client = QdrantClient(url="http://localhost:6333")
# Tạo collection
client.create_collection(
collection_name="legal_docs",
vectors_config=VectorParams(size=1024, distance=Distance.COSINE),
)
# Upsert vectors
client.upsert(
collection_name="legal_docs",
points=[
PointStruct(
id=1,
vector=embeddings[0].tolist(),
payload={"text": "...", "source": "luat-dn-2020", "dieu": 14}
),
]
)
# Search với filter
from qdrant_client.models import Filter, FieldCondition, MatchValue
results = client.search(
collection_name="legal_docs",
query_vector=query_embedding.tolist(),
query_filter=Filter(
must=[FieldCondition(key="source", match=MatchValue(value="luat-dn-2020"))]
),
limit=5,
)
pgvector - SQL Integration¶
-- Enable extension
CREATE EXTENSION IF NOT EXISTS vector;
-- Tạo bảng
CREATE TABLE legal_chunks (
id SERIAL PRIMARY KEY,
content TEXT,
source VARCHAR(100),
embedding vector(1024)
);
-- Index HNSW
CREATE INDEX ON legal_chunks USING hnsw (embedding vector_cosine_ops);
-- Insert
INSERT INTO legal_chunks (content, source, embedding)
VALUES ('Điều 14 Luật Doanh nghiệp...', 'luat-dn-2020', '[0.1, 0.2, ...]');
-- Semantic search
SELECT content, source,
1 - (embedding <=> '[0.3, 0.1, ...]'::vector) AS similarity
FROM legal_chunks
ORDER BY embedding <=> '[0.3, 0.1, ...]'::vector
LIMIT 5;
Tóm tắt: Chọn Vector DB cho dự án¶
graph TD
A{Quy mô?} -->|Dev / POC| B[Chroma - đơn giản nhất]
A -->|< 10M vectors, self-hosted| C[Qdrant - cân bằng tốt]
A -->|Đã dùng PostgreSQL| D[pgvector - tiện tích hợp]
A -->|Serverless, không muốn ops| E[Pinecone - managed]
A -->|Research, cần speed tối đa| F[FAISS - fastest]
| Tình huống | Khuyến nghị |
|---|---|
| Prototype nhanh | Chroma |
| Production self-hosted | Qdrant |
| Đã có PostgreSQL stack | pgvector |
| Cần scale tự động | Pinecone |
| Tiếng Việt, open-source embedding | BAAI/bge-m3 hoặc multilingual-e5-large |