Google 開源項目 word2vec 的分析?

看了一下最近很火的Google的開源項目word2vec的論文和源碼,感覺上還是不求甚解,不讓知道有哪位大神看懂了整個流程框架可以分享一些經驗和心得?


同學可以移步到這位同學的blog去看更細緻的分析

深度學習word2vec筆記之基礎篇


這個答案是兩年前寫的,當時只讀了下代碼沒有深入的去使用和研究,有些地方理解的還不深,寫的很不好。當時自己主要是搞不明白h-softmax,所以大部分時間在想如何解釋h-softmax,但現在看來,許多同學還不明白這個模型本身,我這個note里連模型的優化目標都沒貼。


最近我又重新回顧了下word representation的文獻,等有時間,會寫個note(放在這了writing/word_representation_note_en.pdf at master · placebokkk/writing · GitHub),但不會細講word2vec, 這個東西已經被講爛了,代碼也在那,很好讀。這裡稍微再說下word2vec里的SGNS(Skip-gram negative sampling):

1.一個詞word會有兩個向量,一般是用做上下文時的C,一個使用做次本身表示的W。

2.SG的優化目標很簡單,參考mikolov論文中的公式(1)(2).不需要跟神經網路扯上關係,直接優化這個目標,梯度下降。

3.mikolov給的圖跟nn的慣用法不太一致,所以每次看我都看不懂。如果要把公式(1)(2)用nn來表示,Skip gram是這樣的:

  • 輸入層, one hot表示,當前詞是1,其他是0
  • 輸入層到隱層的連接矩陣,即前面說的W
  • 隱層的激活函數很特殊,是f(x) =x. 所以隱層的輸出值其實就是當前詞的表示Wi
  • 隱層到輸出層的鏈接矩陣,即前面說的C
  • 輸出層是個softmax

如果用neg sampling,相當於把輸出層改為含V個節點(當前的上下文詞的label是1,其他是0),激活函數變為sigmoid,但只選部分的負樣本(即公式(4))。這樣不能保證輸出是歸一化的,但是因為目的不是train LM,所以不必要求輸出歸一化。


原答案最後隨口說了句 "作者的還用到了一些其他的trick,比如每個句子都做採樣,根據詞頻隨機刪掉一些單詞,上下文窗口的大小是隨機的。" 今年的ACL上,levy的一篇論文說,SGNS之所以比PPMI-SVD效果好,是因為這些"trick"(客氣的稱為hyper-parameter),如果給PPMI引入這些,基於count matrix和SGNS效果都差不多。

levy的oral里最後講到:

如果你想發paper,好好tune hyper-parameter

如果你想做研究,好好tune你的baseline的hyper-parameter

原話忘了,大概是這個意思。


-----------------------------------------------------------------------原文----------------------------------------------------------

我之前寫的一篇筆記:

Word2Vec的一些理解

最近幾位google的研究人員發布了一個工具包叫word2vec,利用神經網路為單詞尋找一個連續向量空間中的表示。這裡整理一下思路,供有興趣的同學參考。

這裡先回顧一下大家比較熟悉的N-gram語言模型。

在自然語言任務里我們經常要計算一句話的概率。比如語音識別中對於一個語音段O,需要找到一個使P(W|O)最大的文本段W。利用貝葉斯公式:

P(W|O) = P(O|W)*P(W)/P(O)

其中 P(O|W)是用hmm-gmm或者hmm-dnn建模的聲學模型,而P(W)稱為語言模型。

P(W)可以分解如下:

Pleft( w_{1}^{T}  
ight)  =prod_{t=1}^{T} Pleft( w_{t} | w_{1}^{t-1}  
ight)

其中:

w_{i}^{j}  = left( w_{i} ,w_{i+1} ,cdot cdot cdot  w_{j-1} ,w_{j} 
ight)

為了簡化問題,可以做一個n-1階Markov假設,即每個單詞只和其前n-1個單詞相關,則:

Pleft( w_{t} |  w_{1}^{t-1}  
ight)  approx Pleft( w_{t} | w_{t-n+1}^{t-1}  
ight)

這樣,只要算出這些條件概率,任意的句子的概率我們都可以計算,而這些條件概率可以通過對語料建立一個多項分布模型用最大似然法求得。

這種方法我們稱為N-gram語言模型。

但是N-gram存在一個問題,若訓練語料裡面有些n元組沒有出現過,其對應的條件概率就是0,這會導致計算一整句話的概率為0。

解決這個問題有兩種常用方法:


一種是平滑法,最簡單的是將出現k次的某個n元組看做出現了k+1次,這樣出現0次的n元組就變成了出現1次。或者用更複雜的其他平滑方法。其本質都是將模型編程貝葉斯模型,為參數引入了先驗分布,從而解決最大似然法的問題。

另一種是回退法,即如果n元的概率不到,那就用n-1元的概率乘上一個權重來模擬。


一.神經網路語言模型

在Bengio03的文章中,他們提到除了上面說的問題之外,n-gram還存在其他問題:

1.n-gram語言模型無法建模更遠的關係,語料的不足使得無法訓練更高階的語言模型。(這篇文章發表時,基本都是trigram,還沒有高階的模型,不過幾年後,互聯網的海量數據使得可以訓練10幾階的語言模型)

2.這種模型無法建模出詞之間的相似度,有時候兩個具有某種相似性的詞,如果一個詞經常出現在某段詞之後,那麼也許另一個詞出現在這段詞後面的概率也比較大,比如


The cat is walking in the bedroom

A dog was running in a room


如果第一句話里的元組在語料中出現的很多,訓練的很充分,第二句話中的元組在語料中出現的少,訓練的不充分,那麼使用語言模型計算第一句話的概率就比較高,而第二句話的概率就低。

如果有一種方法,能知道The和a相似,cat和dog相似等等,並且會給相似的詞類似的語言模型概率,那麼第二句話也可以得到高概率。


另一種模型

既然我們想要的就是P(wt|Wp),那麼可以用一個神經網路直接去建模這個概率,網路的輸入是前N-1單詞,輸出是V個節點,其中第i個節點的輸出值就是P(wi|Wp).本文用Wp表示wt前面的N-1的個詞.

具體的模型如下:


1.每個單詞i表示成一個向量Ci

2.把單詞的向量表示Ci排列成一個更大的向量作為神經網路輸入,輸出是第n個單詞。

3.訓練出C和神經網路的參數

如果不考慮虛線(輸入跳過隱層直接到輸出的連接),那麼紅框內是個標準的三層前向神經網路,輸入層是單詞的向量表示,隱層使用tanh激活函數,輸出層用softmax做概率歸一化。

這個模型也可以看做是4層的網路,輸入層每個節點到層C之間的權重就是這個節點對應的單詞i的向量表示Ci。

對於每個樣本,輸入是Wp,輸出是一個1 of V的向量,即V個輸出節點,只有wt對應的那個節點為1,其他都為0.

使用BP演算法訓練這個網路即可得到所有的參數值。

雖然這篇文章的方法是為了計算語言模型,但也同時獲得一種單詞在向量空間上的表示。而這個副作用才是google的word2vec的真正目標。


二.層次soft-max

雖然基於神經網路的語言模型在效果上表現的很不錯,但是其訓練和預測的時間較長,影響實際的應用。

這個模型的輸出節點個數是V,在訓練時,對於一個樣本(Wp,wt),我們的目標是最大化P(wt|Wp),模型的輸出是V個沒有歸一化的值,所以需要對其做soft-max,得到P(wt|Wp).

這就需要前向時計算所有節點的輸出,並且要更新隱層到所有輸出節點的權重。隱層個數是H,則這一計算量是正比於H*V的。

