深度學習word2vec筆記之演算法篇

garfielder007發表於2016-05-08

宣告:

1)該博文是Google專家以及多位博主所無私奉獻的論文資料整理的。具體引用的資料請看參考文獻。具體的版本宣告也參考原文獻

2)本文僅供學術交流,非商用。所以每一部分具體的參考資料並沒有詳細對應,更有些部分本來就是直接從其他部落格複製過來的。如果某部分不小心侵犯了大家的利益,還望海涵,並聯系老衲刪除或修改,直到相關人士滿意為止。

3)本人才疏學淺,整理總結的時候難免出錯,還望各位前輩不吝指正,謝謝。

4)閱讀本文需要機器學習、概率統計演算法等等基礎(如果沒有也沒關係了,沒有就看看,當做跟同學們吹牛的本錢),基礎篇url:http://blog.csdn.net/mytestmy/article/details/26961315

5)此屬於第一版本,若有錯誤,還需繼續修正與增刪。還望大家多多指點。請直接回帖,本人來想辦法處理。

6)word版的和pdf版的文件已上傳到csdn,下載url:http://download.csdn.net/detail/mytestmy/8565955,或者http://download.csdn.net/detail/mytestmy/8565959,資源分1分,評論後據說可以返還的,就有勞各位幫忙攢點分吧。如果有必要可以回覆或者發郵件到郵箱:beiliude@163.com


前言

在看word2vec的資料的時候,經常會被叫去看那幾篇論文,而那幾篇論文也沒有系統地說明word2vec的具體原理和演算法,所以老衲就斗膽整理了一個筆記,希望能幫助各位儘快理解word2vec的基本原理,避免浪費時間。

當然如果已經瞭解了,就隨便看看得了。


一. CBOW加層次的網路結構與使用說明

Word2vec總共有兩種型別,每種型別有兩個策略,總共4種。這裡先說最常用的一種。這種的網路結構如下圖。

其中第一層,也就是最上面的那一層可以稱為輸入層。輸入的是若干個詞的詞向量(詞向量的意思就是把一個詞表示成一個向量的形式表達,後面會介紹)。中間那個層可以成為隱層,是輸入的若干個詞向量的累加和,注意是向量的累加和,結果是一個向量。
第三層是方框裡面的那個二叉樹,可以稱之為輸出層,隱層的那個節點要跟輸出層的那個二叉樹的所有非葉節點連結的,線太多畫不過來了。第三層的這個二叉樹是一個霍夫曼樹,每個非葉節點也是一個向量,但是這個向量不代表某個詞,代表某一類別的詞;每個葉子節點代表一個詞向量,為了簡單隻用一個w表示,沒有下標。另外要注意的是,輸入的幾個詞向量其實跟這個霍夫曼樹中的某幾個葉子節點是一樣的,當然輸入的那幾個詞跟它們最終輸出的到的那個詞未必是同一個詞,而且基本不會是同一個詞,只是這幾個詞跟輸出的那個詞往往有語義上的關係。
還有要注意的是,這個霍夫曼樹的所有葉子節點就代表了語料庫裡面的所有詞,而且是每個葉子節點對應一個詞,不重複。
這個網路結構的功能是為了完成一個的事情——判斷一句話是否是自然語言。怎麼判斷呢?使用的是概率,就是計算一下這句話的“一列詞的組合”的概率的連乘(聯合概率)是多少,如果比較低,那麼就可以認為不是一句自然語言,如果概率高,就是一句正常的話。這個其實也是語言模型的目標。前面說的“一列詞的組合”其實包括了一個詞跟它的上下文的聯合起來的概率,一種普通的情況就是每一個詞跟它前面所有的詞的組合的概率的連乘,這個後面介紹。
對於上面的那個網路結構來說,網路訓練完成後,假如給定一句話s,這句話由詞w1,w2,w3,…,wT組成,就可以利用計算這句話是自然語言的概率了,計算的公式是下面的公式

