CS224N學習筆記(二)—— Word2Vec

一、如何表示一個詞語的意思

在計算機中如何表示一個詞的意思

過去幾個世紀里一直用的是分類詞典。計算語言學中常見的方式是WordNet那樣的詞庫。比如NLTK中可以通過WordNet查詢熊貓的上位詞就可以得到“食肉動物”“動物”之類的上位詞。也可以查詢“good”的同義詞——“just”(品格好)、“ripe”(熟了)。

panda的上位詞,來自于NLTK中wordnet接口的演示
good的同義詞集
語義詞典存在的問題
  • 這種discrete representation雖然是種語言學資源,但丟失了韻味。比如這些同義詞的意思實際上還是有微妙的差別:adept、expert、good、practiced、proficient、skillful

  • 會錯過一些新詞,幾乎不可能做到及時更新: wicked, badass, nifty, crack, ace, wizard, genius, ninjia

  • 有一定的主觀傾向

  • 需要大量的人力物力

  • 很難用來計算兩個詞語的相似度

One-hot Representation

無論是規則學派,還是統計學派,絕大多數NLP學家都將詞語作為最小單位。事實上,詞語只是詞表長度的one-hot向量,這是一種localist representation(大概是借用localist“局部”的意項)。

使用One-hot 表示,就會導致在不同的語料中,詞表大小不同。例如Google的1TB語料詞匯量是1300萬,做One-hot后的向量就太長了。

而且使用One-hot 表示會導致任意兩個詞之間都是孤立的,光從這兩個向量中看不出兩個詞是否有關系,比如king和queen。而one-hot向量是正交的,無法通過任何運算得到相似度。

One-hot后的兩個詞
Distributional similarity based representations

語言學家J. R. Firth提出,通過一個單詞的上下文可以得到它的意思。J. R. Firth甚至建議,如果你能把單詞放到正確的上下文中去,才說明你掌握了它的意義。

使用共現矩陣(Co-occurrence matrix)使用上下文來表示單詞。

基于窗口的共現矩陣的一個簡單例子:

  • 窗口長度是1(一般是5-10)
  • 對稱(左右內容無關)

語料樣例

  • I like deep learning.
  • I like NLP.
  • I enjoy flying
共現矩陣

現在,我們可以用矩陣的行向量來計算word之間的相似度了。

然而, 這種定義方式得到的詞向量的維度等于詞典的大小。這意味著,我們需要大量的空間來存儲這些高維的詞向量。同時,伴隨著高維向量出現的數據稀疏性問題,也使得基于這些詞向量的機器學習模型的訓練變得異常困難。

簡單來說,co-occurrence矩陣定義的詞向量在一定程度上緩解了one-hot向量相似度為0的問題,但沒有解決數據稀疏性和維度災難的問題。

解決方案:

1、SVD分解:低維詞向量的間接學習
既然基于co-occurrence矩陣得到的離散詞向量存在著高維和稀疏性的問題,一個自然而然的解決思路是對原始詞向量進行降維,從而得到一個稠密的連續詞向量。然而,高昂的計算復雜度(O(mn2))是SVD算法的性能瓶頸。這對于現在動輒上百萬甚至上億數據量的語料庫而言,是一筆巨大的計算開銷。更不用說每一個新詞的加入都需要在一個新的矩陣上重新運行一遍SVD分解。此外,后面我們會看到,SVD算法和其他Deep Learning模型本質上是屬于兩類不同的學習算法。

2、word2vec:低維詞向量的直接學習
word2vec模型背后的基本思想是對出現在上下文環境里的詞進行預測。對于每一條輸入文本,我們選取一個上下文窗口和一個中心詞,并基于這個中心詞去預測窗口里其他詞出現的概率。因此,word2vec模型可以方便地從新增語料中學習到新增詞的向量表達,是一種高效的在線學習算法(online learning)。

二、word2vec

word2vec的主要思路:通過單詞和上下文彼此預測

兩個算法:

  • Skip-grams (SG):預測上下文
  • Continuous Bag of Words (CBOW):預測目標單詞

兩種稍微高效一些的訓練方法:

  • Hierarchical softmax
  • Negative sampling

1. Skip-grams原理和網絡結構

Word2Vec模型中,主要有Skip-Gram和CBOW兩種模型,從直觀上理解,Skip-Gram是給定input word來預測上下文。而CBOW是給定上下文,來預測input word。

Word2Vec模型實際上分為了兩個部分,第一部分為建立模型,第二部分是通過模型獲取嵌入詞向量。

Word2Vec的整個建模過程實際上與自編碼器(auto-encoder)的思想很相似,即先基于訓練數據構建一個神經網絡,當這個模型訓練好以后,我們并不會用這個訓練好的模型處理新的任務,我們真正需要的是這個模型通過訓練數據所學得的參數,例如隱層的權重矩陣——后面我們將會看到這些權重在Word2Vec中實際上就是我們試圖去學習的“word vectors”。

Skip-grams過程

假如我們有一個句子“The dog barked at the mailman”。

  1. 首先我們選句子中間的一個詞作為我們的輸入詞,例如我們選取“dog”作為input word;

  2. 有了input word以后,我們再定義一個叫做skip_window的參數,它代表著我們從當前input word的一側(左邊或右邊)選取詞的數量。如果我們設置skip_window=2,那么我們最終獲得窗口中的詞(包括input word在內)就是['The', 'dog','barked', 'at']。skip_window=2代表著選取左input word左側2個詞和右側2個詞進入我們的窗口,所以整個窗口大小span=2x2=4。另一個參數叫num_skips,它代表著我們從整個窗口中選取多少個不同的詞作為我們的output word,當skip_window=2,num_skips=2時,我們將會得到兩組 (input word, output word) 形式的訓練數據,即 ('dog', 'barked'),('dog', 'the')。

  3. 神經網絡基于這些訓練數據將會輸出一個概率分布,這個概率代表著我們的詞典中的每個詞作為input word的output word的可能性。這句話有點繞,我們來看個例子。第二步中我們在設置skip_window和num_skips=2的情況下獲得了兩組訓練數據。假如我們先拿一組數據 ('dog', 'barked') 來訓練神經網絡,那么模型通過學習這個訓練樣本,會告訴我們詞匯表中每個單詞當'dog'作為input word時,其作為output word的可能性。

也就是說模型的輸出概率代表著到我們詞典中每個詞有多大可能性跟input word同時出現。例如:如果我們向神經網絡模型中輸入一個單詞“Soviet“,那么最終模型的輸出概率中,像“Union”, ”Russia“這種相關詞的概率將遠高于像”watermelon“,”kangaroo“非相關詞的概率。因為”Union“,”Russia“在文本中更大可能在”Soviet“的窗口中出現。

我們將通過給神經網絡輸入文本中成對的單詞來訓練它完成上面所說的概率計算。下面的圖中給出了一些我們訓練樣本的例子。我們選定句子“The quick brown fox jumps over lazy dog”,設定我們的窗口大小為2(window_size=2),也就是說我們僅選輸入詞前后各兩個詞和輸入詞進行組合。下圖中,藍色代表input word,方框內代表位于窗口內的單詞。

網絡結構

我們的模型將會從每對單詞出現的次數中習得統計結果。例如,我們的神經網絡可能會得到更多類似(“Soviet“,”Union“)這樣的訓練樣本對,而對于(”Soviet“,”Sasquatch“)這樣的組合卻看到的很少。因此,當我們的模型完成訓練后,給定一個單詞”Soviet“作為輸入,輸出的結果中”Union“或者”Russia“要比”Sasquatch“被賦予更高的概率。

PS:input word和output word都會被我們進行one-hot編碼。仔細想一下,我們的輸入被one-hot編碼以后大多數維度上都是0(實際上僅有一個位置為1),所以這個向量相當稀疏,那么會造成什么結果呢。如果我們將一個1 x 10000的向量和10000 x 300的矩陣相乘,它會消耗相當大的計算資源,為了高效計算,它僅僅會選擇矩陣中對應的向量中維度值為1的索引行:


2. Skip-grams訓練和negative sampling

由上部分可知,Word2Vec模型是一個超級大的神經網絡(權重矩陣規模非常大)。例如:我們擁有10000個單詞的詞匯表,我們如果想嵌入300維的詞向量,那么我們的輸入-隱層權重矩陣和隱層-輸出層的權重矩陣都會有 10000 x 300 = 300萬個權重,在如此龐大的神經網絡中進行梯度下降是相當慢的。更糟糕的是,你需要大量的訓練數據來調整這些權重并且避免過擬合。百萬數量級的權重矩陣和億萬數量級的訓練樣本意味著訓練這個模型將會是個災難

