1. 程式人生 > >深度學習必須熟悉的演算法之word2vector(一)

深度學習必須熟悉的演算法之word2vector(一)

作者 milter

作者:milter

連結:https://www.jianshu.com/p/1405932293ea

word2vector已經成為NLP領域的基石演算法。作為一名AI 從業者,如果不能主動去熟悉該演算法,應該感到臉紅。本文是一篇翻譯的文章,原文連結是:http://mccormickml.com/2016/04/19/word2vec-tutorial-the-skip-gram-model/
如果你的英語很好,強烈建議直接閱讀原文。這篇文章寫的非常好,簡明扼要,語言流暢。

這是我認為入門word2vector的最好文章,沒有之一。當然,我也不是生硬的翻譯,而是理解之後按照自己的邏輯再寫出來,希望能更加清晰一些。

歡迎在評論中說出你的看法,多多交流。word2vector常見的有兩種演算法CBOW和skip gram,本文使用skip gram演算法作為講解物件。

1演算法的基本思想

word2vector,顧名思義,就是將語料庫中的詞轉化成向量,以便後續在詞向量的基礎上進行各種計算。最常見的表示方法是counting 編碼。假設我們的語料庫中是如下三句話:
I like deep learning
I like NLP
I enjoy flying
利用counting編碼,我們可以繪出如下矩陣:

640?wxfrom=5&wx_lazy=1&retryload=1

假設語料庫中的單詞數量是N,則上圖矩陣的大小就是N*N,其中的每一行就代表一個詞的向量表示。如第一行
0 2 1 0 0  0 0
是單詞I的向量表示。其中的2代表I這個單詞與like這個詞在語料庫中共同出現了2次。
似乎我們很簡單就完成了“word2vector”是不是?
但是這種辦法至少有三個缺陷:

  • 1是詞語數量較大時,向量維度高且稀疏,向量矩陣巨大而難以儲存

  • 2是向量並不包含單詞的語義內容,只是基於數量統計。

  • 3是當有新的詞加入語料庫後,整個向量矩陣需要更新

儘管我們可以通過SVD來降低向量的維度,但是SVD本身卻是一個需要巨大計算量的操作。


很明顯,這種辦法在實際中並不好用。我們今天學習的skip gram演算法可以成功克服以上三個缺陷。它的基本思想是首先將所有詞語進行one-hot編碼,輸入只有一個隱藏層的神經網路,定義好loss後進行訓練。

後面我們會講解如何定義loss,這裡暫時按下不表。訓練完成後,我們就可以用隱藏層的權重來作為詞的向量表示!!


這個思想乍聽起來很神奇是不是?其實我們早就熟悉它了。auto-encoder時,我們也是用有一個隱藏層的神經網路進行訓練,訓練完成後,丟去後面的output層,只用隱藏層的輸出作為最終需要的向量物件,藉此成功完成向量的壓縮。

2舉例說明

1 構造訓練資料

假設我們的語料庫中只有一句話:
The quick brown fox jumps over the lazy dog.
這句話中共有8個詞(這裡The與the算同一個詞)。


skip gram演算法是怎麼為這8個詞生成詞向量的呢?
我們知道用神經網路訓練,大體有如下幾個步驟:

  • 準備好data,即X和Y

  • 定義好網路結構

  • 定義好loss

  • 選擇合適的優化器

  • 進行迭代訓練

  • 儲存訓練好的網路

所以,我們下面先來關注下如何確定X和Y的形式。
其實非常簡單,(x,y)就是一個個的單詞對。比如(the,quick)就是一個單詞對,the就是樣本資料,quick就是該條樣本的標籤。


那麼,如何從上面那句話中生成單詞對資料呢?答案就是n-gram方法。多說不如看圖:

640?

我們以詞為單位掃描這句話,每掃描到一個詞,都把該詞左右各兩個詞共4個詞拿出來,分別與被掃描的單片語成單詞對,作為我們的訓練資料。


這裡有兩個細節,一個就是取被掃描單詞左右各2個詞,這裡的2被稱為視窗尺寸,是可以調整的,用多大的視窗生成的單詞對來訓練最好,需要具體問題具體分析。

一般來說,取5是很好的經驗值。也就是左右各取5個單詞,共10個單詞。這裡我們用2只是為了方便說明問題。
第二個細節就是句子頭尾的單詞被掃描時,其能取的單詞對數要少幾個,這個不影響大局,不用理會。

這裡我們需要停下來細細琢磨下,我們這樣取單詞對作為訓練資料的目的何在?以(fox,jumps)為例,jumps可以理解為fox的上下文,我們將fox輸入神經網路時,希望網路能夠告訴我們,在語料庫的8個單詞中,jumps是更可能出現在fox周圍的。


你可能會想,(fox,brown)也是一個單詞對,它輸入神經網路後,豈不是希望神經網路告訴我們,在8個單詞中,brown是更可能出現在fox周圍?如果是這樣,那麼訓練完成後的神經網路,輸入fox,它的輸出會是brown和jumps的哪一個呢?


答案是取決於(fox,brown)和(fox,jumps)兩個單詞對誰在訓練集中出現的次數比較多,神經網路就會針對哪個單詞對按照梯度下降進行更多的調整,從而就會傾向於預測誰將出現在fox周圍。

3數字化表示單詞對

上面我們獲得了許多單詞對作為訓練資料,但是神經網路不能直接接收和輸出字串形式的單詞對,所以需要將單詞對轉化為數字的形式。

方法也很簡單,就是用one-hot編碼,如下圖所示:

640?

(the,quick)單詞對就表示成【(1,0,0,0,0,0,0,0),(0,1,0,0,0,0,0,0)】

這樣就可以輸入神經網路進行訓練了,當我們將the輸入神經網路時,希望網路也能輸出一個8維的向量,並且第二維儘可能接近1,其他維儘可能接近0。

也就是讓神經網路告訴我們,quick更可能出現在the的周圍。當然,我們還希望這8維向量所有位置的值相加為1,WHY?

因為相加為1就可以認為這個8維向量描述的是一個概率分佈,正好我們的y值也是一個概率分佈(一個位置為1,其他位置為0),我們就可以用交叉熵來衡量神經網路的輸出與我們的label y的差異大小,也就可以定義出loss了。

什麼,你不知道啥是交叉熵?請參考我的另一篇文章【機器學習面試之各種混亂的熵】,應該不會讓你失望。

4定義網路結構

通過之前的敘述,我們已經基本知道神經網路應該是什麼樣子了,總結一下,可以確定如下情況:

  • 神經網路的輸入應該是8維的向量

  • 神經網路只有一個隱藏層

  • 神經網路的輸出應該是一個8維向量,且各維的值相加為1
    有了這些資訊,我們可以很容易定義出如下的網路結構:

640?

觀察這個網路結構,我們可以發現,它的隱藏層並沒有啟用函式,但是輸出層卻用了softmax,這是為了保證輸出的向量是一個概率分佈。

5隱藏層

顯然,輸出層的神經元應該是8個,這樣才能輸出一個8維的向量。那麼隱藏層的神經元應該是多少?


這取決於我們希望得到的詞向量是多少維,有多少個隱藏神經元詞向量就是多少維。每一個隱藏的神經元接收的輸入都是一個8維向量,假設我們的隱藏神經元有3個(僅僅是為了舉例說明使用,實際中,google推薦的是300個,但具體多少合適,需要你自己進行試驗,怎麼效果好怎麼來),如此以來,隱藏層的權重就可以用一個8行3列的矩陣來表示。

下面就是見證奇蹟的時刻!

網路訓練完成後,這個8行3列的矩陣的每一行就是一個單詞的詞向量!如下圖所示:

640?

so,訓練完成後,我們只需要儲存好隱藏層的權重矩陣即可,輸出層此時已經完成歷史使命,可以丟掉了。

那麼怎麼使用去掉了輸出層的網路呢?
我們知道,網路的輸入是one-hot編碼的單詞,它與隱藏層權重矩陣相乘實際上是取權重矩陣特定的行,如下圖所示:

640?

這意味著,隱藏層實際上相當於是一個查詢表,它的輸出就是輸入的單詞的詞向量。

6輸出層

當我們從隱藏層獲得一個單詞的詞向量後,就要經過輸出層了。
輸出層的神經元數量和語料庫中的單詞數量一樣。

每一個神經元可以認為對應一個單詞的輸出權重,詞向量乘以該輸出權重就得到一個數,該數字代表了輸出神經元對應的單詞出現在輸入單詞周圍的可能性大小,通過對所有的輸出層神經元的輸出進行softmax操作,我們就把輸出層的輸出規整為一個概率分佈了。如下圖所示:


640?

這裡有一點需要注意,我們說輸出的是該單詞出現在輸入單詞周圍的概率大小,這個“周圍”包含單詞的前面,也包含單詞的後面。

7直覺的啟示

前面,我們表示,skip gram演算法生成的詞向量可以包含語義資訊,也就是說,語義相近的詞其詞向量也相近。這裡,我們給一個直覺的解釋。


首先,語義相近的詞往往有著類似的上下文。這是什麼意思呢?舉例來說,“聰明”和“伶俐”兩個詞語義是相近的,那麼它們的使用場景也是相似的,它們周圍的詞很大程度上是相近或相同的。


語義相近的詞有著相似的上下文,讓我們的神經網路在訓練過程中對相近的詞產生相近的輸出向量。網路如何做到這一點呢?答案就是訓練完成後,網路能夠對語義相近的詞產生相近的詞向量。因為此時的輸出層已經訓練完成,不會改變了。

這個直覺式的思考顯然是不嚴謹的,但卻能讓我們對神經網路有很好的洞見。

記得李巨集毅說過,有人問,LSTM設計那麼複雜,設計的人怎麼知道這樣的結構就能達到記憶的效果呢?事實上,不是知道這樣做會有記憶的效果才去這樣做,而是這樣做了,才有這樣的效果。是不是有點雞生蛋蛋生雞的趕腳?這就是為什麼深度學習被稱為玄學的原因吧。

       歡迎關注公眾號學習交流~    

640?wx_fmt=jpeg