其中的Context表示的是該詞的上下文,也就是這個詞的前面和後面各若干個詞,這個“若干”(後面簡稱c)一般是隨機的,也就是一般會從1到5之間的一個隨機數;每個p({w_i}|Contex{t_i})代表的意義是前後的c個詞分別是那幾個的情況下,出現該詞的概率。舉個例子就是:“大家 喜歡 吃 好吃 的 蘋果”這句話總共6個詞,假設對“吃”這個詞來說c隨機抽到2,則“吃”這個詞的context是“大家”、“喜歡”、“好吃”和“的”,總共四個詞,這四個詞的順序可以亂,這是word2vec的一個特點。
計算p({w_i}|Contex{t_i})的時候都要用到上面的那個網路,具體計算的方法用例子說明,假設就是計算“吃”這個詞的在“大家”、“喜歡”、“好吃”和“的”這四個詞作為上下文的條件概率,又假設“吃”這個詞在霍夫曼樹中是的最右邊那一個葉子節點,那麼從根節點到到達它就有兩個非葉節點,根節點對應的詞向量命名為A,根節點的右孩子節點對應的詞向量命名為B,另外再假設“大家”、“喜歡”、“好吃”和“的”這四個詞的詞向量的和為C,則

其中{\rm{\sigma }}\left( {\rm{x}} \right) = 1/\left( {1 + {e^{ - x}}} \right),是sigmoid公式。為什麼這麼算,看完後面就明白了,這裡僅僅說個流程,讓後面的描述流暢起來。要注意的是,如果“吃”這個詞在非葉節點B的左孩子節點(假設稱為E)的右邊的那個葉子節點,也就是在圖中右邊的三個葉子的中間那個,則有

上面的那句話的每個詞都計算p({w_i}|Contex{t_i})後連乘起來得到聯合概率,這個概率如果大於某個閾值,就認為是正常的話;否則就認為不是自然語言,要排除掉。
對於這個神經網路的描述索然無味,因為主角也不是這個概率,這個神經網路最重要的是輸出層的那個霍夫曼樹的葉子節點上的那些向量,那些向量被稱為詞向量,詞向量就是另外一篇博文裡面介紹的,是個好東西。

怎麼得到這些詞向量更加是一個重要的過程,也是word2vec這整個演算法最重要的東西,後面會認真介紹。

至於霍夫曼樹,其實是一個優化的解法,後面再提。


二.優化目標與解問題

2.1從霍夫曼樹到條件概率的計算

前面已經提過語言模型的目標就是判斷一句話是否是正常的,至於怎麼判斷則需要計算很多條件概率如p({w_i}|Contex{t_i}),然後還要把這些條件概率連乘起來得到聯合概率。這樣就帶來了問題了——怎麼去計算p({w_i}|Contex{t_i}),有很多辦法的,後面的章節會介紹。這裡的word2vec的計算這個條件概率的方法是利用神經網路的能量函式,因為在能量模型中,能量函式的功能是把神經網路的狀態轉化為概率表示,這在另外一篇博文RBM裡面有提到,具體要看hinton的論文來了解了。能量模型有個特別大的好處,就是能擬合所有的指數族的分佈。那麼,如果認為這些條件概率是符合某個指數族的分佈的話,是可以用能量模型去擬合的。總之word2vec就認為p({w_i}|Contex{t_i})這個條件概率可以用能量模型來表示了。
既然是能量模型,那麼就需要能量函式,word2vec定義了一個非常簡單的能量函式
E(A,C)=-(A∙C)
其中A可以認為是某個詞的詞向量,C是這個詞的上下文的詞向量的和(向量的和),基本上就可以認為C代表Context;中間的點號表示兩個向量的內積。
然後根據能量模型(這個模型假設了溫度一直是1,所以能量函式沒有分母了),就可以表示出詞A的在上下文詞向量C下的概率來了
                 (2.1.2)
其中V表示語料庫裡面的的詞的個數,這個定義的意思是在上下文C出現的情況下,中間這個詞是A的概率,為了計算這個概率,肯定得把語料庫裡面所有的詞的能量都算一次,然後再根據詞A的能量,那個比值就是出現A的概率。這種計算概率的方式倒是能量模型裡面特有的,這個定義在論文《Hierarchical Probabilistic Neural Network Language Model》裡面,這裡拿來改了個形式。
這個概率其實並不好統計,為了算一個詞的的概率,得算上這種上下文的情況下所有詞的能量,然後還計算指數值再加和。注意那個分母,對語料庫裡面的每個詞,分母都要算上能量函式,而且再加和,假如有V個詞彙,整個語料庫有W個詞,那麼一輪迭代中光計算分母就有W*V*D個乘法,如果詞向量維度是D的話。比如,語料庫有100000000個詞,詞彙量是10000,計算100維的詞向量,一輪迭代要〖10〗^14次乘法,計算機計算能力一般是〖10〗^9每秒,然後一輪迭代就要跑100000秒,大約27小時,一天多吧。1000輪迭代就三年了。
這時候科學家們的作用又體現了,假如把語料庫的所有詞分成兩類,分別稱為G類和H類,每類一半,其中詞A屬於G類,那麼下面的式子就可以成立了
p(A│C)=p(A|G,C)p(G|C)                        (2.1.3)
這個式子的的含義算明確的了,詞A在上下文C的條件下出現的概率,與後面的這個概率相等——在上下文C的條件下出現了G類詞,同時在上下文為C,並且應該出現的詞是G類詞的條件下,詞A出現的概率。
列出這麼一個式子在論文《Hierarchical Probabilistic Neural Network Language Model》裡面也有個證明的,看原始的情況
P(Y=y│X=x)=P(Y=y|D=d(y),X)P(D=d(y)|X=x)

其中d是一個對映函式,把Y裡面的元素對映到詞的類別D裡面的元素。還有個證明


式子(2.1.3)說明了一個問題,計算一個詞A在上下文C的情況下出現的概率,可以先對語料庫中的詞分成兩簇,然後能節省計算。現在來展示一下怎麼節省計算,假設G,H這兩類的簇就用G和H表示(G和H也是一個詞向量,意思就是G表示了其中一簇的詞,H表示了另外一簇的詞,G和H只是一個代表,也不是什麼簇中心的說法。其實如果情況極端點,只有兩個詞,看下面的式子就完全沒問題了。在多個詞的情況下,就可以認為詞被分成了兩團,G和H個表示一團的詞,計算概率什麼的都以一整團為單位),那麼式子(2.3)中的p(G|C)可以用下面的式子計算

也就是說,可以不用關心這兩個簇用什麼表示,只要利用一個F=H-G的類詞向量的一個向量就可以計算P(G|C)了,所以這一步是很節省時間的。再看另外一步

由於在G內的詞數量只有V/2個,也就是說計算分母的時候只要計算V/2個詞的能量就可以了。這已經省了一半的計算量了,可惜科學家們是貪得無厭的,所以還要繼續省,怎麼來呢?把G類詞再分成兩個簇GG,GH,A在GH裡面,然後
p(A│G,C)=p(A|GH,G,C)p(GH|G,C)
同樣有



同樣可以把GG-GH用一個類詞向量表達,這時候
p(A│C)=p(A|GH,G,C)p(GH|G,C)p(G|C)
繼續下去假設繼續分到GHG簇的時候只剩兩個詞了,再分兩簇為GHGG和GHGH,其中的簇GHGG就只有一個詞A,那麼p(A│C)可以用下面的式子算
p(A│C)=p(A│GHGG,GHG,GH,G,C)p(GHGG|GHG,GH,G,C)p(GHG|GH,G,C)p(GH|G,C)p(G|C)
其中p(A|GHGG,GHG,GH,G)是1,因為只有一個單詞,代到公式(2.2)就可以得到,那麼就有
p(A│C)=p(GHGG|GHG,GH,G,C)p(GHG|GH,G,C)p(GH|G,C)p(G|C)
也就是

假設再令FFF=GHH-GHG,FF=GG-GH,F=H-G,那麼p(A|C)只要算這三個詞與上下文C的能量函式了,確實比原來的要節省很多計算的。
對於上面的霍夫曼樹來說假設G表示向右,H表示向左,那麼A就是從右邊開始數的第二個葉子節點,就是圖中右邊的三個W的中間那個。那麼F,FF,FFF就是這個葉子節點路徑上的三個非葉節點。
但是一個詞總是會一會向左,一會向右的,也就是在根節點那裡,一會是p(G|C)那麼F=H-G,一會又是p(H|C)那麼F=G-H,如果F在每個節點都是唯一一個值,就可以直接用一次詞向量表示這個非葉節點了。這下難不倒科學家的,令F一直是等於H-G,那麼一直有

並且有p(G|C)=1-p(H|C)。
這樣每個非葉節點就可以用唯一一個詞向量表示了。
看到這裡,總該明白為啥p(A|C)要這麼算了吧。再換種情況,上面的概率這個概率的計算方法是不是也是同樣的道理?
總結下來,p({w_i}|Contex{t_i})可以用下面的公式計算了

其中C表示上下文的詞向量累加後的向量,qk表示從根節點下來到葉子節點的路徑上的那些非葉節點,dk就是編碼了,也可以說是分類,因為在霍夫曼樹的每個非葉節點都只有兩個孩子節點,那可以認為當wi在這個節點的左子樹的葉子節點上時dk=0,否則dk=1。這樣的話每個詞都可以用一組霍夫曼編碼來表示,就有了上面的那個式子中間的那個dk,整個p(w│Context)就可以用霍夫曼樹上的若干個非葉節點和詞w的霍夫曼編碼來計算了。

看到這務必想明白,因為開始要討論怎麼訓練了。

2.1.1霍夫曼樹


上面輸出層的二叉樹是霍夫曼樹,其實並沒有要求是霍夫曼樹,隨便一個不太離譜的二叉樹都可以的,但是用霍夫曼樹能達到最優的計算效果。
根據之前的討論,已經知道了語料庫裡面每個詞都要從根節點下來,一直走到葉子節點,每經過一個非葉節點,就要計算一個sigmoid函式。
隨便亂分也能達到效果,但是資訊熵理論給出了最優的方案——霍夫曼樹。具體可以檢視其它資料。

2.2目標函式

假設語料庫是有S個句子組成的一個句子序列(順序不重要),整個語料庫有V個詞,似然函式就會構建成下面的樣子
                                                         (2.2.1)
其中T_j表示第j個句子的詞個數,極大似然要對整個語料庫去做的。對數似然就會是下面的樣子
                              (2.2.2)
如果前面有個1/V,對數似然還有些人稱為交叉熵,這個具體也不瞭解,就不介紹了;不用1/V的話,就是正常的極大似然的樣子。
有意向的同學可以擴充套件到有文件的樣子,這裡就不介紹了。
但是對於word2vec來說,上面的似然函式得改改,變成下面的樣子

其中的Cij表示上下文相加的那個詞向量。對數似然就是下面的

這裡就不要1/V了。
這個看起來應該比較熟悉了,很像二分類的概率輸出的邏輯迴歸——logistic regression模型。沒錯了,word2vec就是這麼考慮的,把在霍夫曼樹向左的情況,也就是dk=0的情況認為是正類,向右就認為是負類(這裡的正負類只表示兩種類別之一)。這樣每當出現了一個上下文C和一個詞在左子樹的情況,就認為得到了一個正類樣本,否則就是一個負類樣本,每個樣本的屬於正類的概率都可以用上面的引數算出來,就是\sigma \left( {{q_{{i_{jk}}}} \cdot Contex{t_{{i_j}}}} \right),如果是向右的話,就用1 - \sigma \left( {{q_{{i_{jk}}}} \cdot Contex{t_{{i_j}}}} \right)計算其概率。注意每個詞可以產生多個樣本,因為從霍夫曼樹的根節點開始,每個葉子節點都產生一個樣本,這個樣本的label(也就是屬於正類或者負類標誌)可以用霍夫曼編碼來產生,前面說過了,向左的霍夫曼編碼dk=0,所以很自然地可以用1-dk表示每個樣本label。
在這裡,霍夫曼編碼也變成了一個重要的東西了。
這樣就好多了,問題到這也該清楚了,上面那個l(θ)就是對數似然,然後負對數似然f=-l(θ)就是需要最小化的目標函式了。

2.3解法

解法選用的是SGD,博文《線上學習演算法FTRL》中說過SGD演算法的一些情況。具體說來就是對每一個樣本都進行迭代,但是每個樣本隻影響其相關的引數,跟它無關的引數不影響。對於上面來說,第j個樣本的第ij個詞的負對數似然是

第j個樣本的第ij個詞的在遇到第kij個非葉節點時的負對數似然是

計算{f_{{k_{ij}}}}的梯度,注意引數包括{q_{{k_{ij}}}}{C_{{i_j}}},其中{C_{{i_j}}}的梯度是用來計算{w_{{i_j}}}的時候用到。另外需要注意的是logσ(x)的梯度是1-σ(x),log(1-σ(x))的梯度是-σ(x),



上面的Fq和Fc只是簡寫,有了梯度就可以對每個引數進行迭代了

同時,每個詞的詞向量也可以進行迭代了

注意第二個迭代的wI是代表所有的輸入詞的,也就是假如輸入了4個詞,這四個詞都要根據這個方式進行迭代(注意是上下文的詞才是輸入詞,才根據梯度更新了,但是wij這個詞本身不更新的,就是輪到它自己在中間的時候就不更新了)。第二個迭代式確實不好理解,因為這裡的意思是所有非葉節點上的對上下文的梯度全部加和就得到了這個詞的上下文的梯度,看起來這個就是BP神經網路的誤差反向傳播。
論文《Hierarchical Probabilistic Neural Network Language Model》和《Three New Graphical Models for Statistical Language Modelling》中看起來也是這麼樣的解釋,人家都是Context的幾個詞首尾連線得到的一個向量,對這個長向量有一個梯度,或者一個超大的V*m矩陣(m是詞向量的維度),對這個矩陣每個元素有一個梯度,這些梯度自然也包括了輸入詞的梯度。
如果有人發現了這個做法的解釋請告知。

2.4程式碼中的trick

如前文,c表示左右各取多少個詞,程式碼中c是一個從0到window-1的一個數,是對每個詞都隨機生成的,而這個window就是使用者自己輸入的一個變數,預設值是5(這裡得換換概念了,程式碼中的c代表當前處理到了那個詞的下標,實際的隨機變數是b)。程式碼實際實現的時候是換了一種方法首先生成一個0到window-1的一個數b,然後訓練的詞(假設是第i個詞)的視窗是從第i-(window-b)個詞開始到第i+(window-b)個詞結束,中間用a != window這個判斷跳過了這個詞他自己,也就是更新詞向量的時候只更新上下文相關的幾個輸入詞,這個詞本身是不更新的。要注意的是每個詞的b都不一樣的,都是隨機生成的,這就意味著連視窗大小都是隨機的。
如果有人看過程式碼,就會發現,其中的q_(k_ij )在程式碼中用矩陣syn1表示,C_(i_j )在程式碼中用neu1表示。葉子節點裡面的每個詞向量在程式碼中用syn0表示,利用下標移動去讀取。
核心程式碼如下,其中的vocab[word].code[d]就表示d_(k_ij ),其他就是迭代過程,程式碼是寫得相當簡潔啊。

程式碼中的419行就是計算Cij,425-428行就是計算f,也就是σ(q_(k_ij )∙C_(i_j ) )的值,432行就是累積Cij的誤差,434就是更新q_(k_ij)^(n+1)。

注意上面,對每個輸入詞都進行了更新,更新的幅度就是432行中誤差累計的結果。

三. CBOW加抽樣的網路結構與使用說明

3.1網路結構與使用說明

網路結構如下

如(二)中,中間的那個隱層是把上下文累加起來的一個詞向量,然後有一個矩陣R,是在訓練過程中用到的臨時矩陣,這個矩陣連線隱層與所有輸出節點,但是這個矩陣在使用這個網路的時候不怎麼用得到,這裡也是沒弄清楚的一個地方。每個輸出節點代表一個詞向量。
同樣如(二)中的例子,計算這麼一個概率,這裡的計算方法就簡單多了,就是隨機從語料庫裡面抽取c個詞,這裡假設c=3,抽中了D,E,F這三個詞,又假設“吃”這個詞的詞向量是A,那麼就計算“吃”這個詞的概率就用下面的公式

同樣如(二)中,那句話的每個詞都計算p({w_i}|Contex{t_i})後連乘起來得到聯合概率,這個概率如果大於某個閾值,就認為是正常的話;否則就認為不是自然語言,要排除掉。
這裡只是說明這個網路是怎麼樣的例子,真正重要的始終是那些詞向量。

四.CBOW加抽樣的優化目標與解問題

4.1抽樣方法的意義與目標函式

為啥要抽樣呢?目的跟(二)中的霍夫曼樹其實是一樣的,都是為了節省計算量,這個計算量就是式(2.1.2)中計算 p(A|C)的概率,因為這個概率實在不好算。論文《Distributed Representations of Words and Phrases and their Compositionality》中提到有一個叫NCE的方法可以來代替上面的那個hierarchical softmax方法(就是使用霍夫曼樹的方法),但是由於word2vec只關心怎麼學到高質量的詞向量,所以就用了一種簡單的NCE方法,稱為NEG,方法的本質就是在第j個句子的第ij個詞wij處使用下面的式子代替


其中E下面的那個下標的意思是wk是符合某個分佈的,在這裡p_V (w)表示詞頻的分佈。
這個式子的第二項是求K個期望,這K個期望中的每一個期望,都是在該上下文的情況下不出現這個詞的期望。這裡出現一個特別大的偷懶,就是認為這個期望只要抽取一個樣本就能計算出來,當然,如果說遍歷完整個語料庫,其實還可以認為是抽取了多次實驗的,因為每次抽取詞的時候,是按照詞頻的分佈來抽樣的,如果抽樣的次數足夠多,在總體的結果上,這裡計算的期望還是接近這個期望的真實值的,這個可以參考博文中RBM中計算梯度的時候的那個蒙特卡洛抽樣來理解。
在這裡,從程式碼上體現來看,就只用一個樣本來估算這個期望的,所有式子被簡化成了下面的形式

用這個式子取代(2.2.2)中的logp\left( {{w_{{i_j}}}{\rm{|}}Contex{t_{{i_j}}}} \right),就能得到CBOW加抽樣的目標函式(去掉1/V的),這個目標函式也是極其像logistic regression的目標函式,其中wij是正類樣本,wk是負類樣本。
為了統一表示,正類樣本設定一個label為1,負類樣本設定label為0,每個樣本的負對數似然都變成下面的方式

4.2CBOW加抽樣方法的解法

解法還是用SGD,所以對一個詞wij來說,這個詞本身是一個正類樣本,同時對這個詞,還隨機抽取了k個負類樣本,那麼每個詞在訓練的時候都有k+1個樣本,所以要做k+1次SGD。
對於每個樣本求兩個梯度



兩個梯度都有這麼相似的形式實在太好了,然後開始迭代,程式碼裡面有個奇怪的地方,就是一開的網路結構中的那個V*m的矩陣,用來儲存的是每次抽中的負類樣本的詞向量,每一行代表一個詞向量,剛好是所有詞的詞向量。每次抽樣抽中一個詞後,拿去進行迭代的(就是計算梯度什麼的),就是這個矩陣中對應的那個詞向量,而且這個矩陣還參與更新,就是第一個梯度實際更新的是這個矩陣裡面的詞向量。但總的看來,這個矩陣在迭代計算完了就丟棄了,因為最終更新的詞向量,每次只有一個,就是公式一直出現的那個詞wij,最終輸出的,也是輸入裡面的詞向量(在圖中是最下面的輸出層的那些詞向量),當然這個矩陣可以認為是隱藏層跟輸出層的連線矩陣。

其中w代表每個樣本對應的詞向量,包括wij和抽中的詞向量,注意更新的是R這個連線矩陣。詞向量的更新公式如下

注意每個梯度的值的計算都是拿連線矩陣R中對應的那一行來算的,這樣看起來可以避免每次都更新輸出層的詞向量,可能是怕搞亂了吧。

4.3CBOW加抽樣方法程式碼中的trick

隨機數是自己產生的,程式碼裡面自己寫了隨機數程式。
每個詞都要執行negative次抽樣的,如果遇到了當前詞(就是label為1的詞)就提前退出抽樣,這個步驟就提前完成了,這個也是一個比較費解的trick。
其中前面說的R矩陣在程式碼中用syn1neg表示,C_(i_j )在程式碼中用neu1表示,同樣的,葉子節點裡面的每個詞向量在程式碼中用syn0表示,利用下標移動去讀取。

其中442-446就是抽樣的程式碼了,是作者自己寫的模組,然後label這個變數跟上文的label意思是一樣的,f就表示σ(w∙C_(i_j ) )的值,syn1neg就儲存了矩陣R每一行的值,neu1e還是累積誤差,直到一輪抽樣完了後再更新輸入層的詞向量。。
對輸入層的更新模組和上面的(二)中一樣的,都是更新所有的輸入詞。


五.Skip-gram加層次的優化目標與解問題

5.1網路結構與使用說明

網路結構如下圖

其中的Wi是相應的詞,詞Wi與huffman樹直接連線,沒有隱藏層的。使用方法依然與cbow加層次的相似。
在判斷“大家 喜歡 吃 好吃 的 蘋果”這句話是否自然語言的時候,是這麼來的,同樣比如計算到了“吃”這個詞,同樣隨機抽到的c=2,對吃這個詞需要計算的東西比較多,總共要計算的概率是p(大家|吃),p(喜歡|吃),p(好吃|吃)和p(的|吃)總共四個,在計算p(大家|吃)這個概率的時候,要用到上面圖中的二叉樹,假設“大家”這個詞在huffman樹根節點的右孩子的最左邊的那個節點,就是圖中右數第三個葉子節點。再假設從根節點開始到這個葉子節點的路徑上的三個非葉節點分別為A,B,C(從高到低排列的),“吃”這個詞的詞向量設為D,那麼p(大家|吃)這個概率可以用下面的公式算概率
p(大家│吃)=(1-σ(A∙D))∙σ(B∙D)∙σ(C∙D)
同樣的方法計算p(喜歡|吃),p(好吃|吃)和p(的|吃),再把這四個概率連乘,得到了“吃”這個詞的上下文概率,注意這只是一個詞的概率。
把一整句話的所有詞的概率都計算出來後進行連乘,得到的就是這句話是自然語言的概率。這個概率如果大於某個閾值,就認為是正常的話;否則就認為不是自然語言,要排除掉。
再宣告一下,這裡只是說明這個網路是怎麼樣的例子,真正重要的始終是那些詞向量。

5.2目標函式

假設語料庫是有S個句子組成的一個句子序列(順序不重要),整個語料庫有V個詞,似然函式就會構建成下面的樣子
       (5.2.1)

其中T_j表示第j個句子的詞個數,w_(u_ij+i_j )表示詞w_(i_j )左右的各c_ij個詞的其中一個,注意c_ij對每個w_(i_j )都不一樣的。極大似然要對整個語料庫去做的。

但是,Google這公司,為了程式碼風格的統一,用了一個trick,我舉例說明吧。
對於一開始的那句話:大家 喜歡 吃 好吃 的 蘋果,總共6個詞,假設每次的cij都抽到了2,按照上面的公式中的部分按每個詞作為條件w_(i_j )展開,看到得到什麼吧。
大家:P(喜歡|大家)*p(吃|大家)
喜歡:p(大家|喜歡)*p(吃|喜歡)*p(好吃|喜歡)
吃: p(大家|吃) *p(喜歡|吃)*p(好吃|吃)*p(的|吃)
好吃:p(喜歡|好吃)*p(吃|好吃)*p(的|好吃)*p(蘋果|好吃)
的: p(吃|的) *p(好吃|的)*p(蘋果|的)
蘋果:p(好吃|蘋果)*p(的|蘋果)
把結果重新組合一下,得到下面的組合方式。
大家:p(大家|喜歡)*p(大家|吃)
喜歡:P(喜歡|大家)*p(喜歡|吃)*p(喜歡|好吃)
吃: p(吃|大家) *p(吃|喜歡)*p(吃|好吃)*p(吃|的)
好吃:p(好吃|喜歡)*p(好吃|吃)*p(好吃|的)*p(好吃|蘋果)
的: p(的|吃) *p(的|好吃)*p(的|蘋果)
蘋果:p(蘋果|好吃)* p(蘋果|的)
不證明,不推導,直接得到下面的結論

這個是網易有道的那個資料給出的結論,這裡是變成了本文的方式來描述,具體參考網易有道的原文。
這個變化倒是莫名其妙的,不過也能理解,Google公司的程式碼是要求很優美的,這樣做能把程式碼極大地統一起來。
對數似然就會是下面的樣子

       (5.2.2)
其中的V表示整個語料庫的詞沒有去重的總個數,整個目標函式也可以叫交叉熵,但是這裡對這也不感興趣,一般去掉。
這就涉及到計算某個詞的概率了,如p({w_{{u_{ij}} + {i_j}}}|{w_{{i_j}}}),這個概率變了,條件變成輸入中要考察的那個詞,計算條件概率的詞變成了上下文的詞。當然,計算方法沒有變,前面介紹的huffman樹的計算方法到這裡是一摸一樣的。

其中I表示輸入的那個詞,也就是(5.1)的例子中的那個詞“吃”,那麼w就表示例子中的“大家”;qk表示從根節點下來到“大家”這個詞所在的葉子節點的路徑上的非葉節點,dk就是編碼了,也可以說是分類,當w在某個節點如qk的左子樹的葉子節點上時dk=0,否則dk=1。
用這個式子代替掉上面的(5.2.1)中的似然函式中的p({w_{{u_{ij}} + {i_j}}}|{w_{{i_j}}}),當然每個變數都對號入座,就能得到總的似然函式了。
再對這個式子求個對數,得到


再利用這個式子替換掉(5.2.2)中的{{\rm{log}}p({w_{{u_{ij}} + {i_j}}}|{w_{{i_j}}})}就能得到總的對數似然函式,也就是目標函式,剩下的就是怎麼解了。
可以注意的是,計算每個詞(例中的“吃”)上下文概率的時候都要計算好幾個條件概率的(例子中p(大家|吃),p(喜歡|吃),p(好吃|吃)和p(的|吃)),這每個條件概率又是需要在huffman樹上走好幾個非葉節點的,每走到一個非葉節點,都需要計算一個{\left( {1 - {d_k}} \right)log\sigma \left( {{q_k} \cdot {\rm{I}}} \right) + {d_k} \cdot \left( {1 - \sigma \left( {{q_k} \cdot I} \right)} \right)}的。可以看到,走到每一個非葉節點,在總的對數似然函式中,都類似logistic regression的一個樣本,為了方便描述,就用樣本和label的方式在稱呼這些東西。
跟一般的logistic regression一樣,每走到一個非葉節點,如果是向左走的,就定義label為1,為正樣本;否則label就是0,是負樣本。這樣label=1-dk,每一個非葉節點都為整個問題產生了一個樣本。

5.3解法

解法選用的是SGD,在處理每個樣本時,對總目標函式的貢獻是

計算梯度



更新


5.4程式碼中的trick

對輸入詞I的更新是走完整個huffman樹後對整個誤差一起計算的,這個誤差儲存在neu1e這個陣列裡面。

其中480-483行是計算\sigma \left( {{q_k} \cdot I} \right)的值,儲存在f中,vocab[word].code[d]表示的就是dk的值,neu1e就儲存了從根節點到葉子節點的路徑上的所有非葉節點的累積誤差。

這個誤差反向傳播就簡單多了,每次都是對一個詞進行更新的,就是p(w|I)中的那個w。

六.Skip-gram加抽樣的優化目標與解問題

這個就簡單說說吧,不清楚的看上面的。

6.1網路結構與使用說明

網路結構如下

使用說明就不說的,同樣是抽樣的。
如(四)中,有一個矩陣R,是在訓練過程中用到的臨時矩陣,這個矩陣連線隱層與所有輸出節點,但是這個矩陣在使用這個網路的時候不怎麼用得到,這裡也是沒弄清楚的一個地方。每個輸出節點代表一個詞向量。
同樣如(五)中的例子,計算p(大家│吃)這麼一個概率,這裡的計算方法就簡單多了,就是隨機從語料庫裡面抽取c個詞,這裡假設c=3,抽中了D,E,F這三個詞,又假設“吃”這個詞的詞向量是A,那麼就計算“吃”這個詞的概率就用下面的公式

同樣如(五)中,那句話的每個詞都計算與上下文幾個詞的概率(p(大家|吃),p(喜歡|吃),p(好吃|吃)和p(的|吃))後連乘起來得到詞“吃”的概率,所有詞的概率計算出來再連乘就是整句話是自然語言的概率了。剩下的同上。

6.2目標函式與解法

似然函式跟(5.2.1)一樣的,對數似然函式跟(5.2.2)是一樣的,但是計算{\rm{logp}}({w_{{u_{ij}} + {i_j}}}|{w_{{i_j}}})也就是logp(w|I)的的時候不一樣,論文《Distributed Representations of Words and Phrases and their Compositionality》中認為logp(w|I)可以用下面的式子

代替。
同樣可以和(四)中一樣,設定正負樣本的概念。為了統一表示,正類樣本設定一個label為1,負類樣本設定label為0,每個樣本的負對數似然都變成下面的方式

梯度



更新



6.3程式碼

還是每個詞都要執行negative次抽樣的,如果遇到了當前詞(就是label為1的詞)就提前退出抽樣。

其中493-502就是抽樣的程式碼,505-508是計算σ(w∙I)的值,儲存在f中,syn1neg就是儲存了矩陣R中的每一行的值。而neu1e還是累積這誤差,直到一輪抽樣完了後再更新輸入層的詞向量。

更新輸入層還是一樣。

七.一些總結

從程式碼看來,word2vec的作者Mikolov是個比較實在的人,那種方法效果好就用哪種,也不糾結非常嚴格的理論證明,程式碼中的trick也是很實用的,可以參考到其他地方使用。


致謝

多位Google公司的研究員無私公開的資料。
多位博主的部落格資料,包括@peghoty,就是deeplearning學習群裡面的皮果提。


參考文獻

[1] http://techblog.youdao.com/?p=915      Deep Learning實戰之word2vec,網易有道的pdf
[2] http://www.zhihu.com/question/21661274/answer/19331979                @楊超在知乎上的問答《Word2Vec的一些理解》
[3] http://xiaoquanzi.net/?p=156                  hisen部落格的博文
[4] Hierarchical probabilistic neural network language model. Frederic Morin and Yoshua Bengio.
[5] Distributed Representations of Words and Phrases and their Compositionality T. Mikolov, I. Sutskever, K. Chen, G. Corrado, and J. Dean.
[6] A neural probabilistic language model Y. Bengio, R. Ducharme, P. Vincent.
[7] Linguistic Regularities in Continuous Space Word Representations. Tomas Mikolov,Wen-tau Yih,Geoffrey Zweig

[8] Efficient Estimation of Word Representations in Vector Space. Tomas Mikolov,Kai Chen,Greg Corrado,Jeffrey Dean.

from: http://blog.csdn.net/mytestmy/article/details/26969149

相關文章