Python 分詞練習:詞元、向量與餘弦相似度
分詞是 AI 流水線第一個可能悄悄丟失資訊的地方。這些練習把選擇擺到明面上:什麼算一個詞元、詞表如何共用,以及相似度分數能說明什麼、不能說明什麼。
先規範化,再切分
轉小寫是簡單的規範化步驟,讓 Python 和 python 成為同一個詞元,但也會抹去人名或程式碼中可能重要的資訊。選定規則後,訓練文字和新文字都要一致應用。
import re
def tokenize(text):
return re.findall(r"[a-z']+", text.lower())
print(tokenize("Python, Python! Text."))所有文件使用同一規則
共用一個詞表
從訓練文件構造詞表,而不是為每篇新文件單獨構造。對詞元排序,讓維度順序確定。詞表外的詞元需要明確策略,例如忽略或映射到未知詞元。
docs = ["python text", "text models"]
vocab = sorted({word for doc in docs for word in doc.split()})
index = {word: i for i, word in enumerate(vocab)}
print(index)穩定的維度
把詞元計數成向量
計數向量很容易檢查:每個位置表示某個詞表詞元出現了幾次。如果長文件不應自動與所有內容更相似,可以按文件長度歸一化。
def vector(text, index):
counts = [0] * len(index)
for word in text.split():
if word in index:
counts[index[word]] += 1
return counts
index = {"python": 0, "text": 1}
print(vector("python text text", index))可以審計的向量
用餘弦比較
餘弦相似度衡量向量夾角,因此忽略長度。當一篇文件只是更長時,這很有用。但如果兩句話用詞完全不同,它仍然無法判斷語意是否相同。
import math
def cosine(a, b):
dot = sum(x * y for x, y in zip(a, b))
norm_a = math.sqrt(sum(x * x for x in a))
norm_b = math.sqrt(sum(y * y for y in b))
if not norm_a or not norm_b:
return 0.0
return dot / (norm_a * norm_b)
print(round(cosine([1, 1], [2, 2]), 3))
print(round(cosine([1, 0], [0, 1]), 3))看方向,不看長度
知道詞袋漏掉了什麼
詞袋模型會給 「dog bites man」 和 「man bites dog」 相同向量。字元 n-gram 可以緩解拼字錯誤,詞嵌入可以捕捉分布相似性,但都不能代替在真實任務上的評估。
def bag(text):
return sorted(text.lower().split())
print(bag("dog bites man") == bag("man bites dog"))詞序消失了
你的練習清單
- 訓練和推理使用相同的規範化和分詞器。
- 只構造一個詞表,並保持順序穩定。
- 明確決定未知詞元如何處理。
- 餘弦相似度要防止零長度向量。
- 用改寫、拼字錯誤和調換詞序的句子測試流水線。
常見問題
詞元一定是一個單字嗎?
不一定。生產分詞器可能按標點、子詞、位元組或 Unicode 碼點切分。按單字只是教學上的簡化。
比較文字一定要用神經嵌入嗎?
不一定。詞袋和 TF-IDF 是許多搜尋與分類任務的強基線。詞彙重疊不夠時,嵌入才更有價值。
為什麼餘弦會回傳 0.0?
兩個向量沒有共用的非零維度,或者其中一個向量全為零。保護條件把這個情況顯式化。
練習詞元與向量
12 道題,覆蓋分詞、詞表、計數、餘弦相似度和詞彙比對局限。
繼續閱讀: