所有入門單元免費開放 · 課程內容持續更新中

Embeddings:把文字變成座標

想讓 AI 根據你的文件回答,第一步是「找到相關段落」。這堂課認識 embedding:把一段文字變成一串數字座標,讓電腦能依照「意思」而不是「字面」找出相近的內容。

第 12.1 堂約 14 分鐘

歡迎來到這個單元。先說說我們要去哪裡:五堂課後,你會做出一個能讀懂你自己文件的問答小幫手。它回答問題時會附上「這句話出自哪份文件」,找不到答案時會老實說找不到,而且你有一套方法檢查它答得好不好。

這種做法叫 RAG(Retrieval-Augmented Generation,檢索增強生成)。名字很長,意思很簡單:先找資料,再回答。

為什麼不直接把文件丟給模型?

你可能會想:現在的模型能讀很長的內容,把整份員工手冊貼進去不就好了?

文件少的時候,這確實是最簡單的做法,也值得先試。但當資料變成幾百份文件、幾千頁時,就會遇到三個問題:

  • 放不下:再大的上下文視窗也有上限。
  • 又慢又貴:還記得 LLM API 單元學的嗎?費用依照輸入的 token 計算,每問一次就送一整座圖書館進去,帳單會很可觀。
  • 容易分心:重點埋在一大堆不相關的內容裡,模型比較容易漏看。

所以 RAG 的策略是:每次只挑出最相關的幾段交給模型。問題就變成了,要怎麼「挑」?

關鍵字搜尋不夠聰明

最直覺的方法是關鍵字搜尋:問題裡有「特休」,就去找含有「特休」的段落。

但人說話不會照著文件用字。員工問「我想請特休」,手冊寫的可能是「年假申請流程」;問「計程車錢可以拿回來嗎」,手冊寫的是「交通費報銷」。字面上完全不同,意思卻一樣。

我們需要一種能比較「意思」的方法。這就是 embedding 登場的時候。

Embedding:文字的座標

Embedding(嵌入向量)是把一段文字轉換成一串數字的技術。負責轉換的是一種專門的模型,叫做 embedding 模型。

三句話經過 embedding 模型,各自變成一串數字;「我想請特休」和「年假要怎麼申請?」的數字很接近,「影印機卡紙了」的數字差很多

這串數字也叫做「向量」(vector)。它的長度是固定的,由模型決定,常見的是幾百到幾千個數字。

神奇的地方在於:embedding 模型讀過大量文字,學會了把意思相近的文字,轉成很接近的數字。

你可以把每串數字想像成地圖上的一個座標。在這張地圖上,講請假的句子住在同一區,講報帳的句子住在另一區:

平面地圖上,請假、報帳、設備三個話題各自聚成一區;新問題「休假規定是什麼?」落在請假區附近,最近的鄰居就是最相關的內容

找相關內容,就變成了「找地圖上離問題最近的點」。

真實的座標不是兩個數字,而是幾百個維度,人腦沒辦法想像,但數學上「距離」的概念一樣成立。

怎麼量「有多接近」?

最常用的方法叫餘弦相似度(cosine similarity)。不必記公式,只要知道它的結果:

數值 意思
接近 1 意思非常相近
0 附近 沒什麼關係
負數 方向相反(實務上比較少見)

如果先把向量「標準化」(讓每個向量的長度都是 1),餘弦相似度就等於兩個向量逐項相乘再加總,算起來非常快。等一下的程式會這樣做。

動手算算看

在虛擬環境裡安裝 sentence-transformers,這是一個很常用的開源套件:

pip install sentence-transformers

它會連帶安裝深度學習套件,檔案比較大,安裝需要幾分鐘,請耐心等一下。

建立 try_embeddings.py:

from sentence_transformers import SentenceTransformer

# 一個支援中文的小型多語言模型,第一次執行會自動下載
model = SentenceTransformer("sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")

sentences = [
    "我想請特休",
    "年假要怎麼申請?",
    "計程車費可以報帳嗎",
    "影印機卡紙了",
]

# normalize_embeddings=True:先把向量標準化,之後相乘就是餘弦相似度
vectors = model.encode(sentences, normalize_embeddings=True)
print("形狀:", vectors.shape)  # (句子數, 每句的數字個數)
print("第一句的前五個數字:", vectors[0][:5])

query = model.encode("休假規定是什麼?", normalize_embeddings=True)
scores = vectors @ query  # 每一句和問題的相似度

for sentence, score in sorted(zip(sentences, scores), key=lambda x: -x[1]):
    print(f"{score:.3f}  {sentence}")

執行:

python try_embeddings.py

第一次執行會下載模型檔(幾百 MB),之後就會直接使用已下載的檔案。你會看到類似這樣的結果(實際數字會不同):

形狀: (4, 384)
第一句的前五個數字: [ 0.03 -0.08  0.01  0.05 -0.02]
0.71  年假要怎麼申請?
0.64  我想請特休
0.18  計程車費可以報帳嗎
0.05  影印機卡紙了

注意看:問題「休假規定是什麼?」和「我想請特休」幾乎沒有共同的字,卻排在前面;「影印機卡紙了」則被排到最後。電腦開始能比較「意思」了。

逐行看懂

  • SentenceTransformer(...):載入 embedding 模型。模型名稱就像 Claude 的模型 ID,可以換成其他模型。
  • model.encode(...):把文字變成向量。可以一次丟一整個清單,速度比一句一句算快。
  • vectors @ query:@ 是矩陣相乘,一次算出每一句和問題的相似度。因為向量都標準化過,結果就是餘弦相似度。

其他選擇:雲端 embedding 服務

除了在自己電腦上算,也可以呼叫雲端的 embedding API。Voyage AI、OpenAI、Google 等供應商都有提供,用法通常是「送出一批文字,拿回一批向量」,和你剛剛做的事一樣。

怎麼選?

  • 自己電腦上算:免費、資料不外流,適合學習和小量資料;缺點是要下載模型,電腦慢的話會比較久。
  • 雲端服務:不用下載、通常品質更好、速度穩定;缺點是要付費,而且文件內容會送到供應商那裡。

這個單元用開源模型示範,但後面的流程完全一樣。之後想換成雲端服務,只要改「把文字變成向量」那一小段。

可以這樣問 AI

我正在學 embedding。這是我的 try_embeddings.py,請幫我改成:讓我在終端機輸入任意一句話,程式列出 sentences 裡和它最相似的前兩句與分數。也請用白話解釋為什麼 normalize_embeddings=True 之後,用 @ 就能算出餘弦相似度。

行動練習
  1. 安裝 sentence-transformers,執行 try_embeddings.py,確認看得到向量形狀與相似度排名。
  2. 把 sentences 換成你工作或生活中的十句話,例如常被問的問題、筆記標題。
  3. 想三個「意思相同、用字完全不同」的問題去查詢,看排名第一的句子對不對。
  4. 再試一個和所有句子都無關的問題,例如「今天晚餐吃什麼」,記下最高分是多少。這個數字在第四堂課會派上用場:它告訴你「不相關」大概長什麼樣子。

讀完了嗎?

標記完成後,單元與專案的進度會自動更新(只存在這台裝置的瀏覽器)。

卡住了?讓 AI 當你的家教複製這段提問範本,貼到 Claude 或其他 AI 工具,再補上你卡住的地方。

留言與討論

卡關了、有心得,或發現內容需要更新?都歡迎留言。留言使用 GitHub 帳號登入。

留言區即將開放。現在可以先到 GitHub 討論區 發問,或看看 交流頁 的其他管道。