深度學習word2vec筆記之算法篇


(轉)深度學習word2vec筆記之算法篇

聲明:

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

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

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

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

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

6)本人手上有word版的和pdf版的,有必要的話可以上傳到csdn供各位下載

 

 

前言

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

 

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

 

 

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

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

其中第一層,也就是最上面的那一層可以稱為輸入層。輸入的是若干個詞的詞向量(詞向量的意思就是把一個詞表示成一個向量的形式表達,后面會介紹)。中間那個層可以成為隱層,是輸入的若干個詞向量的累加和,注意是向量的累加和,結果是一個向量。
第三層是方框里面的那個二叉樹,可以稱之為輸出層,隱層的那個節點要跟輸出層的那個二叉樹的所有非葉節點鏈接的,線太多畫不過來了。第三層的這個二叉樹是一個霍夫曼樹,每個非葉節點也是一個向量,但是這個向量不代表某個詞,代表某一類別的詞;每個葉子節點代表一個詞向量,為了簡單只用一個w表示,沒有下標。另外要注意的是,輸入的幾個詞向量其實跟這個霍夫曼樹中的某幾個葉子節點是一樣的,當然輸入的那幾個詞跟它們最終輸出的到的那個詞未必是同一個詞,而且基本不會是同一個詞,只是這幾個詞跟輸出的那個詞往往有語義上的關系。
還有要注意的是,這個霍夫曼樹的所有葉子節點就代表了語料庫里面的所有詞,而且是每個葉子節點對應一個詞,不重復。
這個網絡結構的功能是為了完成一個的事情——判斷一句話是否是自然語言。怎么判斷呢?使用的是概率,就是計算一下這句話的“一列詞的組合”的概率的連乘(聯合概率)是多少,如果比較低,那么就可以認為不是一句自然語言,如果概率高,就是一句正常的話。這個其實也是語言模型的目標。前面說的“一列詞的組合”其實包括了一個詞跟它的上下文的聯合起來的概率,一種普通的情況就是每一個詞跟它前面所有的詞的組合的概率的連乘,這個后面介紹。
對於上面的那個網絡結構來說,網絡訓練完成后,假如給定一句話s,這句話由詞w1,w2,w3,…,wT組成,就可以利用計算這句話是自然語言的概率了,計算的公式是下面的公式
{\rm{p}}\left( {\rm{s}} \right) = {\rm{p}}\left( {{w_1},{w_2}, \cdots {w_T}} \right) =  \prod \limits_{i = 1}^T p({w_i}|Contex{t_i})
其中的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下的概率來了
{\rm{p}}\left( {{\rm{A|C}}} \right) = \frac{{{e^{ - E\left( {A,C} \right)}}}}{{ \sum \nolimits_{v = 1}^V {e^{ - E\left( {{w_v},C} \right)}}}}                 (2.1.2)
其中V表示語料庫里面的的詞的個數,這個定義的意思是在上下文C出現的情況下,中間這個詞是A的概率,為了計算這個概率,肯定得把語料庫里面所有的詞的能量都算一次,然后再根據詞A的能量,那個比值就是出現A的概率。這種計算概率的方式倒是能量模型里面特有的,這個定義在論文《Hierarchical Probabilistic Neural Network Language Model》里面,這里拿來改了個形式。
這個概率其實並不好統計,為了算一個詞的的概率,得算上這種上下文的情況下所有詞的能量,然后還計算指數值再加和。
這時候科學家們的作用又體現了,假如把語料庫的所有詞分成兩類,分別稱為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里面的元素。還有個證明
{\rm{P}}\left( {{\rm{Y|X}}} \right) =  \sum \limits_i P\left( {Y,D = {\rm{i|X}}} \right) =  \sum \limits_i P\left( {Y{\rm{|}}D = i,X} \right)P\left( {D = i{\rm{|}}X} \right) = P(Y|D = d\left( Y \right),X)P(D = d\left( Y \right)|X)
式子(2.1.3)說明了一個問題,計算一個詞A在上下文C的情況下出現的概率,可以先對語料庫中的詞分成兩簇,然后能節省計算。現在來展示一下怎么節省計算,假設G,H這兩類的簇中心也用G和H表示,那么式子(2.3)中的p(G|C)可以用下面的式子計算
{\rm{p}}\left( {{\rm{G|C}}} \right) = \frac{{{e^{ - E\left( {G,C} \right)}}}}{{{e^{ - E\left( {G,C} \right)}} + {e^{ - E\left( {H,C} \right)}}}} = \frac{1}{{1 + {e^{ - \left( { - \left( {H - G} \right) \cdot C} \right)}}}} = \frac{1}{{1 + {e^{ - E\left( {H - G,C} \right)}}}}
也就是說,可以不用關系那個是簇中心,只要利用一個F=H-G的類詞向量的一個向量就可以計算P(G|C)了,所以這一步是很節省時間的。再看另外一步
{\rm{p}}\left( {{\rm{A|G}},{\rm{C}}} \right) = \frac{{{e^{ - E\left( {A,C} \right)}}}}{{ \sum \nolimits_{W \in G} {e^{ - E\left( {W,C} \right)}}}}
由於在G內的詞數量只有V/2個,也就是說計算分母的時候只要計算V/2個詞的能量就可以了。這已經省了一半的計算量了,可惜科學家們是貪得無厭的,所以還要繼續省,怎么來呢?把G類詞再分成兩個簇GG,GH,A在GH里面,然后
p(A│G,C)=p(A|GH,G,C)p(GH|G,C)
同樣有
{\rm{p}}\left( {{\rm{GH|G}},{\rm{C}}} \right) = \frac{1}{{1 + {e^{ - E\left( {GG - GH,C} \right)}}}}

{\rm{p}}\left( {{\rm{A|GH}},{\rm{G}},{\rm{C}}} \right) = \frac{{{e^{ - E\left( {A,C} \right)}}}}{{ \sum \nolimits_{W \in GH} {e^{ - E\left( {W,C} \right)}}}}
同樣可以把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)
也就是
{\rm{p}}\left( {{\rm{A|C}}} \right) = \frac{1}{{1 + {e^{ - E\left( {GHH - GHG,C} \right)}}}} \cdot \frac{1}{{1 + {e^{ - E\left( {GG - GH,C} \right)}}}} \cdot \frac{1}{{1 + {e^{ - E\left( {H - G,C} \right)}}}}
假設再令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,那么一直有
{\rm{p}}\left( {{\rm{H|C}}} \right) = \frac{1}{{1 + {e^{ - E\left( {F,C} \right)}}}}
並且有p(G|C)=1-p(H|C)。
這樣每個非葉節點就可以用唯一一個詞向量表示了。
看到這里,總該明白為啥p(A|C)要這么算了吧。再換種情況,上面的概率這個概率的計算方法是不是也是同樣的道理?
總結下來,p({w_i}|Contex{t_i})可以用下面的公式計算了
{\rm{p}}\left( {{\rm{w|Context}}} \right) =  \prod \limits_{k = 1}^K p({d_k}|{q_k},C) =  \prod \limits_{k = 1}^K \left( {{{\left( {\sigma \left( {{q_k} \cdot C} \right)} \right)}^{1 - {d_k}}} \cdot {{\left( {1 - \sigma \left( {{q_k} \cdot C} \right)} \right)}^{{d_k}}}} \right)
其中C表示上下文的詞向量累加后的向量,qk表示從根節點下來到葉子節點的路徑上的那些非葉節點,dk就是編碼了,也可以說是分類,因為在霍夫曼樹的每個非葉節點都只有兩個孩子節點,那可以認為當wi在這個節點的左子樹的葉子節點上時dk=0,否則dk=1。這樣的話每個詞都可以用一組霍夫曼編碼來表示,就有了上面的那個式子中間的那個dk,整個p(w│Context)就可以用霍夫曼樹上的若干個非葉節點和詞w的霍夫曼編碼來計算了。
看到這務必想明白,因為開始要討論怎么訓練了。

 

 

2.2目標函數

假設語料庫是有S個句子組成的一個句子序列(順序不重要),整個語料庫有V個詞,似然函數就會構建成下面的樣子
{\rm{L}}\left( {\rm{\theta }} \right) =  \prod \limits_j^{\rm{S}} \left( { \prod \limits_{{i_j} = 1}^{{T_j}} p\left( {{w_{{i_j}}}{\rm{|}}Contex{t_{{i_j}}}} \right)} \right)                                                         (2.2.1)
其中T_j表示第j個句子的詞個數,極大似然要對整個語料庫去做的。對數似然就會是下面的樣子
{\rm{l}}\left( {\rm{\theta }} \right) = {\rm{logL}}\left( {\rm{\theta }} \right) = \frac{1}{V} \sum \limits_{j = 1}^{\rm{S}} \left( { \sum \limits_{{i_j} = 1}^{{T_j}} logp\left( {{w_{{i_j}}}{\rm{|}}Contex{t_{{i_j}}}} \right)} \right)                              (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}}}} =  - \left( {1 - {d_{{k_{ij}}}}} \right)log\sigma \left( {{q_{{k_{ij}}}} \cdot {C_{{i_j}}}} \right) - {d_{{k_{ij}}}}log\left( {1 - \sigma \left( {{q_{{k_{ij}}}} \cdot {C_{{i_j}}}} \right)} \right)
計算{f_{{k_{ij}}}}的梯度,注意參數包括{q_{{k_{ij}}}}{C_{{i_j}}},其中{C_{{i_j}}}的梯度是用來計算{w_{{i_j}}}的時候用到。另外需要注意的是logσ(x)的梯度是1-σ(x),log(1-σ(x))的梯度是-σ(x),
Fq\left( {{q_{{k_{ij}}}}} \right) = \frac{{\partial {f_{{k_{ij}}}}}}{{\partial {q_{{k_{ij}}}}}} =  - \left( {1 - {d_{{k_{ij}}}}} \right) \cdot \left( {1 - \sigma \left( {{q_{{k_{ij}}}} \cdot {C_{{i_j}}}} \right)} \right) \cdot {C_{{i_j}}} - {d_{{k_{ij}}}} \cdot \left( { - \sigma \left( {{q_{{k_{ij}}}} \cdot {C_{{i_j}}}} \right)} \right) \cdot {C_{{i_j}}} =  - \left( {1 - {d_{{k_{ij}}}} - \sigma \left( {{q_{{k_{ij}}}} \cdot {C_{{i_j}}}} \right)} \right) \cdot {C_{{i_j}}}

Fc\left( {{q_{{k_{ij}}}}} \right) = \frac{{\partial {f_{{k_{ij}}}}}}{{\partial {C_{{i_j}}}}} =  - \left( {1 - {d_{{k_{ij}}}}} \right) \cdot \left( {1 - \sigma \left( {{q_{{k_{ij}}}} \cdot {C_{{i_j}}}} \right)} \right) \cdot {q_{{k_{ij}}}} - {d_{{k_{ij}}}} \cdot \left( { - \sigma \left( {{q_{{k_{ij}}}} \cdot {C_{{i_j}}}} \right)} \right) \cdot {q_{{k_{ij}}}} =  - \left( {1 - {d_{{k_{ij}}}} - \sigma \left( {{q_{{k_{ij}}}} \cdot {C_{{i_j}}}} \right)} \right) \cdot {q_{{k_{ij}}}}
上面的Fq和Fc只是簡寫,有了梯度就可以對每個參數進行迭代了
q_{{k_{ij}}}^{n + 1} = q_{{k_{ij}}}^n - \eta Fq\left( {q_{{k_{ij}}}^n} \right)
同時,每個詞的詞向量也可以進行迭代了
{\rm{w}}_I^{n + 1} = {\rm{w}}_I^n - \eta  \sum \limits_{{k_{ij}} = 1}^{{K_{ij}}} Fc\left( {q_{{k_{ij}}}^n} \right)
注意第二個迭代的wI是代表所有的輸入詞的,也就是假如輸入了4個詞,這四個詞都要根據這個方式進行迭代。第二個迭代式確實不好理解,因為這里的意思所有非葉節點上的對上下文的梯度全部加和就得到了這個詞的上下文的梯度,看起來這個就是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。代碼實際實現的時候是換了一種方法首先生成一個0到window-1的一個數b,然后訓練的詞(假設是第i個詞)的窗口是從第i-window+b個詞開始到第i-window+b個詞結束。要注意的是每個詞的c都不一樣的,都是隨機生成的。
如果有人看過代碼,就會發現,其中的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處使用下面的式子代替logp\left( {{w_{{i_j}}}{\rm{|}}Contex{t_{{i_j}}}} \right)
{\rm{log\sigma }}\left( {{w_{{i_j}}} \cdot {C_{{i_j}}}} \right) +  \sum \limits_{k = 1}^K {E_{{w_k}\~{p_V}\left( w \right)}}{\rm{log}}\left( {1 - \sigma \left( {{w_k} \cdot {C_{{i_j}}}} \right)} \right)
其中E下面的那個下標的意思是wk是符合某個分布的,在這里p_V (w)表示詞頻的分布。
這個式子的第二項是求K個期望,這K個期望中的每一個期望,都是在該上下文的情況下不出現這個詞的期望。這里出現一個特別大的偷懶,就是認為這個期望只要抽取一個樣本就能計算出來,當然,如果說遍歷完整個語料庫,其實還可以認為是抽取了多次實驗的,因為每次抽取詞的時候,是按照詞頻的分布來抽樣的,如果抽樣的次數足夠多,在總體的結果上,這里計算的期望還是接近這個期望的真實值的,這個可以參考博文中RBM中計算梯度的時候的那個蒙特卡洛抽樣來理解。
在這里,從代碼上體現來看,就只用一個樣本來估算這個期望的,所有式子被簡化成了下面的形式
{\rm{log\sigma }}\left( {{w_{{i_j}}} \cdot {C_{{i_j}}}} \right) +  \sum \limits_{k = 1}^K {\rm{log}}\left( {1 - \sigma \left( {{w_k} \cdot {C_{{i_j}}}} \right)} \right)
用這個式子取代(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,每個樣本的負對數似然都變成下面的方式
{f_w} =  - {\rm{label}} \cdot {\rm{log\sigma }}\left( {{\rm{w}} \cdot {C_{{i_j}}}} \right) - \left( {1 - {\rm{label}}} \right){\rm{log}}\left( {1 - \sigma \left( {w \cdot {C_{{i_j}}}} \right)} \right)

 

 

 

4.2CBOW加抽樣方法的解法

解法還是用SGD,所以對一個詞wij來說,這個詞本身是一個正類樣本,同時對這個詞,還隨機抽取了k個負類樣本,那么每個詞在訓練的時候都有k+1個樣本,所以要做k+1次SGD。
對於每個樣本求兩個梯度
{\rm{Fw}}\left( {\rm{w}} \right) = \frac{{\partial {f_w}}}{{\partial w}} =  - {\rm{label}} \cdot \left( {1 - {\rm{\sigma }}\left( {{\rm{w}} \cdot {C_{{i_j}}}} \right)} \right) \cdot {C_{{i_j}}} + \left( {1 - label} \right) \cdot {\rm{\sigma }}\left( {{\rm{w}} \cdot {C_{{i_j}}}} \right) \cdot {C_{{i_j}}} =  - \left( {label - {\rm{\sigma }}\left( {{\rm{w}} \cdot {C_{{i_j}}}} \right)} \right) \cdot {C_{{i_j}}}

{\rm{Fc}}\left( {\rm{w}} \right) = \frac{{\partial {f_w}}}{{\partial {C_{{i_j}}}}} =  - {\rm{label}} \cdot \left( {1 - {\rm{\sigma }}\left( {{\rm{w}} \cdot {C_{{i_j}}}} \right)} \right) \cdot w + \left( {1 - label} \right) \cdot {\rm{\sigma }}\left( {{\rm{w}} \cdot {C_{{i_j}}}} \right) \cdot w =  - \left( {label - {\rm{\sigma }}\left( {{\rm{w}} \cdot {C_{{i_j}}}} \right)} \right) \cdot w
兩個梯度都有這么相似的形式實在太好了,然后開始迭代,代碼里面有個奇怪的地方,就是一開的網絡結構中的那個V*m的矩陣,用來保存的是每次抽中的負類樣本的詞向量,每一行代表一個詞向量,剛好是所有詞的詞向量。每次抽樣抽中一個詞后,拿去進行迭代的(就是計算梯度什么的),就是這個矩陣中對應的那個詞向量,而且這個矩陣還參與更新,就是第一個梯度實際更新的是這個矩陣里面的詞向量。但總的看來,這個矩陣在迭代計算完了就丟棄了,因為最終更新的詞向量,每次只有一個,就是公式一直出現的那個詞wij,最終輸出的,也是輸入里面的詞向量(在圖中是最下面的輸出層的那些詞向量),當然這個矩陣可以認為是隱藏層跟輸出層的連接矩陣。
{\rm{R}}_w^{n + 1} = {\rm{R}}_w^n - \eta {\rm{Fw}}\left( {{\rm{R}}_w^n} \right)
其中w代表每個樣本對應的詞向量,包括wij和抽中的詞向量,注意更新的是R這個連接矩陣。詞向量的更新公式如下
{\rm{w}}_I^{n + 1} = {\rm{w}}_I^n - \eta \left( {Fc\left( {{\rm{R}}_{{w_I}}^n} \right) +  \sum \limits_{k = 1}^K Fc\left( {{\rm{R}}_{{w_k}}^n} \right)} \right)
注意每個梯度的值的計算都是拿連接矩陣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 )都不一樣的。極大似然要對整個語料庫去做的。對數似然就會是下面的樣子
      (5.2.2)
其中的V表示整個語料庫的詞沒有去重的總個數,整個目標函數也可以叫交叉熵,但是這里對這也不感興趣,一般去掉。
這就涉及到計算某個詞的概率了,如p({w_{{u_{ij}} + {i_j}}}|{w_{{i_j}}}),這個概率變了,條件變成輸入中要考察的那個詞,計算條件概率的詞變成了上下文的詞。當然,計算方法沒有變,前面介紹的huffman樹的計算方法到這里是一摸一樣的。
{\rm{p}}\left( {{\rm{w|I}}} \right) =  \prod \limits_{k = 1}^K p({d_k}|{q_k},{\rm{I}}) =  \prod \limits_{k = 1}^K \left( {{{\left( {\sigma \left( {{q_k} \cdot {\rm{I}}} \right)} \right)}^{1 - {d_k}}} \cdot {{\left( {1 - \sigma \left( {{q_k} \cdot I} \right)} \right)}^{{d_k}}}} \right)
其中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,在處理每個樣本時,對總目標函數的貢獻是
{\rm{lf}} = p\left( {{d_k}{\rm{|}}{q_k},{\rm{I}}} \right) =  - \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)
計算梯度
Fq\left( {{q_k}} \right) = \frac{{\partial l{\rm{f}}}}{{\partial {q_k}}} =  - \left( {1 - {d_k}} \right) \cdot \left( {1 - \sigma \left( {{q_k} \cdot I} \right)} \right) \cdot I - {d_k} \cdot \left( { - \sigma \left( {{q_k} \cdot I} \right)} \right) \cdot I =  - \left( {1 - {d_k} - \sigma \left( {{q_k} \cdot I} \right)} \right) \cdot I

Fi\left( {{q_k}} \right) = \frac{{\partial l{\rm{f}}}}{{\partial I}} =  - \left( {1 - {d_k}} \right) \cdot \left( {1 - \sigma \left( {{q_k} \cdot I} \right)} \right) \cdot {q_k} - {d_k} \cdot \left( { - \sigma \left( {{q_k} \cdot I} \right)} \right) \cdot {q_k} =  - \left( {1 - {d_k} - \sigma \left( {{q_k} \cdot I} \right)} \right) \cdot {q_k}
更新
q_k^{n + 1} = q_k^n - \eta {\rm{Fq}}\left( {q_k^n} \right)
^{n + 1} = {I^n} - \eta {\rm{Fi}}\left( {q_k^n} \right)

 

 

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)可以用下面的式子
{\rm{log\sigma }}\left( {w \cdot I} \right) +  \sum \limits_{k = 1}^K {E_{w\~{p_V}\left( w \right)}}\left[ {log\left( {1 - \sigma \left( {{w_k} \cdot {C_{{i_j}}}} \right)} \right)} \right]
代替。
同樣可以和(四)中一樣,設定正負樣本的概念。為了統一表示,正類樣本設置一個label為1,負類樣本設置label為0,每個樣本的負對數似然都變成下面的方式
{f_w} =  - {\rm{label}} \cdot {\rm{log\sigma }}\left( {{\rm{w}} \cdot I} \right) - \left( {1 - {\rm{label}}} \right){\rm{log}}\left( {1 - \sigma \left( {w \cdot I} \right)} \right)
梯度
{\rm{Fw}}\left( {\rm{w}} \right) = \frac{{\partial {f_w}}}{{\partial w}} =  - {\rm{label}} \cdot \left( {1 - {\rm{\sigma }}\left( {{\rm{w}} \cdot I} \right)} \right) \cdot {C_{{i_j}}} + \left( {1 - label} \right) \cdot {\rm{\sigma }}\left( {{\rm{w}} \cdot I} \right) \cdot I =  - \left( {label - {\rm{\sigma }}\left( {{\rm{w}} \cdot I} \right)} \right) \cdot I

{\rm{Fc}}\left( {\rm{w}} \right) = \frac{{\partial {f_w}}}{{\partial I}} =  - {\rm{label}} \cdot \left( {1 - {\rm{\sigma }}\left( {{\rm{w}} \cdot I} \right)} \right) \cdot w + \left( {1 - label} \right) \cdot {\rm{\sigma }}\left( {{\rm{w}} \cdot I} \right) \cdot w =  - \left( {label - {\rm{\sigma }}\left( {{\rm{w}} \cdot I} \right)} \right) \cdot w
更新
{\rm{R}}_w^{n + 1} = {\rm{R}}_w^n - \eta {\rm{Fw}}\left( {{\rm{R}}_w^n} \right)
{{\rm{I}}^{n + 1}} = {{\rm{I}}^n} - \eta \left( {Fc\left( {{\rm{R}}_{{{\rm{I}}^n}}^n} \right) +  \sum \limits_{k = 1}^K Fc\left( {{\rm{R}}_{{w_k}}^n} \right)} \right)

 

 

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.

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM