雷鋒網按:這是一個關於 Skip-Gram 模型的系列教程,依次分為結構、訓練和實現三個部分,本文為第二部分:訓練篇,最後一部分我們將隨後發布,敬請期待。原文作者天雨粟,原載於作者知乎專欄,雷鋒網已獲授權。
第一部分我們了解skip-gram的輸入層、隱層、輸出層。在第二部分,會繼續深入講如何在skip-gram模型上進行高效的訓練。
在第一部分講解完成後,我們會發現Word2Vec模型是一個超級大的神經網絡(權重矩陣規模非常大)。
舉個慄子,我們擁有10000個單詞的詞彙表,我們如果想嵌入300維的詞向量,那麼我們的輸入-隱層權重矩陣和隱層-輸出層的權重矩陣都會有 10000 x 300 = 300萬個權重,在如此龐大的神經網絡中進行梯度下降是相當慢的。更糟糕的是,你需要大量的訓練數據來調整這些權重並且避免過擬合。百萬數量級的權重矩陣和億萬數量級的訓練樣本意味著訓練這個模型將會是個災難(太兇殘了)。
Word2Vec 的作者在它的第二篇論文中強調了這些問題,下面是作者在第二篇論文中的三個創新:
1. 將常見的單詞組合(word pairs)或者詞組作為單個「words」來處理。
2. 對高頻次單詞進行抽樣來減少訓練樣本的個數。
3. 對優化目標採用「negative sampling」方法,這樣每個訓練樣本的訓練只會更新一小部分的模型權重,從而降低計算負擔。
事實證明,對常用詞抽樣並且對優化目標採用「negative sampling」不僅降低了訓練過程中的計算負擔,還提高了訓練的詞向量的質量。
論文的作者指出,一些單詞組合(或者詞組)的含義和拆開以後具有完全不同的意義。比如「Boston Globe」是一種報刊的名字,而單獨的「Boston」和「Globe」這樣單個的單詞卻表達不出這樣的含義。因此,在文章中只要出現「Boston Globe」,我們就應該把它作為一個單獨的詞來生成其詞向量,而不是將其拆開。同樣的例子還有「New York」,「United Stated」等。
在Google發布的模型中,它本身的訓練樣本中有來自Google News數據集中的1000億的單詞,但是除了單個單詞以外,單詞組合(或詞組)又有3百萬之多。
如果你對模型的詞彙表感興趣,可以點擊:
http://t.cn/RoVde3h
你還可以直接瀏覽這個詞彙表:
http://t.cn/RoVdsZr
如果想了解這個模型如何進行文檔中的詞組抽取,可以看論文中「Learning Phrases」這一章,對應的代碼在 word2phrase.c ,相關連結如下。
論文連結:
http://t.cn/RMct1c7
代碼連結:
http://t.cn/R5auFLz
在第一部分的講解中,我們展示了訓練樣本是如何從原始文檔中生成出來的,這裡我再重複一次。我們的原始文本為「The quick brown fox jumps over the laze dog」,如果我使用大小為2的窗口,那麼我們可以得到圖中展示的那些訓練樣本。
但是對於「the」這種常用高頻單詞,這樣的處理方式會存在下面兩個問題:
當我們得到成對的單詞訓練樣本時,("fox", "the") 這樣的訓練樣本並不會給我們提供關於「fox」更多的語義信息,因為「the」在每個單詞的上下文中幾乎都會出現。
由於在文本中「the」這樣的常用詞出現概率很大,因此我們將會有大量的(」the「,...)這樣的訓練樣本,而這些樣本數量遠遠超過了我們學習「the」這個詞向量所需的訓練樣本數。
Word2Vec通過「抽樣」模式來解決這種高頻詞問題。它的基本思想如下:對於我們在訓練原始文本中遇到的每一個單詞,它們都有一定概率被我們從文本中刪掉,而這個被刪除的概率與單詞的頻率有關。
如果我們設置窗口大小(即),並且從我們的文本中刪除所有的「the」,那麼會有下面的結果:
1. 由於我們刪除了文本中所有的「the」,那麼在我們的訓練樣本中,「the」這個詞永遠也不會出現在我們的上下文窗口中。
2. 當「the」作為input word時,我們的訓練樣本數至少會減少10個。
這句話應該這麼理解,假如我們的文本中僅出現了一個「the」,那麼當這個「the」作為input word時,我們設置span=10,此時會得到10個訓練樣本 ("the", ...) ,如果刪掉這個「the」,我們就會減少10個訓練樣本。實際中我們的文本中不止一個「the」,因此當「the」作為input word的時候,至少會減少10個訓練樣本。
上面提到的這兩個影響結果實際上就幫助我們解決了高頻詞帶來的問題。
word2vec的C語言代碼實現了一個計算在詞彙表中保留某個詞概率的公式。
ωi 是一個單詞,Z(ωi) 是 ωi 這個單詞在所有語料中出現的頻次。舉個慄子,如果單詞「peanut」在10億規模大小的語料中出現了1000次,那麼 Z(peanut) = 1000/1000000000 = 1e - 6。
在代碼中還有一個參數叫「sample」,這個參數代表一個閾值,默認值為0.001(在gensim包中的Word2Vec類說明中,這個參數默認為0.001,文檔中對這個參數的解釋為「 threshold for configuring which higher-frequency words are randomly downsampled」)。這個值越小意味著這個單詞被保留下來的概率越小(即有越大的概率被我們刪除)。
P(ωi) 代表著保留某個單詞的概率:
圖中x軸代表著 Z(ωi) ,即單詞 ωi 在語料中出現頻率,y軸代表某個單詞被保留的概率。對於一個龐大的語料來說,單個單詞的出現頻率不會很大,即使是常用詞,也不可能特別大。
從這個圖中,我們可以看到,隨著單詞出現頻率的增高,它被採樣保留的概率越來越小,我們還可以看到一些有趣的結論:
● 當 Z(ωi) <= 0.0026 時,P(ωi) = 1.0 。當單詞在語料中出現的頻率小於 0.0026 時,它是 100% 被保留的,這意味著只有那些在語料中出現頻率超過 0.26% 的單詞才會被採樣。
● 當時 Z(ωi) = 0.00746 時,P(ωi) = 0.5,意味著這一部分的單詞有 50% 的概率被保留。
● 當 Z(ωi) = 1.0 時,P(ωi) = 0.033,意味著這部分單詞以 3.3% 的概率被保留。
如果你去看那篇論文的話,你會發現作者在論文中對函數公式的定義和在C語言代碼的實現上有一些差別,但我認為C語言代碼的公式實現是更權威的一個版本。
訓練一個神經網絡意味著要輸入訓練樣本並且不斷調整神經元的權重,從而不斷提高對目標的準確預測。每當神經網絡經過一個訓練樣本的訓練,它的權重就會進行一次調整。
正如我們上面所討論的,vocabulary的大小決定了我們的Skip-Gram神經網絡將會擁有大規模的權重矩陣,所有的這些權重需要通過我們數以億計的訓練樣本來進行調整,這是非常消耗計算資源的,並且實際中訓練起來會非常慢。
負採樣(negative sampling)解決了這個問題,它是用來提高訓練速度並且改善所得到詞向量的質量的一種方法。不同於原本每個訓練樣本更新所有的權重,負採樣每次讓一個訓練樣本僅僅更新一小部分的權重,這樣就會降低梯度下降過程中的計算量。
當我們用訓練樣本 ( input word: "fox",output word: "quick") 來訓練我們的神經網絡時,「 fox」和「quick」都是經過one-hot編碼的。如果我們的vocabulary大小為10000時,在輸出層,我們期望對應「quick」單詞的那個神經元結點輸出1,其餘9999個都應該輸出0。在這裡,這9999個我們期望輸出為0的神經元結點所對應的單詞我們稱為「negative」 word。
當使用負採樣時,我們將隨機選擇一小部分的negative words(比如選5個negative words)來更新對應的權重。我們也會對我們的「positive」 word進行權重更新(在我們上面的例子中,這個單詞指的是」quick「)。
在論文中,作者指出指出對於小規模數據集,選擇5-20個negative words會比較好,對於大規模數據集可以僅選擇2-5個negative words。
回憶一下我們的隱層-輸出層擁有300 x 10000的權重矩陣。如果使用了負採樣的方法我們僅僅去更新我們的positive word-「quick」的和我們選擇的其他5個negative words的結點對應的權重,共計6個輸出神經元,相當於每次只更新 300 x 6 = 1800 個權重。對於3百萬的權重來說,相當於只計算了0.06%的權重,這樣計算效率就大幅度提高。
我們使用「一元模型分布(unigram distribution)」來選擇「negative words」。
要注意的一點是,一個單詞被選作negative sample的概率跟它出現的頻次有關,出現頻次越高的單詞越容易被選作negative words。
在word2vec的C語言實現中,你可以看到對於這個概率的實現公式。每個單詞被選為「negative words」的概率計算公式與其出現的頻次有關。
代碼中的公式實現如下:
每個單詞被賦予一個權重,即 f(ωi), 它代表著單詞出現的頻次。
公式中開3/4的根號完全是基於經驗的,論文中提到這個公式的效果要比其它公式更加出色。你可以在google的搜索欄中輸入「plot y = x^(3/4) and y = x」,然後看到這兩幅圖(如下圖),仔細觀察x在[0,1]區間內時y的取值,x^(3/4) 有一小段弧形,取值在 y = x 函數之上。
負採樣的C語言實現非常的有趣。unigram table有一個包含了一億個元素的數組,這個數組是由詞彙表中每個單詞的索引號填充的,並且這個數組中有重複,也就是說有些單詞會出現多次。那麼每個單詞的索引在這個數組中出現的次數該如何決定呢,有公式,也就是說計算出的負採樣概率*1億=單詞在表中出現的次數。
有了這張表以後,每次去我們進行負採樣時,只需要在0-1億範圍內生成一個隨機數,然後選擇表中索引號為這個隨機數的那個單詞作為我們的negative word即可。一個單詞的負採樣概率越大,那麼它在這個表中出現的次數就越多,它被選中的概率就越大。
到目前為止,Word2Vec中的Skip-Gram模型就講完了,對於裡面具體的數學公式推導細節這裡並沒有深入。這篇文章只是對於實現細節上的一些思想進行了闡述。
如果想了解更多的實現細節,可以去查看C語言的實現源碼:
http://t.cn/R6w6Vi7
其他Word2Vec教程請參考:
http://t.cn/R6w6ViZ
下一部分將會介紹如何用 TensorFlow 實現一個 Word2Vec 中的 Skip-Gram 模型。
雷鋒網(公眾號:雷鋒網)相關閱讀:
一文詳解 Word2vec 之 Skip-Gram 模型(結構篇)
一文詳解 Word2vec 之 Skip-Gram 模型(實現篇)
25 行 Python 代碼實現人臉檢測——OpenCV 技術教程
雷鋒網版權文章,未經授權禁止轉載。詳情見轉載須知。