Embeddings & Vector Search

Semantic Similarity Là Gì? Từ Embeddings, Cosine Similarity Đến Semantic Search

Semantic Similarity cho phép máy tính so sánh văn bản dựa trên ý nghĩa thay vì chỉ dựa vào từ khóa. Bài viết đi từ trực giác về embeddings tới Sentence-BERT, Cosine Similarity và cách cơ chế này trở thành nền tảng của Semantic Search.

Minh họa cho bài Semantic Similarity Là Gì? Từ Embeddings, Cosine Similarity Đến Semantic Search
Mục lục bài viết

Một người dùng nhập vào ô tìm kiếm:

“Làm thế nào để giảm thời gian phản hồi của API?”

Trong database lại không có document nào chứa chính xác câu đó. Thứ tồn tại là một tài liệu có tiêu đề:

“Các kỹ thuật tối ưu latency cho backend service”

Nếu hệ thống chỉ tìm kiếm bằng keyword, nó có thể bỏ qua tài liệu này vì "giảm thời gian phản hồi" và "tối ưu latency" không trùng nhau về mặt từ vựng.

Nhưng một developer đọc hai câu trên gần như ngay lập tức nhận ra chúng đang nói về cùng một vấn đề.

Đây chính là khoảng cách mà Semantic Similarity cố gắng thu hẹp: thay vì chỉ hỏi “hai đoạn text có bao nhiêu từ giống nhau?”, hệ thống muốn trả lời một câu hỏi khó hơn:

“Hai đoạn text này gần nhau đến mức nào về mặt ý nghĩa?”

Research nền của bài mô tả sự chuyển dịch này từ lexical matching sang việc biểu diễn văn bản thành các vector trong không gian nhiều chiều, nơi những văn bản gần nhau về ý nghĩa có xu hướng được đặt gần nhau về mặt hình học.

Nhưng câu chuyện không đơn giản là:

Text
text
  ↓
embedding
  ↓
cosine similarity
  ↓
máy tính hiểu ý nghĩa

Để xây một hệ thống semantic search, recommendation hay RAG đáng tin cậy, chúng ta cần hiểu ít nhất ba lớp khác nhau:

Text
Representation
Văn bản được biến thành vector như thế nào?

        ↓

Geometry
"Giống nhau" trong không gian vector được đo thế nào?

        ↓

Engineering
Điểm similarity có thực sự phản ánh điều ứng dụng cần tìm hay không?

Semantic Similarity nằm ở giao điểm của cả ba lớp đó.


1. Semantic Similarity thực chất đang đo cái gì?

Giả sử có ba câu:

Text
A. Python được dùng nhiều trong backend development.

B. Python là một ngôn ngữ phổ biến để xây dựng server-side application.

C. JavaScript chạy trong trình duyệt.

Về mặt lexical matching, A và B không chia sẻ quá nhiều từ.

Nhưng về mặt ý nghĩa, chúng khá gần nhau.

Một hệ thống Semantic Similarity thường thực hiện hai bước:

Text
Text A ──► Encoder ──► Vector A ──┐
                                  │
                                  ├──► Similarity Function ──► Score
                                  │
Text B ──► Encoder ──► Vector B ──┘

Encoder biến văn bản thành một dãy số:

Text
A → [0.13, -0.42, 0.77, ..., 0.05]

B → [0.11, -0.39, 0.74, ..., 0.09]

Những con số riêng lẻ gần như không có ý nghĩa trực quan đối với con người. Điều quan trọng nằm ở cấu trúc tương đối của toàn bộ không gian embedding.

Nếu quá trình training hoạt động tốt, những text có quan hệ mà model được huấn luyện để nhận biết sẽ có representation gần nhau hơn.

Đây là một nuance rất quan trọng.

💡 Mental model: Embedding model không tạo ra một “tọa độ ý nghĩa tuyệt đối” của ngôn ngữ. Nó học một không gian biểu diễn mà trong đó khoảng cách phản ánh những quan hệ được hình thành bởi architecture, dữ liệu và training objective của model.

Vì vậy, một similarity score cao không nên được diễn giải đơn giản thành:

“Hai câu giống nhau 87% về ý nghĩa.”

Score 0.87 chỉ có ý nghĩa trong bối cảnh cụ thể của:

  • embedding model đang dùng;
  • similarity function;
  • loại dữ liệu;
  • task;
  • cách model được train;
  • distribution của corpus.

