《動手學深度學習》task7_3 詞嵌入進階

系統學習《動手學深度學習》點擊下面這個鏈接,有全目錄哦~
https://blog.csdn.net/Shine_rise/article/details/104754764

詞嵌入進階

“Word2Vec的實現”一節中,我們在小規模數據集上訓練了一個 Word2Vec 詞嵌入模型,並通過詞向量的餘弦相似度搜索近義詞。雖然 Word2Vec 已經能夠成功地將離散的單詞轉換爲連續的詞向量,並能一定程度上地保存詞與詞之間的近似關係,但 Word2Vec 模型仍不是完美的,它還可以被進一步地改進:

  1. 子詞嵌入(subword embedding):FastText 以固定大小的 n-gram 形式將單詞更細緻地表示爲了子詞的集合,而 BPE (byte pair encoding) 算法則能根據語料庫的統計信息,自動且動態地生成高頻子詞的集合;
  2. GloVe 全局向量的詞嵌入: 通過等價轉換 Word2Vec 模型的條件概率公式,我們可以得到一個全局的損失函數表達,並在此基礎上進一步優化模型。

實際中,我們常常在大規模的語料上訓練這些詞嵌入模型,並將預訓練得到的詞向量應用到下游的自然語言處理任務中。本節就將以 GloVe 模型爲例,演示如何用預訓練好的詞向量來求近義詞和類比詞。

GloVe 全局向量的詞嵌入

GloVe 模型

先簡單回顧以下 Word2Vec 的損失函數(以 Skip-Gram 模型爲例,不考慮負採樣近似):
t=1Tmjm,j0logP(w(t+j)w(t)) -\sum_{t=1}^T\sum_{-m\le j\le m,j\ne 0} \log P(w^{(t+j)}\mid w^{(t)})
其中
P(wjwi)=exp(ujvi)kVexp(ukvi) P(w_j\mid w_i) = \frac{\exp(\boldsymbol{u}_j^\top\boldsymbol{v}_i)}{\sum_{k\in\mathcal{V}}\exp(\boldsymbol{u}_k^\top\boldsymbol{v}_i)}
wiw_i 爲中心詞,wjw_j 爲背景詞時 Skip-Gram 模型所假設的條件概率計算公式,我們將其簡寫爲 qijq_{ij}

注意到此時我們的損失函數中包含兩個求和符號,它們分別枚舉了語料庫中的每個中心詞和其對應的每個背景詞。實際上我們還可以採用另一種計數方式,那就是直接枚舉每個詞分別作爲中心詞和背景詞的情況:
iVjVxijlogqij -\sum_{i\in\mathcal{V}}\sum_{j\in\mathcal{V}} x_{ij}\log q_{ij}
其中 xijx_{ij} 表示整個數據集中 wjw_j 作爲 wiw_i 的背景詞的次數總和。

我們還可以將該式進一步地改寫爲交叉熵 (cross-entropy) 的形式如下:
iVxijVpijlogqij -\sum_{i\in\mathcal{V}}x_i\sum_{j\in\mathcal{V}}p_{ij} \log q_{ij}
其中 xix_iwiw_i 的背景詞窗大小總和,pij=xij/xip_{ij}=x_{ij}/x_iwjw_jwiw_i 的背景詞窗中所佔的比例。

從這裏可以看出,我們的詞嵌入方法實際上就是想讓模型學出 wjw_j 有多大概率是 wiw_i 的背景詞,而真實的標籤則是語料庫上的統計數據。同時,語料庫中的每個詞根據 xix_i 的不同,在損失函數中所佔的比重也不同。

注意到目前爲止,我們只是改寫了 Skip-Gram 模型損失函數的表面形式,還沒有對模型做任何實質上的改動。而在 Word2Vec 之後提出的 GloVe 模型,則是在之前的基礎上做出了以下幾點改動:

  1. 使用非概率分佈的變量 pij=xijp'_{ij}=x_{ij}qij=exp(ujvi)q′_{ij}=\exp(\boldsymbol{u}^\top_j\boldsymbol{v}_i),並對它們取對數;
  2. 爲每個詞 wiw_i 增加兩個標量模型參數:中心詞偏差項 bib_i 和背景詞偏差項 cic_i,鬆弛了概率定義中的規範性;
  3. 將每個損失項的權重 xix_i 替換成函數 h(xij)h(x_{ij}),權重函數 h(x)h(x) 是值域在 [0,1][0,1] 上的單調遞增函數,鬆弛了中心詞重要性與 xix_i 線性相關的隱含假設;
  4. 用平方損失函數替代了交叉熵損失函數。

綜上,我們獲得了 GloVe 模型的損失函數表達式:
iVjVh(xij)(ujvi+bi+cjlogxij)2 \sum_{i\in\mathcal{V}}\sum_{j\in\mathcal{V}} h(x_{ij}) (\boldsymbol{u}^\top_j\boldsymbol{v}_i+b_i+c_j-\log x_{ij})^2
由於這些非零 xijx_{ij} 是預先基於整個數據集計算得到的,包含了數據集的全局統計信息,因此 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個詞“aa 之於 bb 相當於 cc 之於 dd”,給定前3個詞 a,b,ca,b,cdd。求類比詞的思路是,搜索與 vec(c)+vec(b)vec(a)\text{vec}(c)+\text{vec}(b)−\text{vec}(a) 的結果向量最相似的詞向量,其中 vec(w)\text{vec}(w)ww 的詞向量。

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'


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章