前一篇我們談了 Bag-of-Words (BoW),用統計詞頻的方式,把文本轉換成向量。
不過 BoW 有一個問題,單純統計詞頻的方式,會看到一些常見詞(例如:我、的、是)頻率很高,但對文本的區分度很低。
TF-IDF (Term Frequency–Inverse Document Frequency) 就可以解決這個問題啦~~
接下來我們就來完整了解 TF-IDF 的原理與實作!
TF-IDF 全名是 Term Frequency–Inverse Document Frequency
是由兩個分數:TF 詞頻 跟 IDF 逆文檔頻率 相乘計算出來的。
計算詞在該文本中出現的比率,出現越多次數值越大。
計算詞出現在所有文本的頻率,越常出現在各個文本中數值越小。
綜合兩者,算出字詞的重要程度。
jieba
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
# 測試文本
docs = ["電影很好看", "電影不好看", "電影很難看", "電影好看不好看"]
jieba
進行斷詞# 加入自定義詞
jieba.add_word("難看")
tokenized_docs = [" ".join(jieba.cut(doc)) for doc in docs]
print("斷詞結果:", tokenized_docs)
# === Output ===
斷詞結果: ['電影 很 好看', '電影 不 好看', '電影 很 難看', '電影 好看 不 好看']
tfidf = TfidfVectorizer(tokenizer=lambda x: x.split(), token_pattern=None)
tfidf_vec = tfidf.fit_transform(tokenized_docs)
print("詞彙表:", tfidf.get_feature_names_out())
print("TF-IDF 向量:\n", tfidf_vec.toarray())
# === Output ===
詞彙表: ['不' '好看' '很' '難看' '電影']
TF-IDF 向量:
[[0. 0.55953044 0.69113141 0. 0.4574528 ]
[0.69113141 0.55953044 0. 0. 0.4574528 ]
[0. 0. 0.5728925 0.72664149 0.37919167]
[0.49630284 0.80359984 0. 0. 0.32849776]]
當我們將以上 TF-IDF 的向量結果跟下面的 BoW 作比較,就可以發現:
# === BoW ===
詞彙表: ['不' '好看' '很' '難看' '電影']
BoW 向量:
[[0 1 1 0 1]
[1 1 0 0 1]
[0 0 1 1 1]
[1 2 0 0 1]]
TF-IDF 可以看成是 BoW 的進階版本。它同樣會統計詞頻,但還會進一步為每個詞加上「重要性」的權重。所以真正重要的詞會被賦予較高的分數,而過於常見、資訊量低的詞則會被壓低權重。這樣一來,向量就更能反映出文本的核心內容!
不過大家也可以想像一下,如果文本長度一拉長,BoW 和 TF-IDF 這類基於詞袋、統計詞頻的方式,向量維度就會變得非常龐大。因為整個詞彙表可能會到上萬個詞,而一篇文章可能只用到其中幾百個,於是大部分維度其實都是 0。
這樣的向量我們會稱為 稀疏向量(sparse vector)。雖然這樣的表示法在數學上可行,但在語意表達上卻顯得有點笨拙。
接下來,我們會進入到 「語意向量」(word embeddings)。它會將文字壓縮成 低維且稠密(dense) 的向量,讓每個維度都真正承載語意上的資訊。換句話說,我們就不再只是計算詞頻,而是要探索詞語之間的語意關係~
如果你想知道「語意」是怎麼被數字捕捉的話,那就一定要看下去啦!