解決方案:
  • 將常見的單詞組合(word pairs)或者詞組作為單個“words”來處理
  • 對高頻次單詞進行抽樣來減少訓練樣本的個數
  • 對優化目標采用“negative sampling”方法,這樣每個訓練樣本的訓練只會更新一小部分的模型權重,從而降低計算負擔
Word pairs and "phases"

一些單詞組合(或者詞組)的含義和拆開以后具有完全不同的意義。比如“Boston Globe”是一種報刊的名字,而單獨的“Boston”和“Globe”這樣單個的單詞卻表達不出這樣的含義。因此,在文章中只要出現“Boston Globe”,我們就應該把它作為一個單獨的詞來生成其詞向量,而不是將其拆開。同樣的例子還有“New York”,“United Stated”等。

在Google發布的模型中,它本身的訓練樣本中有來自Google News數據集中的1000億的單詞,但是除了單個單詞以外,單詞組合(或詞組)又有3百萬之多。

對高頻詞抽樣

在上一部分中,對于原始文本為“The quick brown fox jumps over the laze dog”,如果使用大小為2的窗口,那么我們可以得到圖中展示的那些訓練樣本。


但是對于“the”這種常用高頻單詞,這樣的處理方式會存在下面兩個問題:

  1. 當我們得到成對的單詞訓練樣本時,("fox", "the") 這樣的訓練樣本并不會給我們提供關于“fox”更多的語義信息,因為“the”在每個單詞的上下文中幾乎都會出現

  2. 由于在文本中“the”這樣的常用詞出現概率很大,因此我們將會有大量的(”the“,...)這樣的訓練樣本,而這些樣本數量遠遠超過了我們學習“the”這個詞向量所需的訓練樣本數

Word2Vec通過“抽樣”模式來解決這種高頻詞問題。它的基本思想如下:對于我們在訓練原始文本中遇到的每一個單詞,它們都有一定概率被我們從文本中刪掉,而這個被刪除的概率與單詞的頻率有關。

ωi 是一個單詞,Z(ωi) 是 ωi 這個單詞在所有語料中出現的頻次,例如:如果單詞“peanut”在10億規模大小的語料中出現了1000次,那么 Z(peanut) = 1000/1000000000 = 1e - 6。

P(ωi) 代表著保留某個單詞的概率:

negative sampling

訓練一個神經網絡意味著要輸入訓練樣本并且不斷調整神經元的權重,從而不斷提高對目標的準確預測。每當神經網絡經過一個訓練樣本的訓練,它的權重就會進行一次調整。

所以,詞典的大小決定了我們的Skip-Gram神經網絡將會擁有大規模的權重矩陣,所有的這些權重需要通過數以億計的訓練樣本來進行調整,這是非常消耗計算資源的,并且實際中訓練起來會非常慢。

負采樣(negative sampling)解決了這個問題,它是用來提高訓練速度并且改善所得到詞向量的質量的一種方法。不同于原本每個訓練樣本更新所有的權重,負采樣每次讓一個訓練樣本僅僅更新一小部分的權重,這樣就會降低梯度下降過程中的計算量。

當我們用訓練樣本 ( input word: "fox",output word: "quick") 來訓練我們的神經網絡時,“ fox”和“quick”都是經過one-hot編碼的。如果我們的詞典大小為10000時,在輸出層,我們期望對應“quick”單詞的那個神經元結點輸出1,其余9999個都應該輸出0。在這里,這9999個我們期望輸出為0的神經元結點所對應的單詞我們稱為“negative” word。

當使用負采樣時,我們將隨機選擇一小部分的negative words(比如選5個negative words)來更新對應的權重。我們也會對我們的“positive” word進行權重更新(在我們上面的例子中,這個單詞指的是”quick“)。

PS: 在論文中,作者指出指出對于小規模數據集,選擇5-20個negative words會比較好,對于大規模數據集可以僅選擇2-5個negative words。

我們使用“一元模型分布(unigram distribution)”來選擇“negative words”。個單詞被選作negative sample的概率跟它出現的頻次有關,出現頻次越高的單詞越容易被選作negative words。

每個單詞被選為“negative words”的概率計算公式:

其中 f(ωi)代表著單詞出現的頻次,而公式中開3/4的根號完全是基于經驗的。

在代碼負采樣的代碼實現中,unigram table有一個包含了一億個元素的數組,這個數組是由詞匯表中每個單詞的索引號填充的,并且這個數組中有重復,也就是說有些單詞會出現多次。那么每個單詞的索引在這個數組中出現的次數該如何決定呢,有公式,也就是說計算出的負采樣概率*1億=單詞在表中出現的次數。

有了這張表以后,每次去我們進行負采樣時,只需要在0-1億范圍內生成一個隨機數,然后選擇表中索引號為這個隨機數的那個單詞作為我們的negative word即可。一個單詞的負采樣概率越大,那么它在這個表中出現的次數就越多,它被選中的概率就越大。


3. Hierarchical Softmax

霍夫曼樹

輸入:權值為(w1,w2,...wn)的n個節點

輸出:對應的霍夫曼樹

  1. 將(w1,w2,...wn)看做是有n棵樹的森林,每個樹僅有一個節點

  2. 在森林中選擇根節點權值最小的兩棵樹進行合并,得到一個新的樹,這兩顆樹分布作為新樹的左右子樹。新樹的根節點權重為左右子樹的根節點權重之和

  3. 將之前的根節點權值最小的兩棵樹從森林刪除,并把新樹加入森林

  4. 重復步驟 2 和 3 直到森林里只有一棵樹為止

下面我們用一個具體的例子來說明霍夫曼樹建立的過程,我們有(a,b,c,d,e,f)共6個節點,節點的權值分布是(16,4,8,6,20,3)。
首先是最小的b和f合并,得到的新樹根節點權重是7.此時森林里5棵樹,根節點權重分別是16,8,6,20,7。此時根節點權重最小的6,7合并,得到新子樹,依次類推,最終得到下面的霍夫曼樹。

霍夫曼樹

那么霍夫曼樹有什么好處呢?一般得到霍夫曼樹后我們會對葉子節點進行霍夫曼編碼,由于權重高的葉子節點越靠近根節點,而權重低的葉子節點會遠離根節點,這樣我們的高權重節點編碼值較短,而低權重值編碼值較長。這保證的樹的帶權路徑最短,也符合我們的信息論,即我們希望越常用的詞擁有更短的編碼。如何編碼呢?一般對于一個霍夫曼樹的節點(根節點除外),可以約定左子樹編碼為0,右子樹編碼為1。如上圖,則可以得到c的編碼是00。

在word2vec中,約定編碼方式和上面的例子相反,即約定左子樹編碼為1,右子樹編碼為0,同時約定左子樹的權重不小于右子樹的權重。

更多原理可參考:霍夫曼樹原理

Hierarchical Softmax過程

為了避免要計算所有詞的softmax概率,word2vec采樣了霍夫曼樹來代替從隱藏層到輸出softmax層的映射。

霍夫曼樹的建立:

  • 根據標簽(label)和頻率建立霍夫曼樹(label出現的頻率越高,Huffman樹的路徑越短)
  • Huffman樹中每一葉子結點代表一個label
霍夫曼樹

如上圖所示:

注意:此時的theta是一個待定系數,它是由推導最大似然之后求解得到迭代式子。


三、word2vec數學推導

word2vec的主要思路:

  • 預測一個窗口長度為c的窗口內每個單詞的周邊單詞概率
  • 目標函數:對于一個中心詞,最大化周邊任意單詞的log概率

一個詞出現在另一個詞的上下文語境里的條件概率可以表示為:

其中:v_wOv_wI 是分別是 wOwI 的詞向量表示

這兩個矩陣都含有V個詞向量,也就是說同一個詞有兩個詞向量,哪個作為最終的、提供給其他應用使用的embeddings呢?有兩種策略,要么加起來,要么拼接起來。W中的向量叫input vector,W'中的向量叫output vector。

接下去就是推導梯度下降的過程:

使用Hierarchical SoftmaxNegative Sampling的推導參考

參考

  1. CS224n筆記2 詞的向量表示:word2vec
  2. 斯坦福大學深度學習與自然語言處理第二講:詞向量
  3. (Stanford CS224d) Deep Learning and NLP課程筆記(三):GloVe與模型的評估
  4. http://www.cnblogs.com/pinard/p/7249903.html
  5. https://blog.csdn.net/yinkun6514/article/details/79218736
  6. https://www.leiphone.com/news/201706/PamWKpfRFEI42McI.html
  7. cs224n
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。