可不可以每次只對到標註是1的節點的權重進行更新,而不管那些標註是0的節點呢,這樣每次訓練時計算量不就是正比於H了嗎?這是可以的,只是訓練會起來很慢。

為什麼呢,用一種直觀但不嚴謹的說法來解釋:

「只對到輸出是1的節點的權重進行更新,而不管那些輸出是0的節點」這種做法,是讓模型在每次迭代時只增加我看到的東西的概率,並沒有降低我沒看到東西的概率(這裡說的概率是非歸一化概率,即沒做soft-max之前輸出節點的輸出值,按照歸一化概率而言,只要增加了看到東西的概率,就必然會降低沒看到的東西的概率)。而如果讓模型在每次迭代都能在增加我看到的東西的概率的同時,降低我沒看到東西的概率,那麼模型會更快的收斂,所以訓練時隱層到輸出是0的節點間的權重也要做更新。

這裡提前說一下,在word2vec裡面,除了h soft-max,還有另一種方法叫negative sampling,就是這種思路,他每次並不對所有的標註是0的節點的權重進行更新,而隨機選取一些標註是0的節點更新權重,隨機選取的量是n,這樣每次就把H*V降到H*n。

現在回到上面的問題,我希望增大的不是標註為1的節點的輸出概率,而是該輸出在和其他節點一同進行歸一化後的概率,是否有什麼可以減少計算量的方法呢?

我們可以考慮這麼一個問題,如果我們有100個數組成的分布,但我們只有他們的非歸一化概率,這樣想計算出某個數的歸一概率,這樣我們需要計算100次(把他們加起來)。而如果我們把他們分為10類,每類10個數。如果我們知道這10個類的非歸一化的概率,以及每類的10個數的非歸一化概率,那麼我們計算某個數的歸一化概率就只要10+10次,這就提高了5倍的速度。

更極致的方法是分更多的層,每次用2分法,這樣可以把效率提高的極致。這可以等價於對輸出節點進行某種2進位的編碼,第i位表示這個詞在第i個層次上面的分類。

這樣模型的輸出層就如下圖所示:

同學可能會有疑問:這樣做和每次只對輸出是1的節點更新有什麼區別呢?後者只需要H的複雜度,這樣還需要H*Log2(V)呢?

這是有區別的,但這裡需要每一層分類的法則具有一定的意義,不能是隨機劃分。

那麼在訓練時,每次訓練,設樣本里的輸出單詞為wt,你增加了p(wt|Wp)的概率,本質上是增加了他在每個層次上的他屬於的類別的概率,由於在每個層次上每個類別節點都是2分的,那麼增加wt屬於的類別概率,就是降低wt不屬於的另一類別的概率。這樣那些V中和wt同類的節點概率被增加,不同類的節點概率被降低,如果類別是用真是意義的,那麼每次訓練,那些不同類的單詞就相當於負例了,而做n=0的negative sampling是沒有這個效果的,他只會增加wt的輸出概率。

另外在進行預測時,這種分層soft-max方法也可以降低時間複雜度,但前提是只預測一個P(wt|Wp)或者一部分(比如語音識別只要計算候選集里的即可),但如果想要預測此表中所有的V,那麼分層soft-max的複雜度和直接soft-max是一樣的.


三.Google的word2vec

有了上面的知識,word2Vec就很好理解,只是對上面的框架上做了些小改動。

其中的改動有:

1.映射層不再是將輸入單詞的向量表示按順序排列,而是將他們相加。減少計算量。

2.去掉了隱層,減少計算量,同時效果並不差。

3.由於這裡的目的是尋找單詞的向量表示,而不是語言模型,所以可以利用一些其他的判別準則,比如,兩個模型分別對應上圖的CBOW和Skip-gram.這樣真的是利用了上下文而不是上文了。

4.對於分層softmax,並沒有wordnet進行編碼,而是根據詞頻用huffman編碼。我們提到過,使用分層softmax,要求分類是有一定意義的,如果說用人類的先驗知識如wordnet或者某些無監督學習的方法去做分層,倒還有道理,但是huffman編碼只用到了詞頻特徵去分層,這樣的分層為什麼有效呢?作者說:

Ok,實踐是檢驗真理的唯一標準。

5.除了分層softmax,作者還使用了negative sampling的方法。


作者的還用到了一些其他的trick,比如每個句子都做採樣,根據詞頻隨機刪掉一些單詞,上下文窗口的大小是隨機的。

如果仍不理解,請閱讀論文和作者的源代碼。


理解原理細節,較好的中文資料是 word2vec 中的數學原理詳解


word2vec 中的數學原理詳解(多圖,WIFI下閱讀)先看這個,然後找一份熟悉語言的源碼讀,會理解比較深刻,比如python版本的gensim: models.word2vec


一句話,word2vec就是用一個一層的神經網路(CBOW的本質)把one-hot形式的詞向量映射到分散式形式的詞向量,為了加快訓練速度,用了Hierarchical softmax,negative sampling 等trick。

無痛理解word2vec - 上善若水的文章 - 知乎專欄


沒看具體實現,只是試用了一下,隨便說點兒。

這是一個典型的Distributional Semantic的問題,統計的是在給定語境下哪些詞出現的概率更大。
你的輸入是一個word,返回的結果是一個該詞可能出現語境中同時出現的詞的vector
本質上只要取一個適當大小的window當做『語境』,建立一個language model就可以了。
如果有伺服器跑得動,把Google的N-gram語料下下來自己也可以跑跑玩兒。。。


但是從我試用的結果看,word2vec這個項目並沒有考慮語法之類的東西。也就是說,一句話或者一篇文章是被看做一個sequence of words,詞語之間的關係只是位置/Sentence Boundary之類的關係。我同事所在組做的一個項目還加入了syntax feature,參照每個詞語在句子里的各種dependencies,給定一個輸入詞語,返回的結果是可能在相同語境里出現的詞語並且這些詞語和輸入的詞語擁有相似的syntax role。舉個例子,
輸入,HTC
word2vec可能會給你,Android,cellphone,Taiwan,Google,Nokia...
考慮語法之後可能會給你,Moto,Apple,Nokia,Samsung, Xiaomi...

用什麼方法來表示這個vector取決於應用。
Google的這個基本上就是Distributional Semantic最基本想法的一個最大規模的實現,用在計算語義相似度上面應該會不錯,其實可以適當進行一些預處理,應用一個lemmatizer會不會比較好。。。(同一個單詞的複數形式會出現在結果里我覺得挺奇怪的)


如果想嘗試一下word2vec的效果,可以用gensim這個library,用python寫的,很方便。
https://radimrehurek.com/gensim/index.html


最近研究了核心代碼 word2vec.c word2vec源碼解析之word2vec.c


word2vec 中的數學原理詳解,看了這份材料後,讓自己對word2vec的理解更清楚了


僅僅做一個資源的搬運工:
http://www-personal.umich.edu/~ronxin/pdf/w2vexp.pdf
http://arxiv.org/abs/1402.3722
感覺前者講得比較詳細,行文自然,後者就作為補充吧,前者重點說了CBOW和Skip-gram的參數和公式推導,和兩個非常重要的optimization,hierarchical softmax和Negative Sampling, 看完了再看看他人(比如最高票答者)關於這方面的理解,收穫頗豐。


有個問題,求解答。開源word2vec,gensim,tensorflow這幾種實現方式,效率和性能上大家怎麼看?


推薦閱讀:

如何評價Google 的 Project Tango ?
目前 Google 的創新精神是否超過了蘋果?
假如 Android 突然不開源了,整個智能設備格局會發生什麼變化?
搜索引擎的價值確是在降低嗎?
為什麼有人說「谷歌,人類的希望!」?

TAG:機器學習 | 自然語言處理 | 谷歌 (Google) | 開源項目 | word2vec |