RAG 向量距離計算:餘弦相似度(Cosine Similarity)概論
.作者:Jollen/
.日期:Sun Nov 09 2025 08:00:00 GMT+0800 (台北標準時間)
之前的教學,我們將語言變成可運算的數學表達式。接下來,就是要實際寫程式判斷兩個句子是否「語意相近」,最基本的方法,就是:餘弦相似度(Cosine Similarity)。
何謂「餘弦」相似度
假設我們把每個句子看作是一個向量,它在語意空間中都有一個方向。兩個向量的「夾角」越小,代表語意越接近;夾角越大,代表語意越不同。我們不關心句子的「長度」有多長,而是關心它們「指向的方向」是否一致。這正是餘弦相似度的核心觀念。
餘弦相似度的數學定義如下:
[ \text{similarity}(A, B) = \frac{A \cdot B}{|A||B|} ]
其中:
- (A \cdot B):兩個向量的內積(dot product)
- (|A|) 與 (|B|):分別是兩個向量的長度(norm)
- 結果範圍介於 (-1) 到 (1)。
計算角度(語意差距)
在語意搜尋的應用中,要從語意判斷轉化為距離計算,可以分為四個步驟:
- 語句轉向量:透過 Embedding 模型將每句話轉為向量。
- 向量正規化:確保所有向量在相同維度下可比較。
- 計算內積:求出兩個向量之間的夾角關係。
- 取得相似度:以 (\frac{A \cdot B}{|A||B|}) 的結果作為語意相似度。
這樣,我們就能在數學空間中衡量語言的距離。計算結果如下:
| 結果值 | 語意關係 |
|---|---|
| 接近 1 | 兩句話語意非常相似 |
| 接近 0 | 幾乎無關聯 |
| 接近 -1 | 語意相反(自然語言中較少見) |
也就是說,餘弦相似度不僅僅是一個數學計算,而是一種「語意角度」的量化方式。它將抽象的語意關聯轉換為具體的可比數值。
Node.js 實作:語意相似度運算
以下範例展示如何以 Node.js 實際計算句子之間的餘弦相似度。此範例承接前述的 Embedding 範例:
// src/examples/cosine-similarity.js
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
// 餘弦相似度函式
function cosineSimilarity(a, b) {
const dot = a.reduce((sum, val, i) => sum + val * b[i], 0);
const normA = Math.sqrt(a.reduce((sum, val) => sum + val * val, 0));
const normB = Math.sqrt(b.reduce((sum, val) => sum + val * val, 0));
return dot / (normA * normB);
}
async function main() {
const sentences = ['退貨政策', '退款流程', '物流延誤'];
// 生成向量
const { data } = await client.embeddings.create({
model: 'text-embedding-3-small',
input: sentences
});
const [v1, v2, v3] = data.map(d => d.embedding);
console.log('\n語意相似度:');
console.log(`退貨政策 vs 退款流程 → ${cosineSimilarity(v1, v2).toFixed(3)}`);
console.log(`退貨政策 vs 物流延誤 → ${cosineSimilarity(v1, v3).toFixed(3)}`);
}
main();
Text Embedding 模型會回傳每句話的 embedding 物件,裡面包含該句子的向量值;接著,以餘弦來計算其相似度。執行後可得到類似輸出:
語意相似度:
退貨政策 vs 退款流程 → 0.436
退貨政策 vs 物流延誤 → 0.395
我們針對「退貨政策」與這二句話的距離做比較。以此例來看:
- 「退貨政策」與「退款流程」的語意距離較近
- 「退貨政策」與「物流延誤」的距離較遠
這表示語意空間中的「距離」,確實能對應到人類概念上的「語意理解能力」。
Also read
Tags: rag, llm, text-embedding