Alink漫談(十七) :Word2Vec原始碼分析 之 迭代訓練
阿新 • • 發佈:2020-08-08
# Alink漫談(十七) :Word2Vec原始碼分析 之 迭代訓練
[ToC]
## 0x00 摘要
Alink 是阿里巴巴基於實時計算引擎 Flink 研發的新一代機器學習演算法平臺,是業界首個同時支援批式演算法、流式演算法的機器學習平臺。本文和上文將帶領大家來分析Alink中 Word2Vec 的實現。
因為Alink的公開資料太少,所以以下均為自行揣測,肯定會有疏漏錯誤,希望大家指出,我會隨時更新。
## 0x01 前文回顧
從前文 [Alink漫談(十六) :Word2Vec之建立霍夫曼樹](https://www.cnblogs.com/rossiXYZ/p/13427829.html) 我們瞭解了Word2Vec的概念、在Alink中的整體架構以及完成對輸入的處理,以及詞典、二叉樹的建立。
此時我們已經有了一個已經構造好的Huffman樹,以及初始化完畢的各個向量,可以開始輸入文字來進行訓練了。
### 1.1 上文總體流程圖
先給出一個上文總體流程圖:
![](https://img2020.cnblogs.com/blog/1850883/202008/1850883-20200808093538204-1326057810.png)
### 1.2 回顧霍夫曼樹
#### 1.2.1 變數定義
現在定義變數如下:
- n : 一個詞的上下文包含的詞數,與n-gram中n的含義相同
- m : 詞向量的長度,通常在10~100
- h : 隱藏層的規模,一般在100量級
- N :詞典的規模,通常在1W~10W
- T : 訓練文字中單詞個數
#### 1.2.2 為何要引入霍夫曼樹
word2vec也使用了CBOW與Skip-Gram來訓練模型與得到詞向量,但是並沒有使用傳統的DNN模型。最先優化使用的資料結構是用霍夫曼樹來代替隱藏層和輸出層的神經元,霍夫曼樹的葉子節點起到輸出層神經元的作用,葉子節點的個數即為詞彙表的小大。 而內部節點則起到隱藏層神經元的作用。
以CBOW為例,輸入層為n-1個單詞的詞向量,長度為m(n-1),隱藏層的規模為h,輸出層的規模為N。那麼前向的時間複雜度就是o(m(n-1)h+hN) = o(hN) 這還是處理一個詞所需要的複雜度。如果要處理所有文字,則需要o(hNT)的時間複雜度。這個是不可接受的。
同時我們也注意到,o(hNT)之中,h和T的值相對固定,想要對其進行優化,主要還是應該從N入手。而輸出層的規模之所以為N,是因為這個神經網路要完成的是N選1的任務。那麼可不可以減小N的值呢?答案是可以的。解決的思路就是將一次分類分解為多次分類,這也是Hierarchical Softmax的核心思想 。
舉個栗子,有[1,2,3,4,5,6,7,8]這8個分類,想要判斷詞A屬於哪個分類,我們可以一步步來,首先判斷A是屬於[1,2,3,4]還是屬於[5,6,7,8]。如果判斷出屬於[1,2,3,4],那麼就進一步分析是屬於[1,2]還是[3,4],以此類推。這樣一來,就把單個詞的時間複雜度從o(h*N)降為o(h*logN),更重要的減少了記憶體的開銷。
從輸入到輸出,中間是一個樹形結構,其中的每一個節點都完成一個二分類(logistic分類)問題。那麼就**存在一個如何構建樹的問題**。這裡採用huffman樹,因為這樣構建的話,出現頻率越高的詞所經過的路徑越短,從而使得所有單詞的平均路徑長度達到最短。
假設 足球 的路徑 1001,那麼當要輸出 **足球** 這個詞的時候,這個模型其實並不是直接輸出 "1001" 這條路徑,而是在每一個節點都進行一次二分類。這樣相當於將最後輸出的二叉樹變成多個二分類的任務。而路徑中的每個根節點都是一個待求的向量。 也就是說這個模型不僅需要求每個輸入引數的變數,還需要求這棵二叉樹中每個非葉子節點的向量,當然這些向量都只是臨時用的向量。
## 0x02 訓練
### 2.1 訓練流程
Alink 實現的是 **基於Hierarchical Softmax的Skip-Gram模型**。
現在我們先看看基於Skip-Gram模型時, Hierarchical Softmax如何使用。此時輸入的只有一個詞w,輸出的為2c個詞向量context(w)。
訓練的過程主要有輸入層(input),對映層(projection)和輸出層(output)三個階段。
- 我們對於訓練樣本中的每一個詞,該詞本身作為樣本的輸入,其前面的c個詞和後面的c個詞作為了Skip-Gram模型的輸出,期望這些詞的softmax概率比其他的詞大。
- 我們需要先將詞彙表建立成一顆霍夫曼樹(此步驟在上文已經完成)。
- 對於從輸入層到隱藏層(對映層),這一步比CBOW簡單,由於只有一個詞,所以,即x_w就是詞w對應的詞向量。
- 通過梯度上升法來更新我們的θwj−1和x_w,注意這裡的x_w周圍有2c個詞向量,我們期望P(xi|xw),i=1,2...2c最大。此時我們注意到由於上下文是相互的,在期望P(xi|xw),i=1,2...2c最大化的同時,反過來我們也期望P(xw|xi),i=1,2...2c最大。那麼是使用P(xi|xw)好還是P(xw|xi)好呢,word2vec使用了後者,這樣做的好處就是在一個迭代視窗內,我們不是隻更新xw一個詞,而是xi,i=1,2...2c共2c個詞。這樣整體的迭代會更加的均衡。因為這個原因,Skip-Gram模型並沒有和CBOW模型一樣對輸入進行迭代更新,而是對2c個輸出進行迭代更新。
- 從根節點開始,對映層的值需要沿著Huffman樹不斷的進行logistic分類,並且不斷的修正各中間向量和詞向量。
- 假設對映層輸入為 pro(t),單詞為“足球”,即w(t)=“足球”,假設其Huffman碼可知為d(t)=”1001,那麼根據Huffman碼可知,從根節點到葉節點的路徑為“左右右左”,即從根節點開始,先往左拐,再往右拐2次,最後再左拐。
- 既然知道了路徑,那麼就按照路徑從上往下依次修正路徑上各節點的中間向量。在第一個節點,根據節點的中間向量Θ(t,1)和pro(t)進行Logistic分類。如果分類結果顯示為0,則表示分類錯誤 (應該向左拐,即分類到1),則要對Θ(t,1)進行修正,並記錄誤差量。
- 接下來,處理完第一個節點之後,開始處理第二個節點。方法類似,修正Θ(t,2),並累加誤差量。接下來的節點都以此類推。
- 在處理完所有節點,達到葉節點之後,根據之前累計的誤差來修正詞向量v(w(t))。這裡引入學習率概念,η 表示學習率。學習率越大,則判斷錯誤的懲罰也越大,對中間向量的修正跨度也越大。
這樣,一個詞w(t)的處理流程就結束了。如果一個文字中有N個詞,則需要將上述過程在重複N遍,從w(0)~w(N-1)。
這裡總結下基於Hierarchical Softmax的Skip-Gram模型演算法流程,梯度迭代使用了隨機梯度上升法:
- 輸入:基於Skip-Gram的語料訓練樣本,詞向量的維度大小M,Skip-Gram的上下文大小2c,步長η
- 輸出:霍夫曼樹的內部節點模型引數θ,所有的詞向量w
![](https://img2020.cnblogs.com/blog/1850883/202008/1850883-20200808093553961-1064797237.jpg)
### 2.2 生成訓練模型
Huffman樹中非葉節點儲存的中間向量的初始化值是零向量,而葉節點對應的單詞的詞向量是隨機初始化的。我們可以看到,對於 input 和 output,是會進行 AllReduce 的,就是聚合每個task的計算結果 。
```java