Đó cũng là lý do một embedding model rất mạnh trên Semantic Textual Similarity chưa chắc là model retrieval tốt nhất. Benchmark MTEB được xây dựng chính vì embedding cần được đánh giá trên nhiều task khác nhau như retrieval, clustering, classification, reranking và STS; nghiên cứu MTEB cũng cho thấy không có một phương pháp embedding duy nhất thống trị tất cả các loại tác vụ.


2. Vì sao ý nghĩa có thể biến thành vector?

Ý tưởng đứng phía sau embeddings hiện đại có nguồn gốc lâu hơn deep learning rất nhiều.

Một intuition quan trọng của ngôn ngữ học phân phối là:

Những từ xuất hiện trong những ngữ cảnh tương tự thường có quan hệ về mặt ngữ nghĩa.

Ví dụ:

Text
The cat sat on the mat.
The dog sat on the mat.

Nếu "cat" và "dog" liên tục xuất hiện trong những cấu trúc tương tự trên một corpus rất lớn, hệ thống có thể học được rằng chúng có một số đặc tính ngữ nghĩa gần nhau dù không cần con người định nghĩa thủ công:

Text
cat = động vật nhỏ, thú nuôi, ...
dog = động vật nhỏ, thú nuôi, ...

Máy không cần lưu những label này.

Nó chỉ cần học pattern thống kê đủ tốt.

Từ co-occurrence đến Word2Vec

Các phương pháp sớm có thể biểu diễn từ bằng ma trận đồng xuất hiện:

Text
             pet   bark   milk   animal   drive
cat           40     2     31      58       0
dog           37    42      3      61       0
car            0     0      0       1      49

cat và dog có distribution gần nhau hơn cat và car.

Vấn đề là vocabulary thực tế có thể chứa hàng trăm nghìn hoặc hàng triệu token, khiến representation kiểu này cực kỳ lớn và sparse.

Word2Vec đưa ra một hướng khác: thay vì trực tiếp lưu ma trận đồng xuất hiện khổng lồ, model học một dense vector có số chiều nhỏ hơn nhiều thông qua một prediction objective.

Paper Word2Vec năm 2013 giới thiệu hai kiến trúc để học continuous word representations hiệu quả trên corpus lớn.

Hai kiến trúc nổi tiếng là:

Kiến trúc Input Model cố dự đoán
CBOW Các từ xung quanh Từ ở giữa
Skip-gram Từ ở giữa Các từ xung quanh

Ví dụ với Skip-gram:

Text
"The cat sits on the sofa"

             target
               │
               ▼
              cat
               │
      ┌────────┼─────────┐
      ▼        ▼         ▼
     the      sits       on

Để thực hiện prediction tốt trên hàng triệu ví dụ, model phải tổ chức các word vectors theo cách giúp những từ có behavior tương tự chia sẻ representation tương tự.

Kết quả là chúng ta có một không gian hình học mang cấu trúc ngữ nghĩa.

Nhưng Word2Vec có một vấn đề lớn

Một từ chỉ có một vector.

Text
bank → [ ... ]

Cho dù "bank" xuất hiện trong:

Text
I deposited money at the bank.

hay:

Text
We sat on the bank of the river.

representation vẫn giống nhau.

Đây được gọi là static embedding.

Trong ngôn ngữ thật, ý nghĩa của từ lại phụ thuộc mạnh vào context.

Đó là một trong những lý do contextual language models như BERT trở nên quan trọng.


3. Từ word embedding đến sentence embedding

BERT không tạo representation của "bank" một cách độc lập.

Representation của token được hình thành dựa trên các token xung quanh nó.

Vì vậy:

Text
deposit → bank → money

và:

Text
river → bank → water

có thể tạo ra những contextual representations khác nhau.

BERT được thiết kế để học deep bidirectional representations bằng cách conditioning trên cả context bên trái và bên phải của token.

Điều đó giải quyết một vấn đề quan trọng của static embedding.

Nhưng lại xuất hiện một vấn đề khác.

BERT giỏi hiểu một cặp text, nhưng không tự nhiên phù hợp để search hàng triệu text

Giả sử ta có:

Text
Query:
"How can I reduce API latency?"

Document:
"Backend performance optimization techniques"

Một Cross-Encoder có thể ghép chúng thành một input:

Text
[CLS]
How can I reduce API latency?
[SEP]
Backend performance optimization techniques
[SEP]

Sau đó Transformer cho phép token của query tương tác trực tiếp với token của document.

Điều này rất mạnh.

Model có thể reasoning ở mức token:

Text
"reduce"
    ↕
"optimization"

"API"
    ↕
"backend"

"latency"
    ↕
"performance"

Nhưng có một hạn chế kiến trúc rất lớn:

Representation của document phụ thuộc vào query.

Vì vậy ta không thể đơn giản encode document một lần rồi reuse cho mọi query.

Nếu có một triệu documents:

Text
query + document_1 → model
query + document_2 → model
query + document_3 → model
...
query + document_1_000_000 → model

Một triệu forward pass Transformer cho mỗi query là quá đắt đối với first-stage retrieval.

Sentence-BERT thay đổi bài toán

Sentence-BERT, hay SBERT, giới thiệu kiến trúc Siamese/Bi-Encoder để tạo sentence embeddings có thể so sánh trực tiếp bằng cosine similarity. Paper gốc báo cáo rằng bài toán tìm cặp giống nhau nhất trong 10.000 câu cần khoảng 50 triệu inference với BERT và mất khoảng 65 giờ trong setup của nghiên cứu, trong khi SBERT giảm tác vụ này xuống khoảng 5 giây.

Mental model của Bi-Encoder:

Text
                  ┌──────────────┐
Sentence A ──────►│              │──────► u
                  │   Encoder    │
                  │ shared θ     │
Sentence B ──────►│              │──────► v
                  └──────────────┘

                           │
                           ▼

                   similarity(u, v)

Hai input được encode độc lập nhưng sử dụng cùng một model.

Đây chính là điểm mở khóa semantic retrieval.

Documents có thể được encode trước:

Text
Document 1 → vector 1 ─┐
Document 2 → vector 2  │
Document 3 → vector 3  ├──► Vector Index
...                     │
Document N → vector N ─┘

Khi query xuất hiện:

Text
Query
  │
  ▼
Embedding Model
  │
  ▼
Query Vector
  │
  ▼
Nearest Neighbors
  │
  ▼
Top-K Documents

Transformer chỉ cần encode query một lần.

Sau đó phần search chuyển thành bài toán hình học.

Đừng nhầm hai loại “quadratic complexity”

Đây là một lỗi technical khá phổ biến.

Transformer self-attention có complexity tăng gần bậc hai theo sequence length vì token tương tác với token.

Nhưng con số gần 50 triệu comparison trong ví dụ 10.000 câu của SBERT lại xuất phát từ số sentence pairs:

10,000×9,9992≈50,000,000\frac{10{,}000 \times 9{,}999}{2} \approx 50{,}000{,}000

Đó là hai nguồn chi phí khác nhau:

Text
Self-attention:
sequence length ↑
→ cost mỗi forward pass ↑

All-pairs comparison:
số sentence ↑
→ số forward pass ↑

Trong semantic search với một query và M documents, Cross-Encoder cần khoảng M pairwise forward passes chứ không phải M².

Tuy nhiên, khi M là hàng triệu, ngay cả O(M) Transformer inference cho mỗi query vẫn không thực tế.

Đó là lý do kiến trúc phổ biến là:

Text
Bi-Encoder
    │
    │ fast retrieval
    ▼
Top 100–1000 candidates
    │
    │ expensive but accurate
    ▼
Cross-Encoder
    │
    ▼
Top results
Bi-Encoder Cross-Encoder
Encode query/document riêng Có Không
Precompute document embeddings Có Không
Token interaction giữa query và document Không trực tiếp Có
First-stage retrieval Rất phù hợp Quá đắt ở corpus lớn
Reranking Top-K Có thể Rất phù hợp
Latency Thấp Cao hơn
Precision trên candidate nhỏ Tốt Thường tốt hơn

Sentence Transformers hiện cũng mô tả Bi-Encoder như bước đầu thường dùng trong pipeline hai tầng, sau đó Cross-Encoder rerank Top-K candidates.


4. Từ embedding đến hình học: Cosine Similarity thực sự làm gì?

Sau khi hai đoạn text đã trở thành vectors, neural network tạm thời rời khỏi trung tâm của bài toán.

Giả sử:

a=[a1,a2,…,an]\mathbf{a} = [a_1, a_2, \ldots, a_n]

và:

b=[b1,b2,…,bn]\mathbf{b} = [b_1, b_2, \ldots, b_n]

Ta cần một function:

Text
similarity(a, b) → score

Ba lựa chọn thường gặp là:

  • Cosine Similarity;
  • Dot Product;
  • Euclidean Distance.

Euclidean Distance

Euclidean distance đo khoảng cách đường thẳng giữa hai điểm:

d(a,b)=∑i=1n(ai−bi)2d(\mathbf{a},\mathbf{b}) = \sqrt{\sum_{i=1}^{n}(a_i-b_i)^2}

Trong hai chiều, intuition rất đơn giản:

Text
y
│
│        ● B
│       /
│      / distance
│     /
│  ● A
│
└──────────── x

Khoảng cách càng nhỏ thì hai vector càng gần.

Nhưng Euclidean distance nhạy với cả:

  • direction;
  • magnitude.

Việc magnitude có mang ý nghĩa hay không lại phụ thuộc vào embedding model và cách nó được train. Vì vậy không nên mặc định rằng Euclidean luôn “sai” cho text embeddings; metric phải phù hợp với representation mà model được thiết kế để sử dụng.

Dot Product

Dot Product đơn giản hơn:

a⋅b=∑i=1naibi\mathbf{a}\cdot\mathbf{b} = \sum_{i=1}^{n}a_i b_i

Dot Product bị ảnh hưởng bởi cả góc và magnitude:

a⋅b=∥a∥∥b∥cos⁡θ\mathbf{a}\cdot\mathbf{b} = \lVert\mathbf{a}\rVert\lVert\mathbf{b}\rVert\cos\theta

Hai vector cùng hướng nhưng magnitude lớn hơn sẽ có dot product lớn hơn.

Điều này có thể là feature hoặc bug tùy model.

Cosine Similarity

Cosine Similarity loại magnitude khỏi phép so sánh bằng cách chia Dot Product cho độ dài của hai vector:

cosine(a,b)=a⋅b∥a∥∥b∥\text{cosine}(\mathbf{a},\mathbf{b}) = \frac{\mathbf{a}\cdot\mathbf{b}}{\lVert\mathbf{a}\rVert\lVert\mathbf{b}\rVert}

Thay vì hỏi:

Hai điểm cách nhau bao xa?

Cosine hỏi:

Hai vector đang chỉ về những hướng giống nhau đến mức nào?

Text
            B
           ↗
          / θ
         /
        /
O ─────────────► A

Nếu góc nhỏ, Cosine Similarity cao.

Sentence Transformers hiện sử dụng cosine làm similarity function mặc định cho SentenceTransformer.similarity, đồng thời hỗ trợ Dot Product, Euclidean và Manhattan.

Một misconception quan trọng: cosine âm không có nghĩa là “trái nghĩa”

Về toán học:

Text
cos(0°)   =  1
cos(90°)  =  0
cos(180°) = -1

Nhưng embedding space không phải một từ điển antonym.

Hai câu:

Text
I love cats.
I hate cats.

có thể vẫn nằm khá gần nhau vì cả hai nói về:

  • cats;
  • cảm xúc đối với cats;
  • cùng grammatical structure;
  • cùng semantic domain.

Semantic similarity và logical contradiction là hai bài toán khác nhau.

Một model được train cho entailment, contradiction hay reranking có thể phản ánh distinction này tốt hơn, nhưng không nên diễn giải giá trị cosine âm/dương như một logic rule phổ quát.

⚠️ Cẩn thận: Similarity không đồng nghĩa với entailment, correctness, relevance hay factual consistency.


5. Vì sao L2 normalization khiến Cosine và Dot Product trở nên gần như cùng một bài toán?

Đây là một trong những tính chất hữu ích nhất khi triển khai vector search.

Ta chuẩn hóa vector:

v^=v∥v∥\hat{\mathbf{v}} = \frac{\mathbf{v}}{\lVert\mathbf{v}\rVert}

Sau normalization:

∥v^∥=1\lVert\hat{\mathbf{v}}\rVert = 1

Với hai unit vectors u và v:

cosine(u,v)=u⋅v1×1\text{cosine}(\mathbf{u},\mathbf{v}) = \frac{\mathbf{u}\cdot\mathbf{v}}{1 \times 1}

nên:

cosine(u,v)=u⋅v\boxed{\text{cosine}(\mathbf{u},\mathbf{v}) = \mathbf{u}\cdot\mathbf{v}}

Tức là với embeddings đã được L2-normalized:

Text
Cosine Similarity
       =
Dot Product

Sentence Transformers cũng lưu ý rằng nếu embeddings đã normalized thì dùng dot metric có thể hợp lý hơn vì cosine sẽ phải normalize lại; dot product trên normalized embeddings tương đương cosine similarity.

Không chỉ vậy.

Euclidean distance giữa hai unit vectors cũng liên hệ trực tiếp với cosine.

Bắt đầu từ:

∥u−v∥2\lVert\mathbf{u}-\mathbf{v}\rVert^2

Khai triển:

∥u∥2+∥v∥2−2(u⋅v)\lVert\mathbf{u}\rVert^2 + \lVert\mathbf{v}\rVert^2 - 2(\mathbf{u}\cdot\mathbf{v})

