系統學習《動手學深度學習》點擊下面這個鏈接,有全目錄哦~
https://blog.csdn.net/Shine_rise/article/details/104754764
詞嵌入進階
在“Word2Vec的實現”一節中,我們在小規模數據集上訓練了一個 Word2Vec 詞嵌入模型,並通過詞向量的餘弦相似度搜索近義詞。雖然 Word2Vec 已經能夠成功地將離散的單詞轉換爲連續的詞向量,並能一定程度上地保存詞與詞之間的近似關係,但 Word2Vec 模型仍不是完美的,它還可以被進一步地改進:
- 子詞嵌入(subword embedding):FastText 以固定大小的 n-gram 形式將單詞更細緻地表示爲了子詞的集合,而 BPE (byte pair encoding) 算法則能根據語料庫的統計信息,自動且動態地生成高頻子詞的集合;
- GloVe 全局向量的詞嵌入: 通過等價轉換 Word2Vec 模型的條件概率公式,我們可以得到一個全局的損失函數表達,並在此基礎上進一步優化模型。
實際中,我們常常在大規模的語料上訓練這些詞嵌入模型,並將預訓練得到的詞向量應用到下游的自然語言處理任務中。本節就將以 GloVe 模型爲例,演示如何用預訓練好的詞向量來求近義詞和類比詞。
GloVe 全局向量的詞嵌入
GloVe 模型
先簡單回顧以下 Word2Vec 的損失函數(以 Skip-Gram 模型爲例,不考慮負採樣近似):
其中
是 爲中心詞, 爲背景詞時 Skip-Gram 模型所假設的條件概率計算公式,我們將其簡寫爲 。
注意到此時我們的損失函數中包含兩個求和符號,它們分別枚舉了語料庫中的每個中心詞和其對應的每個背景詞。實際上我們還可以採用另一種計數方式,那就是直接枚舉每個詞分別作爲中心詞和背景詞的情況:
其中 表示整個數據集中 作爲 的背景詞的次數總和。
我們還可以將該式進一步地改寫爲交叉熵 (cross-entropy) 的形式如下:
其中 是 的背景詞窗大小總和, 是 在 的背景詞窗中所佔的比例。
從這裏可以看出,我們的詞嵌入方法實際上就是想讓模型學出 有多大概率是 的背景詞,而真實的標籤則是語料庫上的統計數據。同時,語料庫中的每個詞根據 的不同,在損失函數中所佔的比重也不同。
注意到目前爲止,我們只是改寫了 Skip-Gram 模型損失函數的表面形式,還沒有對模型做任何實質上的改動。而在 Word2Vec 之後提出的 GloVe 模型,則是在之前的基礎上做出了以下幾點改動:
- 使用非概率分佈的變量 和 ,並對它們取對數;
- 爲每個詞 增加兩個標量模型參數:中心詞偏差項 和背景詞偏差項 ,鬆弛了概率定義中的規範性;
- 將每個損失項的權重 替換成函數 ,權重函數 是值域在 上的單調遞增函數,鬆弛了中心詞重要性與 線性相關的隱含假設;
- 用平方損失函數替代了交叉熵損失函數。
綜上,我們獲得了 GloVe 模型的損失函數表達式:
由於這些非零 是預先基於整個數據集計算得到的,包含了數據集的全局統計信息,因此 GloVe 模型的命名取“全局向量”(Global Vectors)之意。
載入預訓練的 GloVe 向量
GloVe 官方 提供了多種規格的預訓練詞向量,語料庫分別採用了維基百科、CommonCrawl和推特等,語料庫中詞語總數也涵蓋了從60億到8,400億的不同規模,同時還提供了多種詞向量維度供下游模型使用。
torchtext.vocab
中已經支持了 GloVe, FastText, CharNGram 等常用的預訓練詞向量,我們可以通過聲明 torchtext.vocab.GloVe
類的實例來加載預訓練好的 GloVe 詞向量。
import torch
import torchtext.vocab as vocab
print([key for key in vocab.pretrained_aliases.keys() if "glove" in key])
cache_dir = "/home/kesci/input/GloVe6B5429"
glove = vocab.GloVe(name='6B', dim=50, cache=cache_dir)
print("一共包含%d個詞。" % len(glove.stoi))
print(glove.stoi['beautiful'], glove.itos[3366])
['glove.42B.300d', 'glove.840B.300d', 'glove.twitter.27B.25d', 'glove.twitter.27B.50d', 'glove.twitter.27B.100d', 'glove.twitter.27B.200d', 'glove.6B.50d', 'glove.6B.100d', 'glove.6B.200d', 'glove.6B.300d']
一共包含400000個詞。
3366 beautiful
求近義詞和類比詞
求近義詞
由於詞向量空間中的餘弦相似性可以衡量詞語含義的相似性(爲什麼?),我們可以通過尋找空間中的 k 近鄰,來查詢單詞的近義詞。
def knn(W, x, k):
'''
@params:
W: 所有向量的集合
x: 給定向量
k: 查詢的數量
@outputs:
topk: 餘弦相似性最大k個的下標
[...]: 餘弦相似度
'''
cos = torch.matmul(W, x.view((-1,))) / (
(torch.sum(W * W, dim=1) + 1e-9).sqrt() * torch.sum(x * x).sqrt())
_, topk = torch.topk(cos, k=k)
topk = topk.cpu().numpy()
return topk, [cos[i].item() for i in topk]
def get_similar_tokens(query_token, k, embed):
'''
@params:
query_token: 給定的單詞
k: 所需近義詞的個數
embed: 預訓練詞向量
'''
topk, cos = knn(embed.vectors,
embed.vectors[embed.stoi[query_token]], k+1)
for i, c in zip(topk[1:], cos[1:]): # 除去輸入詞
print('cosine sim=%.3f: %s' % (c, (embed.itos[i])))
get_similar_tokens('chip', 3, glove)
cosine sim=0.856: chips
cosine sim=0.749: intel
cosine sim=0.749: electronics
100%|█████████▉| 398393/400000 [00:30<00:00, 38997.22it/s]
get_similar_tokens('baby', 3, glove)
cosine sim=0.839: babies
cosine sim=0.800: boy
cosine sim=0.792: girl
get_similar_tokens('beautiful', 3, glove)
cosine sim=0.921: lovely
cosine sim=0.893: gorgeous
cosine sim=0.830: wonderful
求類比詞
除了求近義詞以外,我們還可以使用預訓練詞向量求詞與詞之間的類比關係,例如“man”之於“woman”相當於“son”之於“daughter”。求類比詞問題可以定義爲:對於類比關係中的4個詞“ 之於 相當於 之於 ”,給定前3個詞 求 。求類比詞的思路是,搜索與 的結果向量最相似的詞向量,其中 爲 的詞向量。
def get_analogy(token_a, token_b, token_c, embed):
'''
@params:
token_a: 詞a
token_b: 詞b
token_c: 詞c
embed: 預訓練詞向量
@outputs:
res: 類比詞d
'''
vecs = [embed.vectors[embed.stoi[t]]
for t in [token_a, token_b, token_c]]
x = vecs[1] - vecs[0] + vecs[2]
topk, cos = knn(embed.vectors, x, 1)
res = embed.itos[topk[0]]
return res
get_analogy('man', 'woman', 'son', glove)
'daughter'
get_analogy('beijing', 'china', 'tokyo', glove)
'japan'
get_analogy('bad', 'worst', 'big', glove)
'biggest'
get_analogy('do', 'did', 'go', glove)
'went'