近期在準備2020屆秋招,覺得自己簡歷上眾多Embedding的項目,但是自己有時候卻不能很好地表達,給面試官留下了不好的印象。所以準備還是開個專欄,一來是給自己備忘並鍛煉自己的表述能力,二來如果能幫助到別人就再好不過了。

Embedding的技術要從word2vec開始說起,作為本專欄的第一篇文章,就詳細講解一下word2vec。因為知乎上介紹word2vec的文章實在是太多了,我這裡會重點說一下別的文章說的比較少的部分的細節(層次Softmax等),使這些總結略有點價值。

1. 什麼是word2vec

簡單來說,word2vec就是將辭彙轉化為向量的模型,向量裏蘊含了豐富了語義和上下文信息。word2vec的歷史要從NNLM開始講起了,相比較NNLM,word2vec去掉了隱層的激活函數,並將輸出層softmax優化。在看word2vec源碼之前,很難想像作者是用C語言完成了神經網路的訓練,與之相對應的有各種trick,使其具有很廣的適用範圍,使word2vec稱為一個現象級的模型。

word2vec是一個3層的神經網路。輸入層和輸出層都可以看做辭彙表的one-hot表示(</s>表示回車符)。通過語料的訓練,可以獲得辭彙的向量表示。

word2vec模型示意

對於word2vec最基礎的理解:word2vec是一個僅有3層的淺層神經網路,它的輸入和輸出層維度都是 N ,隱層維度為 h 。最終學到的詞向量實際上是該網路的輸入層-隱層的權重(維度為 N*h ,其中 N 是辭彙表大小, h 是詞向量維度)。

2.0 滑動窗口演算法

對於一句完整的話,word2vec採取滑動窗口演算法,在 寬度為w 大小的窗口內,獲得中心詞 V_c 和上下文辭彙 V_{i} ,如下圖。中心詞為fox, 上下文辭彙以紫色標出。

word2vec滑動窗口示例

邊界條件:當中心詞位於邊緣時,窗口大小減小(word2vec沒有做padding處理)

word2vec滑動窗口邊界情況

對應源碼(對應上圖,window_size=2*2+1,b=3):

for (a = b; a < window * 2 + 1 - b; a++) if (a != window) {
c = sentence_position - window + a;
if (c < 0) continue;
if (c >= sentence_length) continue;
...

2.1 CBOW和Skip-Gram

滑動窗口只是提供了獲取局部語料的途徑。為了建模辭彙之間的關係,有2種模型:CBOW(Continuous bag-of-words 連續詞袋模型)和SkipGram(姑且可以叫:跳詞模型[區別於n-gram獲取的都是連續的])這兩種模型直接涉及到模型輸入和輸出的區別:

CBOW: 每個滑動窗口是一個測試用例。輸入 層[V_1, V_2, ...V_{c-1}, V_{c+1}...V_w] ,輸出層 V_c 。即用中心詞 的上下文(不包含 V_c )為輸入,最大化預測輸出為中心詞 V_c 的概率。

Skip-Gram: 每個滑動窗口是w個測試用例。輸入層 V_c ,輸出層 V_i,i
e c 。即用中心詞,最大化預測輸出層為上下文辭彙的概率。

可以看到,word2vec雖然是無監督的,但是其模型卻是有監督的。只不過這個監督學習不需要用戶標註,而是自然存在於語料裡面了。

下面用圖更直觀地展示一下,先看CBOW:

CBOW示例

公式化為如下:

Z^{1}=sum_i^4 W_{i*}x_i+b_i \ h=A^1= frac {1}{4} sum _i z_i^{1} \ Z^{2}= h*W^{} \ hat {y}=a=softmax(Z^{2})=frac {e^{z^2_i}}{sum_k^N e^{z^2_k}}

其中 h 是隱層的輸出, a 是輸出層的輸出(softmax歸一化)。簡單來說,CBOW的做法就是將4個辭彙對應的向量加和求平均,再與 W^{} 乘積後softmax歸一化。因為輸出層辭彙的one-hot可以對應 y=[0,0.....0,1,0,.....0] ,對於softmax輸出一般採用最小化負的交叉熵的似然: Cost=- sum_i^N y_i log( hat y) ,然後使用梯度下降即可更新。

Skip-Gram與CBOW的方法略有不同,每個詞對(中心詞,上下文詞)都是一個訓練樣本:

Skip-Gram示例

與CBOW的差別僅在輸入-隱層,對應的公式

h=A^{1}=Z^{1}= W_{i*}x_i+b_i

因為只有一個輸入單詞,連求平均都省了。相當於直接獲取 W 的第i行(對應中心詞的向量)之後的操作和CBOW相同,都是使用梯度下降更新參數 W .

可以看到,CBOW模型的每個滑動窗口,只會進行一次計算(上下文辭彙取平均,預測中心詞)。所以CBOW的訓練速度會略快於Skip-gram.

3. 層次Softmax

上述方法確實能夠訓練詞向量,但是速度太慢了。主要是因為softmax歸一化操作,使得每一個訓練樣例在前向傳播和反向傳播時都需要計算分母 sum_k^N e^{z_k^2} ,對於一個有 N 個辭彙的語料(比如30w,300w等大小),這個計算將會非常耗時。

於是有學者提出了Hierarchical Softmax來解決這個問題。層次Softmax是一個二叉樹結構,每個非葉子節點是個二分類器,每個葉子節點對應辭彙表中的單詞( N 個辭彙, N-1 個二分類器)。二叉樹是根據詞頻構建的哈夫曼樹。

對於一個訓練樣本(以 (fox, quick) 為例),如下圖

層次Softmax模型示例

softmax層的求和項被一系列二分類器代替,如果當前節點到目標葉子節點往左走,該節點的輸出為二分類器的輸出 sigma ,否則輸出為 1-sigma 。最終輸出為各個二分類器節點輸出的乘積 Cost =sigma(h*W_1)*(1 - sigma(h*W_2))*(1 - sigma(h*W_3))

同樣使用梯度下降優化。顯然此時的計算量大大減少。因為只需要更新從根節點到葉子節點所經過的路徑上的二分類器的權重就好了,其餘的節點權重不需要更新。例如對於上述公式只需要求偏導 frac{partial Cost}{partial W_1} , frac{partial Cost}{partial W_2}, frac{partial Cost}{partial W_3} 即可.

由於二叉樹葉子節點概率之和為1,所以層次Softmax相當於自動完成了歸一化操作,故與普通Softmax可以等價。

採用Softmax之後,需要需要更新參數的數量從 N 降低到 logN ,對於一個大語料來說,這個改進可以說很大了。但是實際上現在實際訓練中很少採取這種模型,因為有個訓練速度更快、獲得詞向量效果更好的負採樣演算法可以使用。

4. Negative Sampling(負採樣)

我們將原始softmax輸出層的 N 個節點變為 N 個二分類器(比如 Logistic 回歸),這樣輸出層公式變為 hat y_i = sigma(Z^2_i)=frac{1}{1+e^{-z_i}} ,就不存在softmax求和項。

但是此時的訓練樣本就需要改進,因為原始的「詞對」都是從語料中提取的,這些詞對正確體現了語料中辭彙的共現關係。僅僅使用這些數據對於輸出層是不夠的,因為輸出層的二分類器需要同時接收「正例」和「負例」輸入才能正確建模共現關係。

負採樣針對每個「詞對」,採樣一定數量的負例(即錯誤的辭彙共現關係),例如對於 (fox, quick) ,採樣多個辭彙替代 quick ,得到負例如 (fox, birthday)(fox, March) 。輸入模型進行訓練,輸出層的損失函數變為:

-[y_i*log(sigma (W_{*i}h))+sum_k^{Samples} (1-y_k)*log(1-sigma(W_{*k}h))]

該式左側為正例樣本的損失,右側為採樣的多個負例的損失。使用梯度下降更新相應的參數。一般採樣數設置為5,所以需要更新的W數量為6,進一步降低了計算量。

5. 參考文獻

以下推薦幾個網站,都是非常好的解釋word2vec的網站,值得仔細研讀。

網易有道word2vec解讀?

kexue.fm

Approximating the Softmax?

ruder.io
圖標
word2vec原理(一) CBOW與Skip-Gram模型基礎?

www.cnblogs.com
圖標

初次發專欄,很多地方需要改進,歡迎各位提提意見和交流。

推薦閱讀:

相關文章