Vì:

∥u∥=∥v∥=1\lVert\mathbf{u}\rVert = \lVert\mathbf{v}\rVert = 1

nên:

∥u−v∥2=2−2(u⋅v)\lVert\mathbf{u}-\mathbf{v}\rVert^2 = 2-2(\mathbf{u}\cdot\mathbf{v})

Và vì:

u⋅v=cos⁡θ\mathbf{u}\cdot\mathbf{v} = \cos\theta

ta có:

∥u−v∥2=2−2cos⁡θ\boxed{\lVert\mathbf{u}-\mathbf{v}\rVert^2 = 2-2\cos\theta}

Điều này dẫn đến một engineering implication rất hữu ích:

Với cùng tập L2-normalized vectors, ranking theo cosine similarity, dot product và Euclidean distance mang thông tin thứ tự tương đương nếu implementation sử dụng các convention tương ứng.

Metric Có quan tâm magnitude? Với normalized vectors
Cosine Similarity Không Bằng Dot Product
Dot Product Có nếu chưa normalize Bằng Cosine
Euclidean Distance Có nếu chưa normalize Monotonic với Cosine

Tuy nhiên, đừng vì đẳng thức toán học này mà tùy tiện đổi metric trong production.

Model card, training objective và vector database configuration vẫn phải thống nhất với nhau.

Nếu model được thiết kế cho Dot Product trên unnormalized embeddings, tự normalize có thể thay đổi behavior mà model đã học.


6. Một Semantic Similarity pipeline bằng Python trông như thế nào?

Với sentence-transformers, một ví dụ STS đơn giản hiện nay có thể viết rất ngắn:

Python
from sentence_transformers import SentenceTransformer

model = SentenceTransformer(
    "sentence-transformers/all-MiniLM-L6-v2"
)

sentences = [
    "How can I reduce API latency?",
    "Techniques for improving backend response time.",
    "How do I reset my database password?",
]

embeddings = model.encode(sentences)

scores = model.similarity(
    embeddings,
    embeddings,
)

print(scores)

model.similarity() trả về một similarity matrix giữa các embeddings. Đây cũng là pattern được tài liệu Sentence Transformers sử dụng cho Semantic Textual Similarity.

Conceptually:

Text
                        Sentence 1   Sentence 2   Sentence 3
Sentence 1                  1.00        0.7x          0.1x
Sentence 2                  0.7x        1.00          0.1x
Sentence 3                  0.1x        0.1x          1.00

Giá trị trên diagonal là similarity của sentence với chính nó.

Nhưng production search khác một chút.

Semantic Textual Similarity và Semantic Search không hoàn toàn giống nhau

STS thường là bài toán symmetric:

Text
"That movie was fantastic."

"The film was really great."

Hai text có vai trò tương tự.

Search thường asymmetric:

Text
Query:
"How to fix Redis memory issue?"

Document:
"Redis eviction policies, maxmemory configuration,
memory fragmentation and troubleshooting..."

Query ngắn, document dài và có vai trò khác nhau.

Với retrieval tasks, Sentence Transformers phiên bản hiện tại khuyến nghị dùng:

Python
query_embedding = model.encode_query(query)
document_embeddings = model.encode_document(documents)

thay vì luôn dùng encode() cho cả hai, vì một số model sử dụng prompt hoặc routing khác nhau cho query và document.

Một pipeline retrieval đơn giản:

Python
from sentence_transformers import SentenceTransformer
import torch

model = SentenceTransformer(
    "sentence-transformers/all-MiniLM-L6-v2"
)

documents = [
    "Redis maxmemory controls the memory limit.",
    "PostgreSQL uses MVCC for concurrency control.",
    "API latency can be reduced through caching and query optimization.",
]

document_embeddings = model.encode_document(
    documents,
    convert_to_tensor=True,
)

query = "How can I make my API respond faster?"

query_embedding = model.encode_query(
    query,
    convert_to_tensor=True,
)

scores = model.similarity(
    query_embedding,
    document_embeddings,
)[0]

top_score, top_index = torch.max(scores, dim=0)

print(documents[top_index])

Với vài nghìn hoặc vài chục nghìn documents, brute-force similarity có thể đủ.

Khi corpus tăng rất lớn, ta thường thay phần:

Text
compare query với mọi document

bằng một ANN index.

Nhưng HNSW, IVF, PQ và vector database là những chủ đề đủ lớn để trở thành bài riêng. Điều cần giữ lại ở đây chỉ là:

Text
Embedding model
     │
     ▼
Vector representation
     │
     ▼
Similarity geometry
     │
     ▼
Nearest-neighbor retrieval

Vector database không tạo ra semantic similarity.

Nó chỉ giúp tìm những vectors gần nhau đủ nhanh ở quy mô lớn.


7. Similarity score không phải probability

Đây là một trong những lỗi phổ biến nhất khi developer mới dùng embeddings.

Giả sử:

Text
cosine = 0.82

Ta rất dễ viết:

Hai câu giống nhau 82%.

Nhưng không có cơ sở tổng quát để kết luận như vậy.

Cosine similarity chỉ là một geometric score.

Distribution của nó có thể khác rất mạnh giữa các model.

Ví dụ, một model có thể cho:

Text
unrelated      ~ 0.10–0.30
related        ~ 0.40–0.65
very related   ~ 0.65–0.85

Trong khi model khác có thể tập trung phần lớn scores trong:

Text
0.55–0.95

Ngay cả cùng một model, distribution cũng thay đổi theo domain.

Legal documents có thể behave khác source code.

Tiếng Việt có thể behave khác tiếng Anh.

Short queries có thể behave khác long passages.

Vì vậy hard-code:

Python
if score > 0.8:
    relevant = True

mà không đánh giá trên data thật là một anti-pattern.

Threshold nên được chọn từ evaluation data

Giả sử ta thu thập:

Text
(query, document, relevant?)

từ application thực tế.

Sau đó chạy embedding model và quan sát:

Text
Relevant pairs:
0.91
0.84
0.76
0.71
0.64

Irrelevant pairs:
0.69
0.55
0.42
0.31
0.12

Lúc này mới có thể phân tích threshold:

Text
threshold = 0.60

sẽ ảnh hưởng:

  • precision;
  • recall;
  • false positives;
  • false negatives.

Và trong search, nhiều hệ thống thậm chí không cần absolute threshold.

Chúng chỉ cần:

Text
Top-K nearest neighbors

rồi đưa candidates sang reranker.

🏗️ Production Note: Đừng thiết kế business logic dựa trên một similarity threshold chỉ vì score “trông có vẻ cao”. Calibration phải dựa trên distribution của chính dữ liệu và task của bạn.


8. Semantic Similarity thất bại ở đâu?

Embedding-based similarity rất mạnh, nhưng nó không phải một semantic oracle.

8.1. Cùng chủ đề không có nghĩa là cùng ý

Xét:

Text
A. Redis improves application performance through caching.

B. Redis causes application performance problems when memory is exhausted.

Hai câu chia sẻ rất nhiều semantic features:

Text
Redis
application
performance
memory/cache

Embedding có thể đặt chúng khá gần nhau.

Nhưng message của hai câu không giống nhau.

Nếu application cần phân biệt:

Text
support
vs
contradiction

thì similarity alone có thể không đủ.


8.2. Named entities và identifiers có thể quan trọng hơn “ý nghĩa tổng thể”

Query:

Text
ERR_CONNECTION_RESET 104

Document A:

Text
Troubleshooting ERR_CONNECTION_RESET 104

Document B:

Text
General guide to debugging network connection failures

Embedding model có thể thấy B rất relevant về mặt semantic.

Nhưng người dùng có thể muốn chính xác error code 104.

Đây là nơi lexical retrieval như BM25 vẫn cực kỳ hữu ích.

Semantic search không khiến keyword search lỗi thời.

Hai phương pháp giải quyết hai loại signal khác nhau:

Signal Lexical Search Semantic Search
Exact keyword Rất mạnh Có thể yếu hơn
Error code / ID Rất mạnh Có thể yếu
Synonym Yếu Mạnh
Paraphrase Yếu Mạnh
Conceptual similarity Hạn chế Mạnh
Từ hiếm mới xuất hiện Thường tốt Phụ thuộc model

Đó là lý do nhiều production retrieval systems kết hợp lexical và dense retrieval thay vì buộc phải chọn một bên.


8.3. Một vector phải nén quá nhiều thông tin

Giả sử document chứa:

Text
- PostgreSQL indexing
- Redis caching
- Kubernetes networking
- AWS IAM
- Kafka consumer lag

Một single embedding phải nén toàn bộ document thành một vector có kích thước cố định.

Mental model có thể hình dung như:

Text
5 chủ đề khác nhau
       │
       ▼
┌──────────────────────┐
│ Fixed-size embedding │
└──────────────────────┘
       │
       ▼
Một representation chung

Những detail nhỏ có thể bị “pha loãng”.

Đây là một lý do retrieval systems thường chunk documents trước khi embedding.

Nhưng chunk càng nhỏ cũng không phải càng tốt.

Chunk quá nhỏ có thể mất context.

Chunk quá lớn có thể làm representation không đủ tập trung.

Semantic Similarity vì thế không thể tách rời hoàn toàn khỏi đơn vị text mà ta lựa chọn để encode.


8.4. Model không hiểu domain của bạn đủ tốt

Một general-purpose embedding model có thể rất giỏi với:

Text
movies
news
general questions
Wikipedia

nhưng chưa chắc hiểu tốt:

Text
internal product codes
medical terminology
Japanese HR vocabulary
proprietary abbreviations
company-specific concepts

Nếu model chưa từng học được semantic distinction quan trọng đối với domain, similarity function không thể tự sửa vấn đề.

Cosine chỉ đo geometry.

Nếu geometry sai, cosine sẽ đo cái sai một cách rất chính xác.


Hai thuật ngữ này thường bị sử dụng thay thế cho nhau nhưng nên tách rõ.

Semantic Similarity

Bài toán:

Text
text A + text B
        │
        ▼
hai text giống nhau đến mức nào?

Output:

Text
similarity score

Bài toán:

Text
query
  │
  ▼
tìm những documents relevant nhất
trong một corpus lớn

Semantic Similarity là một component có thể được sử dụng bên trong Semantic Search.

Một semantic search system thực tế còn bao gồm:

Text
Documents
   │
   ▼
Chunking
   │
   ▼
Embedding
   │
   ▼
Indexing
   │
   ▼
Query Embedding
   │
   ▼
Candidate Retrieval
   │
   ▼
Filtering / Fusion
   │
   ▼
Optional Reranking
   │
   ▼
Results

Nếu ứng dụng là RAG, còn thêm:

Text
Results
   │
   ▼
Context Construction
   │
   ▼
LLM
   │
   ▼
Answer

Vì vậy khi RAG retrieve sai document, nguyên nhân không nhất thiết là:

“Cosine similarity không tốt.”

Có thể lỗi nằm ở:

  • chunking;
  • embedding model;
  • query formulation;
  • metadata filtering;
  • retrieval strategy;
  • corpus quality;
  • ANN configuration;
  • reranking.

Đây là mental model quan trọng nhất khi chuyển từ một notebook demo sang production AI system.


10. Bi-Encoder và Cross-Encoder không phải đối thủ — chúng giải quyết hai stage khác nhau

Một hiểu nhầm phổ biến là hỏi:

Nên dùng Bi-Encoder hay Cross-Encoder?

Trong nhiều hệ thống, câu trả lời là:

Dùng cả hai, nhưng ở những vị trí khác nhau.

Hãy tưởng tượng có 10 triệu documents.

Cross-Encoder không thể đánh giá cả 10 triệu candidates với latency hợp lý.

Bi-Encoder lại đủ nhanh để tìm một candidate set nhỏ:

Text
10,000,000 documents
        │
        │ Bi-Encoder + ANN
        ▼
      Top 100

Sau đó Cross-Encoder có thể dành nhiều compute hơn để phân tích 100 candidates đó:

Text
Top 100
   │
   │ Cross-Encoder
   ▼
 Top 10

Đây là kiến trúc funnel:

Text
┌──────────────────────────────┐
│        Full Corpus           │
│       10M documents          │
└──────────────┬───────────────┘
               │
               │ fast + broad recall
               ▼
┌──────────────────────────────┐
│      Bi-Encoder Retrieval    │
└──────────────┬───────────────┘
               │
               │ Top 100
               ▼
┌──────────────────────────────┐
│     Cross-Encoder Rerank     │
└──────────────┬───────────────┘
               │
               │ precise ranking
               ▼
┌──────────────────────────────┐
│          Top 10              │
└──────────────────────────────┘

Bi-Encoder tối ưu cho:

Text
candidate generation

Cross-Encoder tối ưu cho:

Text
fine-grained comparison

Không phải mọi system đều cần reranking.

Nếu corpus nhỏ, query đơn giản hoặc latency requirement cực thấp, Bi-Encoder có thể đủ.

Ngược lại, trong search hay RAG nơi một vài vị trí đầu tiên có giá trị rất lớn, reranking thường đáng cân nhắc.


11. Năm lỗi dễ gặp khi triển khai Semantic Similarity

Lỗi 1: Chọn model chỉ dựa trên leaderboard

Một model đứng đầu một benchmark không có nghĩa nó tốt nhất cho application của bạn.

Latency, dimension, language, domain, context length và retrieval objective đều quan trọng.

MTEB tồn tại chính vì embedding quality cần được xem xét trên nhiều task thay vì một score duy nhất.

Lỗi 2: Nghĩ cosine score là probability

0.82 không có nghĩa "82% relevant".

Threshold phải được evaluate.

Lỗi 3: Dùng một giant document làm một embedding

Một embedding cố định có giới hạn về lượng information structure nó có thể giữ.

Chunking strategy ảnh hưởng trực tiếp tới retrieval quality.

Lỗi 4: Bỏ lexical search vì đã có embeddings

Semantic models mạnh ở paraphrase.

BM25 mạnh ở exact lexical evidence.

Product code, version number, tên riêng và error identifier có thể khiến lexical retrieval cực kỳ quan trọng.

Lỗi 5: Tập trung vào Vector Database trước Embedding Quality

Developer đôi khi dành rất nhiều thời gian tranh luận:

Text
Pinecone?
Qdrant?
Weaviate?
pgvector?
Milvus?

trong khi vấn đề thực sự nằm ở embedding hoặc data.

Vector database giúp trả lời:

Làm sao tìm nearest neighbors nhanh?

Nó không tự trả lời:

Representation này có đặt đúng documents cạnh nhau hay không?

Nếu embedding space không phản ánh task của bạn, index nhanh hơn chỉ giúp trả về kết quả sai nhanh hơn.


12. Mental model cuối cùng: Semantic Similarity là learned geometry

Ban đầu chúng ta đặt câu hỏi:

Vì sao "giảm thời gian phản hồi API" có thể tìm được tài liệu "tối ưu backend latency" dù hai câu không sử dụng cùng từ?

Bây giờ câu trả lời đã rõ hơn.

Không phải hệ thống đang tra một dictionary chứa:

Text
"giảm thời gian phản hồi"
=
"tối ưu latency"

Thay vào đó:

  1. embedding model đã học statistical structure của language;
  2. mỗi text được ánh xạ thành một representation nhiều chiều;
  3. những patterns mà model đánh giá tương tự được đặt gần nhau trong embedding space;
  4. một similarity function đo relationship hình học giữa các representations;
  5. retrieval system sử dụng relationship đó để tìm candidates gần query.

Có thể rút gọn toàn bộ cơ chế thành:

Text
Language
   │
   ▼
Learned Representation
   │
   ▼
Vector Space
   │
   ▼
Geometry
   │
   ▼
Similarity
   │
   ▼
Retrieval / Ranking / Clustering / Matching

Nhưng điều quan trọng nhất là không thần thánh hóa bước "Vector Space".

Semantic Similarity không phải bằng chứng rằng model sở hữu một hệ thống hiểu ý nghĩa giống con người.

Nó là một learned geometric approximation cực kỳ hữu ích: model được train sao cho những relationship quan trọng đối với objective của nó xuất hiện dưới dạng structure trong vector space.

Và chính điểm này giải thích cả sức mạnh lẫn giới hạn của embeddings.

Khi representation phù hợp với task, một phép Dot Product đơn giản có thể giúp tìm đúng information giữa hàng triệu documents.

Khi representation không phù hợp, không có vector database, cosine formula hay ANN algorithm nào có thể biến geometry sai thành semantic understanding đúng.

Nếu cần giữ lại bốn ý sau khi đọc bài này, hãy nhớ:

  • Embedding quyết định không gian; similarity function chỉ đo không gian đó.
  • Cosine score không phải probability hay truth score.
  • Bi-Encoder giúp retrieval scalable; Cross-Encoder giúp comparison sâu hơn.
  • Semantic Similarity là nền tảng của semantic search, nhưng một production retrieval system còn nhiều stage khác ngoài similarity.

Khi nắm được mental model này, các chủ đề tiếp theo như Vector Database, Hybrid Search, Reranking hay RAG sẽ không còn là những công nghệ rời rạc. Chúng chỉ là những tầng engineering khác nhau được xây lên trên cùng một nền móng: biến thông tin thành representation có thể so sánh và tìm kiếm bằng toán học.

Nguồn tham khảo

  • Mikolov et al. — Efficient Estimation of Word Representations in Vector Space.
  • Devlin et al. — BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding.
  • Reimers & Gurevych — Sentence-BERT: Sentence Embeddings using Siamese BERT-Networks.
  • Sentence Transformers — Semantic Textual Similarity Documentation.
  • Sentence Transformers — Semantic Search Documentation.
  • Sentence Transformers — Migration Guide: encode_query / encode_document.
  • Muennighoff et al. — MTEB: Massive Text Embedding Benchmark.