word2vec


word2vec

word2vec是Google在2013年推出的一個工具。word2vec通過訓練,可以將所有的詞向量化,這樣就可以定量的去度量詞與詞之間的關系,挖掘詞之間的聯系;同時還可以將詞向量輸入到各種RNN網絡中進一步處理。因此,word2vec 輸出的詞向量可以被用來做很多自然語言處理相關的工作,比如聚類、找同義詞、詞性分析等等、文本分析等,是自然語言處理的重要基礎。

本文希望能帶你快速入門word2vec。

Efficient Estimation of Word Representations in Vector Space

word2vec Parameter Learning Explained

基本概念掃盲

語料庫(corpus)、詞(word)、詞匯表(vocabulary)

語料庫一般是指一些根據研究需要從自網站、新聞、報紙等采集的大規模文本。而“詞”是語料中的最小單位。例如:

Winners do what losers do not want to do.

在處理這段非常簡單的語料中,我們會把"winners"作為一個完整詞處理,而不會把詞分為字符單獨處理。通常在自然語言處理(NLP)中,詞也是最小單位,所以有“詞向量”,但是沒有“字符向量“。

而統計語料中出現的不重復的詞構成即可構成詞匯表。如上段語料中出現了"winners losers what want do not to"這7個詞,其中do出現了3次。

為什么要進行詞嵌入(word embedding)?

img

以機器翻譯為例,要把"you"輸入到RNN網絡中,必須把單詞轉化為一個向量(即把詞“嵌入”到高維空間)。

\[you\Longleftrightarrow V_{you}=[v_1,v_2,...,v_n] \]

那么最簡單粗暴的詞嵌入方法就是one-hot編碼:

\[\begin{matrix}I&\Longleftrightarrow& V_I&=&[1,0,0,0,0,0,0,...,0]\\you&\Longleftrightarrow& V_{you}&=&[0,1,0,0,0,0,0,...,0]\\is&\Longleftrightarrow& V_{is}&=&[0,0,1,0,0,0,0,...,0]\\are&\Longleftrightarrow& V_{are}&=&[0,0,0,1,0,0,0,...,0]\\very&\Longleftrightarrow& V_{very}&=&[0,0,0,0,1,0,0,...,0]\\wise&\Longleftrightarrow& V_{wise}&=&[0,0,0,0,0,1,0,...,0]\\smart&\Longleftrightarrow& V_{smart}&=&[0,0,0,0,0,0,1,...,0]\end{matrix} \]

那么one-hot編碼有什么缺點呢?

  • 維度災難

    一般情況下,常用英語單詞約8000個,如果使用one-hot編碼,每個詞向量就是8000維;對應的如果有100000個詞,那么每個詞向量就是100000維。在實際應用中,詞向量維度太大,會造成網絡參數量大、網絡推理速度慢、網絡運行占用內存高等問題。

  • 編碼過於稀疏

    在one-hot編碼的詞向量中,數值幾乎全部是0,非常稀疏,很可能導致實際中網絡難以收斂。

  • 無法表示詞間的關系

    有向量\(A=(a_1,a_2,...,a_n)\)\(B=(b_1,b_2,...,b_n)\),定義\(A\)\(B\)之間相似度為

    \[similarity=cos(\theta)=\frac{A\cdot B}{\left \| A \right \| \left \| B \right \|}=\frac{\sum^n_{i=1}a_i\cdot b_i}{\sqrt{\sum^n_{i=1}(a_i)^2}\sqrt{\sum^n_{i=1}(b_i)^2}} \]

    對於one-hot編碼,任意兩個詞間的相似度都為0,這是違背實際情況的。

那么實際情況是什么?舉例說明:詞"cars"是"car"的復數形式,詞"trucks"又是"truck"的復數形式,所以實際中我們希望他們詞向量相似度很大:

\[similarity(V_{car},V_{cars})\approx similarity(V_{truck},V_{trucks})\approx 1 \]

由於"car"和"truck"詞義接近,"car"和"china"詞義差別較大,我們也希望有如下關系:

\[similarity(V_{car},V_{truck})> similarity(V_{car},V_{china}) \]

img

那么不禁要問:有沒有一種神經網絡,輸入每個詞的one-hot編碼,就可以輸出符合上述要求的詞向量?

img

有,就是word2vec!

word2vec詳解

word2vec是一個典型的3層全連接網絡:INPUT->PROJECTION->OUTPUT,假設:

  • INPUT層->PROJECTION層權重為\(W_1\)矩陣
  • PROJECTION層->OUTPUT層權重為\(W_2\)矩陣

其中\(W_1,W_2\)通過訓練得到

flowchart LR word-->1[one-hot label]-->INPUT--W1-->PROJECTION--W2-->OUTPUT-->2[word vector] INPUT-->PROJECTION INPUT-->PROJECTION INPUT-->PROJECTION INPUT-->PROJECTION INPUT-->PROJECTION INPUT-->PROJECTION INPUT-->PROJECTION INPUT-->PROJECTION PROJECTION-->OUTPUT PROJECTION-->OUTPUT PROJECTION-->OUTPUT PROJECTION-->OUTPUT PROJECTION-->OUTPUT PROJECTION-->OUTPUT PROJECTION-->OUTPUT PROJECTION-->OUTPUT

那么:\(詞向量 = 詞的one-hot編碼向量(轉置)\times W_1\)

img

所以\(W_1\)就是由字典中所有詞對應的詞向量組成的矩陣:

\[W_1=(V^T_{word_1},V^T_{word_2},...,V^T_{word_n}) \]

那么如何訓練網絡獲得\(W_1,W_2\)矩陣?

word2vec提出了CBOW與skip-gram結構。

img

CBOW結構:根據輸入周圍\(2c\)個詞來預測出這個詞本身(即通過上下文預測詞語):

\[CBOW:context(w_t)=(w_{t-c},...,w_{t-1},w_{t+1},...,w_{t+c})\overset{predict}{\longrightarrow}w_t \]

skip-gram結構:根據輸入詞來預測周圍\(2n\)個詞(即預測詞語的上下文):

\[skip-gram:w_t \overset{predict}{\longrightarrow}context(w_t)=(w_{t-c},...,w_{t-1},w_{t+1},...,w_{t+c}) \]

huffman樹

huffman樹是一種特殊結構的二叉樹,通過huffman樹編碼的huffman碼,在通信領域有着廣泛的應用。在word2vec模型中構建PROJECTION->OUTPUT的Hierarchical softmax過程中,也使用到了huffman樹。

構建huffman樹流程:

  1. 根據給定的n個權值{w1, w2, w3 ... wn},構造n棵只有根節點的二叉樹,令起權值為wj

  2. 在森林中選取兩棵根節點權值最小的樹作為左右子樹,構造一顆新的二叉樹,置新二叉樹根節點權值為其左右子樹根節點權值之和。

    注意,左子樹的權值應小於右子樹的權值。

  3. 從森林中刪除這兩棵樹,同時將新得到的二叉樹加入森林中。

    換句話說,之前的2棵最小的根節點已經被合並成一個新的結點了。

  4. 重復上述兩步,直到只含一棵樹為止,這棵樹即是“哈弗曼樹”

接下來舉例說明如何構造huffman樹:

img

假設我們從某書籍中收集了一段語料,統計在語料中出現的詞及每個詞出現的次數,生成如下詞匯表(這里只是舉例,現實中的詞匯表一定會非常大)。

vocabulary = {
    "are" 32,
    "you": 21,
    "and": 19,
    "very": 10,
    "hi": 7,
    "guys": 6,
    "wise": 2,
    "smart": 3,
}

然后通過每個詞在語料中的出現次數建立huffman樹,作為PROJECTION->OUTPUT結構。

另外在word2vec中約定:從huffman樹根節點(root)開始,每次父節點向左子葉遍歷編碼為1,向右子葉遍歷為0。如and編碼為11,smart編碼為01110(出現頻率越高的詞編碼越短)。

img

在word2vec中使用huffman樹的重要原因就是降低訓練時的計算量。

一般來說,訓練用的語料庫都非常大。而在語料庫中,有一些詞出現頻率很高,還有一些詞出現頻率很低,而且這種頻率差異是非常巨大的。那么采用huffman樹后,出現頻率很高的常用詞路徑短,計算量小,從而降低了整個word2vec模型在訓練時的計算開銷。

CBOW(Continuous Bag of Words)結構

img

在之前提到過,CBOW根據輸入詞周圍\(2c\)個詞來預測出這個詞本身。如果當前網絡已經充分訓練,那么輸入you、are、 wise、and四個詞,則應該輸出詞very。那么CBOW結構的word2vec網絡是如何訓練的?

  • 從INPUT->PROJECTION層

    CBOW結構首先會取中心詞的\(2c\)個上下文詞,然后用這些上下文詞的one-hot編碼向量乘以\(W_1\)權重再求和:

    \[V_{context(word_i)}=V_c=\frac{1}{2c}(\sum^c_{t=1}V_{onehot(word_{i+t})}\cdot W_1+\sum^c_{t=1}V_{onehot(word_{i-t})}\cdot W_1) \]

    其中\(V_{onehot(word_i)}\)代表詞\(word_i\)的one-hot編碼向量;\(V_{context(word_i)}\)代表\(word_i\)的上下文詞的詞向量之和(為了便於書寫記為\(V_c\)

  • 從PROJECTION->OUTPUT層(Hierarchical Softmax)

    以詞"very"為例在huffman樹中需要分類4次(編碼為0100)

    • 第一次分類結果\(d_1=0\)概率為:

      \[P(d_1|V_c,\theta_1)=1-\sigma(V^T_c\cdot \theta_1) \]

      其中\(\sigma(x)=\frac{1}{1+e^x}\in(0,1)\)

      img

    • 第二次分類為\(d_2=1\)概率為:

      \[P(d_2|V_c,\theta_2)=\sigma(V^T_c\cdot \theta_2) \]

    • 第三次分類為\(d_3=0\)概率為:

      \[P(d_3|V_c,\theta_3)=1-\sigma(V^T_c\cdot \theta_3) \]

    • 第四次分類為\(d_4=0\)概率為:

      \[P(d_4|V_c,\theta_4)=1-\sigma(V^T_c\cdot \theta_4) \]

    所以詞"very"最終Hierarchical Softmax最終概率為:

    \[P(very|context(very))=\prod_{j=1}^{4}P(d_j|V_c,\theta_j) \]

    img

    推廣一下,詞\(word_i\)最終Hierarchical Softmax最終概率為:

    \[P(word_i|context(word_i))=\prod_{j=1}^{l_i}P(d_j|V_c,\theta_j) \]

    其中\(l_i\)\(word_i\)的huffman樹路徑長度(如"very"為4),而\(P(d_j|V_c,\theta_j)\)為:

    \[P(d_j|V_c,\theta_j)=\left\{\begin{matrix} \sigma(V_c^T\cdot \theta_j) &d_j=0 \\ 1-\sigma(V^T_c\cdot \theta_j)&d_j=1 \end{matrix}\right.\]

    簡化一下

    \[P(d_j|V_c,\theta_j)=[\sigma(V_c^T\cdot \theta_j) ]^{1-d_j}\cdot[1-\sigma(V^T_c\cdot \theta_j)]^{d_j} \]

  • 從OUTPUT->PROJECTION->INPUT訓練

    在訓練時,我們顯然希望輸入是\(word_i\)\(2c\)個上下文詞時輸出使\(word_i\),即概率\(P(word_i|context(word_i))\)越大越好,那么最終優化目標就是對預料中每個詞\(word_i\)都有\(L\)最大:

    \[\begin{matrix}L&=\underset{word_i\in C}{\sum}P(word_i|context(word_i))\\&=\underset{word_i\in C}{\sum}\prod_{j=1}^{l_i}[\sigma(V_c^T\cdot \theta_j) ]^{1-d_j}\cdot[1-\sigma(V^T_c\cdot \theta_j)]^{d_j}\end{matrix} \]

    其中\(C\)代表訓練使用的語料庫,\(l_i\)代表詞\(word_i\)的huffman樹路徑長度。而優化目標是乘法形式,所以取對數\(log\)將優化目標轉化為加法:

    \[\begin{matrix}L'&=\underset{word_i\in C}{\sum}log\prod_{j=1}^{l_i}[\sigma(V_c^T\cdot \theta_j) ]^{1-d_j}\cdot[1-\sigma(V^T_c\cdot \theta_j)]^{d_j}\\&=\underset{word_i\in C}{\sum}\sum^{l_1}_{i=1}\{(1-d_j)log[\sigma(V_c^T\cdot \theta_j)]+g_jlog[1-\sigma(V_c^T\cdot \theta_j)]\}\end{matrix} \]

    由於優化目標是使\(L'\)最大,那么訓練采用梯度上升算法,即每當獲取新的訓練中心詞\(word_i\)時都會通過梯度更新一次權重。記:

    \[L'(i,j)=(1-d_j)log[\sigma(V_c^T\cdot \theta_j)]+g_jlog[1-\sigma(V_c^T\cdot \theta_j)] \]

    這里\(L'(i,j)\)時中心詞為\(word_i\)時的優化目標,即希望通過調整\(\theta_j,V_c\)使得\(L'(i,j)\)最大,所以計算\(L'(i,j)\)\(\theta_j\)的偏導數為

    \[\frac{\partial L'(i,j)}{\partial V_c}=[1-d_j-\sigma(V_c^T\cdot \theta_j)]\theta_j \]

    即可更新\(W_1\)中上下文詞的詞向量\(V_{word_n}\)(注意這里的\(word_n\in context(word_i)\)

    \[V^{new}_{word_i}=V^{old}_{word_n}+\eta\sum^{l_i}_{j=1}\frac{\partial L'(i,j)}{\partial V_c} \]

    其中\(\eta\)表示梯度上升學習率

    對於上式一個比較通俗且不嚴謹的理解:

    誤差傳給了誰,誰就會把梯度返回給傳它誤差的節點,即“原路送回“。在前傳中通過\(V_c\)點將誤差傳遞給了后續網絡,那么在反傳中后續網絡也要把自己所有的梯度\(\sum^{l_i}_{j=1}\frac{\partial L'(i,j)}{\partial V_c}\)還給\(V_c\)節點,然后\(V_c\)又會把梯度返還給\(2c\)個上下文詞的詞向量。

    img

skip-gram結構

img

在之前提到過,skip-gram根據輸入詞詞來預測出周圍\(2c\)個上下文詞。如果當前網絡已經充分訓練,那么輸入very,則應該輸出you、are、 wise、and四個上下文詞。

從INPUT->PROJECTION計算\(word_i\)的詞向量\(V_{worad_i}\),然后PROJECTION直接向后續分層softmax輸出\(V_{worad_i}\) (CBOW是求和)。skip-gram的優化目標與CBOW稍微不同:

\[L'=\underset{word_i\in C}{\sum}log \underset{u\in context(word_i)}{\prod} \prod^{l_i}_{j=1}[\sigma(V_c^T\cdot \theta_j) ]^{1-d_j}\cdot[1-\sigma(V^T_c\cdot \theta_j)]^{d_j} \]

其中多出的\(\underset{u\in context(word_i)}{\prod}\)符號代表skip-gram要通過\(2c\)個huffman樹分別輸出\(2c\)個上下文詞,\(\theta^u_j\)中的\(u\)代表每個輸出詞對應的\(\theta_j\)參數。skip-gram與CBOW非常接近,考慮篇幅這里不再介紹。

word2vec實際測試

Google提供了word2vec的c代碼。使用代碼和text8語料庫訓練200維詞向量,可以看到與詞"google"余弦下降速度最大的詞依次是"yahoo"和"gmail"。Amazing!

img

word2vec缺點

  • OOV(Out of vocabulary)

在word2vec中,詞匯表從開始訓練就已經是確定的。那么在使用時,必然會有詞不在詞匯表中。一般使用<UNKNOW>特殊標志符解決OOV問題,但是當句子中<UNKNOW>過多時必然嚴重影響精度。

后續ELMO使用char cnn、Bert使用word piece,基本解決了OOV問題。

  • 無法處理多義詞

很多詞在不同語境是有含義不同,即多義詞。而word2vec中所有詞的embeding向量都是訓練好即固定的,無法在使用時根據上下文調整,導致處理多義詞效果差。

ELMO和Bert使用訓練language model,動態生成embeding向量,解決多義詞問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM