iT邦幫忙

2025 iThome 鐵人賽

DAY 21
0
AI & Data

ㄧ個八卦的時間也能了解AI最新趨勢系列 第 21

Day 21 Contextual Embedding 基本概念介紹(含簡易實作)

  • 分享至 

  • xImage
  •  

大家早安~
中秋連假還是不能停更!在吃烤肉的同時還是來產出一下🙂‍↕️
那今天我們要延續昨天聊到的 Word2Vec!昨天我們已經知道它能透過上下文學出詞與詞之間的語意關係,
像「貓」和「狗」在向量空間裡就會靠得比較近,而「國王」和「皇后」也會比「國王」和「香蕉」更相似,我們也提到過它的兩種訓練方式,並在最後說到一個小小的限制——那就是 每個詞的向量是固定的!
也就是說,不管「吃」是出現在「吃飯」還是「吃網速」裡,在 Word2Vec 看來,它永遠都是同一個「吃」。

但為什麼會這樣呢?
原因是因為 Word2Vec 屬於靜態詞嵌入(static embedding),也就是每個詞在訓練結束後都只會被賦予一個固定的向量,不會隨著上下文改變而變化,這樣的方式雖然已經比早期的 One-hot 或 Bag of Words 聰明很多,但畢竟語言是充滿歧異的,我們還是會希望電腦能夠從語境(context)的情況下推敲出真正的語意,所以就出現了 contextual embedding(語境式詞嵌入) 的概念啦!

contextual embedding

顧名思義,它會根據「上下文」來決定詞的向量表示,也就是說,同樣的詞在不同句子中,會有不同的向量,我們以下面兩句話為例:

我打開了門
我打開了電腦

在這兩個句子中,「打開」的意義明顯不同,contextual embedding 會讓模型產生兩個不同的「打開」向量,讓模型能更準確地捕捉語意差異。

contextual embedding 怎麼來?
要能生成這種具語境的詞向量,通常會透過 深度學習模型(deep learning models) 來完成。
這些模型會同時處理整個輸入句子(也就是一個「序列」),並考慮詞與詞之間的關聯性,最後產出每個詞的動態向量表示。
常見能做到這件事的模型包括:

  • ELMo(基於雙向 LSTM)
  • BERT(基於 Transformer Encoder)

我們前面有介紹過一些模型像是SVM等,都是屬於傳統機器學習模型,跟這邊提到的深度學習不一樣喔!

那他們的原理蠻複雜,我們今天就先暫時不提,之後會再介紹給大家!
我們直接來看看如何用word2vec 以及BERT 來訓練不同的詞向量~

小小實作

word2vec
首先是要安裝必要的套件

from gensim.models import Word2Vec

再來把我們想訓練的語料經過斷詞後,開始訓練模型

sentences = [["我", "喜歡", "狗"], ["我", "討厭", "狗"], ["狗", "是", "動物"]] #這邊是已經斷詞好的句子

#開始訓練word2vec
w2v_model = Word2Vec(
    sentences, #訓練的語料
    vector_size=50, #每個詞會學到一個 50 維的向量(embedding)。維度越大,能表達的語意越豐富,但需要更多資料支撐。
    window=2, #對每個中心詞,最多看「左 2 + 右 2」的鄰近詞當作 上下文(context)
    min_count=1, # 詞頻門檻 
    # sg=0 #(可選)訓練方式:0=CBOW、1=Skip-gram)  
    )

這樣就訓練好惹!是不是超快!!
接下來我們可以來看看狗的向量

print(w2v_model.wv["狗"]) # 會取出「狗」這個詞的 embedding 向量

輸出結果:

[-1.0724545e-03  4.7286271e-04  1.0206699e-02  1.8018546e-02
 -1.8605899e-02 -1.4233618e-02  1.2917745e-02  1.7945977e-02
 -1.0030856e-02 -7.5267432e-03  1.4761009e-02 -3.0669428e-03
 -9.0732267e-03  1.3108104e-02 -9.7203208e-03 -3.6320353e-03
  5.7531595e-03  1.9837476e-03 -1.6570430e-02 -1.8897636e-02
  1.4623532e-02  1.0140524e-02  1.3515387e-02  1.5257311e-03
  1.2701781e-02 -6.8107317e-03 -1.8928028e-03  1.1537147e-02
 -1.5043275e-02 -7.8722071e-03 -1.5023164e-02 -1.8600845e-03
  1.9076237e-02 -1.4638334e-02 -4.6675373e-03 -3.8754821e-03
  1.6154874e-02 -1.1861792e-02  9.0324880e-05 -9.5074680e-03
 -1.9207101e-02  1.0014586e-02 -1.7519170e-02 -8.7836506e-03
 -7.0199967e-05 -5.9236289e-04 -1.5322480e-02  1.9229487e-02
  9.9641159e-03  1.8466286e-02]

我們可以用相似度來看看動物以及香蕉語意向量上有多接近(應該是狗跟動物會比狗跟香蕉相近比較符合常理吧xd)

print(w2v_model.wv.similarity("狗", "動物"))
print(w2v_model.wv.similarity("狗", "香蕉"))

結果

0.13204393
0.012442183

將將~ 沒錯!在這邊的例子中可以看到"狗"跟"動物"在語意相似度會比"狗"跟"香蕉"來得高!
大家也可以在是其他組比較看看!

那我們接著來看看如何訓練可以產出contextual embedding 的方式吧!

BERT
首先一樣先載入套件並引入bert

from transformers import AutoTokenizer, AutoModel
import torch

# 載入我們的模型
tokenizer = AutoTokenizer.from_pretrained("bert-base-chinese")
model = AutoModel.from_pretrained("bert-base-chinese")

把語料放進來

#要訓練的語料
docs = [
    "我喜歡吃炸雞",
    "這個遊戲很吃網速",
    "想要吃麥當勞", 
    "這家餐廳很好吃"
]

設定目標詞

target_word = "吃"
vectors = []   # 存 target_word 的向量(tensor)
contexts = []  # 存含有 target_word 的句子(str)

得到詞向量後我們來看看我喜歡吃炸雞中的 以及這個遊戲很吃網速中的`吃 語意相似度為何

with torch.no_grad():
    for s in docs:
        inputs = tokenizer(s, return_tensors="pt")
        outputs = model(**inputs)
        tokens = tokenizer.tokenize(s)
        for i, t in enumerate(tokens):
            if t == target_word:
                vec = outputs.last_hidden_state[0, i+1].detach().cpu().numpy()
                vectors.append(vec)

# 比較第一句與第二句的「吃」語意相似度
sim = cosine_similarity([vectors[0]], [vectors[1]])[0][0]
print(f"第一句 vs 第二句『吃』的相似度:{sim:.4f}")

輸出結果
第一句 vs 第二句『吃』的相似度:0.4388
我喜歡吃炸雞想要吃麥當勞 呢?

sim = cosine_similarity([vectors[0]], [vectors[2]])[0][0]
print(f"第一句 vs 第三句『吃』的相似度:{sim:.4f}")

輸出結果

第一句 vs 第三句『吃』的相似度:0.8466

將將將~~有沒有看到!就算是同一個「吃」在不同語境底下也有不同的向量表示,這就是contextual embedding的厲害啦!!

那今天就先到這邊嚕~明天見!


上一篇
Day 20 讓電腦了解字詞語意 -- word2vec 介紹
系列文
ㄧ個八卦的時間也能了解AI最新趨勢21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言