iT邦幫忙

2025 iThome 鐵人賽

DAY 12
0

引言

在前幾篇的內容中,介紹了 Bag-of-Words 與 TF-IDF,這些把文本轉成向量的方法,但因為他們是用「詞頻統計」的方式,所以屬於 稀疏向量(sparse vector)
後來 稠密向量(dense vector) 的概念就出現啦~這個做法指的是把文本壓縮到相對「低維度」的空間中,而且每個維度都真正承載了語意上的資訊。這樣的表示方式,就是我們所說的 「語意向量」(semantic embeddings)

在 2013 年 Google 的 Mikolov 團隊提出了 Word2Vec,這是第一個真正將「語意向量」推向主流的模型架構。雖然在此之前,也有像 LSA、HAL 這些方法也嘗試過用數字來表示語意,但 Word2Vec 以更高效、更直觀的方式實現了這個想法,並成功捕捉到詞語之間的語意結構。

所以今天,我們就要來介紹 Word2Vec,並帶大家做一個簡單的小實作~

Word2Vec

Word2Vec 的核心概念很簡單,就是透過 「看詞的上下文,學到詞的語意」

舉例來說,我們有兩個句子:

  • 我喜歡吃蘋果,也喜歡吃香蕉
  • 我要去買蘋果,也順便買香蕉

從例子中,我們可以看到「蘋果」和「香蕉」會出現在類似的上下文中,像是前面可能都可以加「吃」或「買」,所以我們就能推測它們的語意應該是相近的。
Word2Vec 就是透過大量文本,梳理上下文來去學習出這樣的語意關係,並把每個詞表示成一個稠密向量,或是我們可以說 word embedding。

Word2Vec 有兩種主要的訓練方式:

  • CBOW(Continuous Bag of Words):用上下文(context)去預測中心詞(target word)。
  • Skip-gram:用中心詞(target word)去預測周圍的上下文(context)。


圖片來源:https://arxiv.org/pdf/1301.3781.pdf

細節的模型架構就先不多說明(我們之後才會講到神經網路模型),先進入程式實作吧~

程式實作

這邊我們先用少少幾句中文文本作為範例,來看看要如何用 Word2Vec 將中文文本轉成 embedding,並且最後畫出他們在 2D 空間的樣子。

  1. 安裝必要套件,並為句子斷詞

gensim (generate similarity) 是專門用在將文本向量化的模組,更多資訊可以參考 Gensim 官方文件學習筆記

import jieba
from gensim.models import Word2Vec
from sklearn.decomposition import PCA
import plotly.express as px

# 測試訓練句子
docs = [
    "我喜歡吃蘋果",
    "我喜歡吃香蕉",
    "蘋果和香蕉都是水果",
    "我不喜歡吃芭樂",
    "我要買水果",
    "我要買蘋果",
    "我要買香蕉",
    "我不買芭樂"
]

# 斷詞
tokenized_sents = [jieba.lcut(doc) for doc in docs]
print("斷詞結果:")
for doc, tokens in zip(docs, tokenized_sents):
    print(f"{doc} → {tokens}")
# === Output ===
斷詞結果:
我喜歡吃蘋果 → ['我', '喜歡', '吃', '蘋果']
我喜歡吃香蕉 → ['我', '喜歡', '吃', '香蕉']
蘋果和香蕉都是水果 → ['蘋果', '和', '香蕉', '都', '是', '水果']
我不喜歡吃芭樂 → ['我', '不', '喜歡', '吃', '芭樂']
我要買水果 → ['我要', '買', '水果']
我要買蘋果 → ['我要', '買', '蘋果']
我要買香蕉 → ['我要', '買', '香蕉']
我不買芭樂 → ['我', '不', '買', '芭樂']
  1. 訓練 Word2Vec 模型

Word2Vec() 參數:

  • vector_size=50:向量維度 50 維
  • window=2:上下文窗口大小為兩個詞
  • min_count=2:詞頻門檻,小於兩次不計算
  • sg=0:訓練算法選擇
    • sg=0 → 使用 CBOW
    • sg=1 → 使用 Skip-gram
model = Word2Vec(tokenized_sents, vector_size=50, window=2, min_count=2, sg=0)

# 詞彙表
words = list(model.wv.key_to_index)
print(f"詞彙表:{words}")

# 詞向量
vectors = [model.wv[word] for word in words]
print(f"「{words[0]}」的向量:\n{vectors[0]}")
# === Output ===
詞彙表:['買', '我', '我要', '香蕉', '蘋果', '吃', '喜歡', '芭樂', '不', '水果']
「買」的向量:
[-1.07358338e-03  4.76127345e-04  1.02034053e-02  1.80208236e-02
 -1.86018180e-02 -1.42308073e-02  1.29166720e-02  1.79546662e-02
 -1.00395251e-02 -7.52585474e-03  1.47595024e-02 -3.07206251e-03
 -9.06693377e-03  1.31137725e-02 -9.71385464e-03 -3.63675831e-03
  5.76050673e-03  1.99499261e-03 -1.65814739e-02 -1.89066064e-02
  1.46204270e-02  1.01353303e-02  1.35167204e-02  1.51790457e-03
  1.27119245e-02 -6.81579020e-03 -1.88859983e-03  1.15456525e-02
 -1.50521975e-02 -7.86695257e-03 -1.50185302e-02 -1.86774926e-03
  1.90725997e-02 -1.46450233e-02 -4.67420043e-03 -3.87720601e-03
  1.61593333e-02 -1.18580684e-02  9.44020430e-05 -9.50895250e-03
 -1.92138422e-02  1.00156358e-02 -1.75258722e-02 -8.77958722e-03
 -6.77432981e-05 -5.86947077e-04 -1.53204454e-02  1.92299057e-02
  9.96202230e-03  1.84676088e-02]
  1. 將向量壓縮至二維
# PCA 降到 2 維
pca = PCA(n_components=2)
vectors_2d = pca.fit_transform(vectors)

# 整理成字典方便做圖
data = {
    "x": vectors_2d[:, 0],
    "y": vectors_2d[:, 1],
    "word": words
}
  1. 使用plotly做出動態圖
# 互動散點圖
fig = px.scatter(data, x="x", y="y", text="word",
                 title="Word2Vec 詞向量 2D 可視化",
                 width=700, height=700)

# 調整文字顯示
fig.update_traces(textposition='top center', marker=dict(size=10, color='blue'))

# 加上四象限的參考線
fig.add_shape(type="line", x0=min(data["x"]), y0=0, x1=max(data["x"]), y1=0,
              line=dict(color="black", width=1))
fig.add_shape(type="line", x0=0, y0=min(data["y"]), x1=0, y1=max(data["y"]),
              line=dict(color="black", width=1))
fig.show()

https://ithelp.ithome.com.tw/upload/images/20250925/201787199Xs2DtjmNf.png

結語

一句話總結 Word2Vec,就是使用 淺層神經網路,透過考量 「詞」跟「上下文」 的關係,去學習詞的語意

今天我們用程式自己訓練了一個 Word2Vec 的模型,把文字轉成了 embedding,然後也在 2D 空間中看到詞語之間的語意距離。(雖然訓練資料量太小,基本上不太準XD)目前繁體中文有一些預訓練的詞向量資源也提供給大家:

不過大家可以想想看,Word2Vec 訓練出來的詞向量,會有什麼限制嗎?
提示💡:「我每天騎機車上學」跟「你這人很機車欸」,這兩個句子中的「機車」意思相同嗎?
明天我們要介紹 語境中的向量(Contextual Embedding),就會探討這個問題哦!

References


上一篇
Day 11|文本中的重點詞:TF-IDF
下一篇
Day 13|語境中的向量:Contextual Embedding
系列文
讓電腦聽懂人話:30 天 NLP 入門13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言