Learning Objectives
- 說明 embedding 是什麼,並呼叫 API 取得一段文字的向量
- 用 numpy 算餘弦相似度,說明內積與長度正規化的關係
- 讀懂相似度矩陣,說明相似度數字只能比較、沒有絕對意義
- 舉出相似度高但意思相反的例子,說明 embedding 不能用來判斷語意是否正確
讓程式比較兩段文字的意思
使用者問「圖書館幾點關門?」,手冊裡寫的是「閉館時間」。兩邊沒有共同的詞,字串比對找不到,人卻一眼看得出在講同一件事。
Embedding(嵌入向量) 把一段文字變成一串數字,也就是一個向量。訓練的目標是讓意思相近的文字得到方向相近的向量,所以「比較兩段文字的意思」就變成「比較兩個向量」,這件事程式很擅長。後面幾課的向量搜尋與 RAG,都建立在這個動作上。
和前幾課一樣,右邊的程式和真實環境寫法相同,向量是我們事先呼叫 text-embedding-3-small 錄下來的真實結果(2026-11-03)。
逐步看:取得文字的向量
請注意 input 可以一次放很多段文字,回傳的 data 依序對應;還有每個向量有幾個數字。
餘弦相似度比的是方向
兩個向量的餘弦相似度(cosine similarity) 是它們夾角的餘弦值:
cos = 內積 ÷(a 的長度 × b 的長度)
- 內積(dot product):對應位置相乘再加總。
- 同方向得 1,垂直得 0,反方向得 −1。
- 除以長度之後,只剩方向的差別,向量長短不影響結果。
text-embedding-3-small 回傳的向量已經正規化為長度 1,所以內積就是餘弦相似度,算起來少一步(OpenAI Embeddings 指南 也建議用餘弦相似度)。但別家的模型不一定這樣,自己截短向量之後長度也不是 1 了。不確定時用完整公式,或先正規化一次,結果不會錯。本課的向量四捨五入到小數 4 位存放,長度會有 0.001 以內的誤差,範例裡都會先正規化。
先用二維的小向量算一次,比較容易看出來。
逐步看:內積與長度正規化
a 和 b 方向相同,只是 b 比較長。比較它們的內積和餘弦值。
逐步看:六個句子的相似度矩陣
六個句子裡有換句話說、換成英文、相關但問不同的事,還有完全無關的。先猜猜看哪一對最高。所有向量排成矩陣 V(每一列一段文字),V @ V.T 一次算出每一對的內積,第 i 列第 j 欄就是第 i 句和第 j 句的相似度。
相似度只能拿來比較
從這個矩陣可以看到幾件事:
- 「0.7 算不算像」沒有標準答案。 這個數字的分布依模型、維度、文字長短與語言而不同,換一個模型整批都會變。所以我們說「A 比 B 更像」,不說「0.7 就是相似」。
- 主題相同就會偏高。「圖書館幾點關門」和「圖書館可以借幾本書」問的是不同的事,相似度 0.679,只比換句話說的 0.788 低一點。
- 不同模型的向量不能互相比較。 維度相同也不行,它們是不同的空間。換模型時所有向量要全部重算。
dimensions 參數可以縮短向量,省下儲存空間;縮得太短可能分不清相近的內容。本軌一律用 128 維,是為了讓範例小,實際專案請用自己的資料試過再決定。
逐步看:相似度高不等於意思對
比較「意思相反」和「意思相同但換句話說」,哪一邊的相似度比較高?
Embedding 量的是「在談什麼」
上面的結果很值得記住:意思相反的兩句(0.92、0.824)比真正的換句話說(0.788、0.502)還要像。
Embedding 擅長判斷兩段文字是否在談同一件事,不擅長判斷細節是否相符,例如否定、誰對誰做了什麼、數字大小。對應用的影響:
- 拿它來找資料很合適:把可能相關的段落找出來。
- 不能拿它來判斷對錯:相似度高不表示這段文字支持使用者的說法。找到的內容是否真的回答了問題,要留給後面的步驟檢查(第 13、14 課)。
接下來用小工具親手試試。
從小工具看到的事
- 短句這組裡最像的一對,就是意思相反的那兩句。
- 手冊各節的相似度普遍比短句低:最高的一對是舊制
grade-2與新制grade-3,只有 0.697,而它們的內容互相衝突。這正是第 14 課要處理的問題。 fee-1和health-1在平面上幾乎相鄰,真實相似度只有 0.281,在 276 對裡排第 242。短句這組的前兩個主成分保留了 45.5% 的變異,手冊這組只有 24.2%,其餘的資訊都在投影時丟掉了。平面圖適合用來看大致分布,不能當成相似度的證據。
實作:餘弦相似度與排序
完成兩個函數:
cosine_similarity(a, b):回傳兩個 numpy 向量的餘弦相似度。向量長度不一定是 1,所以要用完整公式。rank(query_vec, vectors):回傳vectors的索引串列,依和query_vec的相似度由高到低排列。
這裡的向量已經錄好,只要不改 docs 和查詢句子,就對得上錄音。
小測驗:Embedding 與相似度
下面這段程式會印出什麼?
import numpy as np
a = np.array([1.0, 0.0])
b = np.array([5.0, 0.0])
print(round(float(a @ b / (np.linalg.norm(a) * np.linalg.norm(b))), 1))
檢索系統找到一段和問題相似度很高的文字。可以由此確定什麼?
原本用模型 A 存了一批向量,改用模型 B 算新查詢的向量(維度也設成一樣),會怎樣?
散布圖上兩個點幾乎重疊,這表示什麼?
同事說:「相似度 0.5 以上就算相關。」這句話的問題在哪裡?
重點整理
- Embedding 把文字變成向量;意思相近的文字,向量方向相近。
- 餘弦相似度 = 內積 ÷ 兩個長度的乘積;向量長度都是 1 時,內積就是餘弦相似度。
- 相似度只能拿來比較:數字的分布依模型和資料而不同,不同模型的向量也不能互相比較。
- Embedding 量的是「在談什麼」。意思相反的句子可以比換句話說更像,所以它適合找資料,不能判斷對錯。
下一課: 有了向量,就能在一整份手冊裡找出和問題最相關的段落。下一課用 numpy 寫向量搜尋,並和關鍵字搜尋比較。