一篇很好的入門博客,http://mccormickml.com/2016/04/19/word2vec-tutorial-the-skip-gram-model/
他的翻譯,https://www.jianshu.com/p/1405932293ea
可以作為參考的,https://blog.csdn.net/mr_tyting/article/details/80091842
有論文和代碼,https://blog.csdn.net/mr_tyting/article/details/80091842
word2vector,顧名思義,就是將語料庫中的詞轉化成向量,以便后續在詞向量的基礎上進行各種計算。
最常見的表示方法是counting 編碼。假設我們的語料庫中是如下三句話:
I like deep learning
I like NLP
I enjoy flying
利用counting編碼,我們可以繪出如下矩陣:
假設語料庫中的單詞數量是N,則上圖矩陣的大小就是N*N,其中的每一行就代表一個詞的向量表示。如第一行
0 2 1 0 0 0 0
是單詞I的向量表示。其中的2代表I這個單詞與like這個詞在語料庫中共同出現了2次。
但是這種辦法至少有三個缺陷:
- 1是詞語數量較大時,向量維度高且稀疏,向量矩陣巨大而難以存儲
- 2是向量並不包含單詞的語義內容,只是基於數量統計。
- 3是當有新的詞加入語料庫后,整個向量矩陣需要更新
盡管我們可以通過SVD來降低向量的維度,但是SVD本身卻是一個需要巨大計算量的操作。
很明顯,這種辦法在實際中並不好用。我們今天學習的skip gram算法可以成功克服以上三個缺陷。它的基本思想是首先將所有詞語進行one-hot編碼,輸入只有一個隱藏層的神經網絡,定義好loss后進行訓練,后面我們會講解如何定義loss,這里暫時按下不表。訓練完成后,我們就可以用隱藏層的權重來作為詞的向量表示!!
這個思想乍聽起來很神奇是不是?其實我們早就熟悉它了。auto-encoder時,我們也是用有一個隱藏層的神經網絡進行訓練,訓練完成后,丟去后面的output層,只用隱藏層的輸出作為最終需要的向量對象,藉此成功完成向量的壓縮。
Word2Vec工作流程
Word2Vec有兩種訓練方法,一種叫CBOW,核心思想是從一個句子里面把一個詞摳掉,用這個詞的上文和下文去預測被摳掉的這個詞;第二種叫做Skip-gram,和CBOW正好反過來,輸入某個單詞,要求網絡預測它的上下文單詞。下面以Skip-gram為例介紹,
1、word2Vec只是一個三層 的神經網絡。
2、喂給模型一個word,然后用來預測它周邊的詞。
3、然后去掉最后一層,只保存input_layer 和 hidden_layer。
4、從詞表中選取一個詞,喂給模型,在hidden_layer 將會給出該詞的embedding repesentation。
用神經網絡訓練,大體有如下幾個步驟:
准備好data,即X和Y
定義好網絡結構
定義好loss
選擇合適的優化器
進行迭代訓練
存儲訓練好的網絡
一、構造訓練數據
假設我們的語料庫中只有一句話:The quick brown fox jumps over the lazy dog.
這句話中共有8個詞(這里The與the算同一個詞)。skip gram算法是怎么為這8個詞生成詞向量的呢?
其實非常簡單,(x,y)就是一個個的單詞對。比如(the,quick)就是一個單詞對,the就是樣本數據,quick就是該條樣本的標簽。
那么,如何從上面那句話中生成單詞對數據呢?答案就是n-gram方法。多說不如看圖:
這里有兩個細節,一個就是取被掃描單詞左右各2個詞,這里的2被稱為 窗口尺寸,是可以調整的,用多大的窗口生成的單詞對來訓練最好,需要具體問題具體分析。一般來說,取5是很好的經驗值。也就是左右各取5個單詞,共10個單詞。第二個細節就是句子頭尾的單詞被掃描時,其能取的單詞對數要少幾個,這個不影響大局,不用理會。
你可能會想,(fox,brown)也是一個單詞對,它輸入神經網絡后,豈不是希望神經網絡告訴我們,在8個單詞中,brown是更可能出現在fox周圍?如果是這樣,那么訓練完成后的神經網絡,輸入fox,它的輸出會是brown和jumps的哪一個呢?
答案是取決於(fox,brown)和(fox,jumps)兩個單詞對誰在訓練集中出現的次數比較多,神經網絡就會針對哪個單詞對按照梯度下降進行更多的調整,從而就會傾向於預測誰將出現在fox周圍。
二、數字化表示單詞對

(the,quick)單詞對就表示成【(1,0,0,0,0,0,0,0),(0,1,0,0,0,0,0,0)】。這樣就可以輸入神經網絡進行訓練了,當我們將the輸入神經網絡時,希望網絡也能輸出一個8維的向量,並且第二維盡可能接近1(即接近quick),其他維盡可能接近0。也就是讓神經網絡告訴我們,quick更可能出現在the的周圍。當然,我們還希望這8維向量所有位置的值相加為1,因為相加為1就可以認為這個8維向量描述的是一個概率分布,正好我們的y值也是一個概率分布(一個位置為1,其他位置為0),我們就可以用交叉熵來衡量神經網絡的輸出與我們的label y的差異大小,也就可以定義出loss了。
網絡結構如下,它的隱藏層並沒有激活函數,但是輸出層卻用了softmax,這是為了保證輸出的向量是一個概率分布。

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

網絡訓練完成后,這個8行3列的矩陣的每一行就是一個單詞的詞向量!如下圖所示:
網絡的輸入是one-hot編碼的單詞,它與隱藏層權重矩陣相乘實際上是取權重矩陣特定的行,如下圖所示:

這意味着,隱藏層實際上相當於是一個查找表,它的輸出就是輸入的單詞的詞向量
5、輸出層
輸出層的神經元數量和語料庫中的單詞數量一樣。每一個神經元可以認為對應一個單詞的 輸出權重,詞向量乘以該 輸出權重就得到一個數,該數字代表了輸出神經元對應的單詞出現在輸入單詞周圍的可能性大小,通過對所有的輸出層神經元的輸出進行softmax操作,我們就把輸出層的輸出規整為一個概率分布了。如下圖所示:

這里有一點需要注意,我們說輸出的是該單詞出現在輸入單詞周圍的概率大小,這個“周圍”包含單詞的前面,也包含單詞的后面。
首先,語義相近的詞往往有着類似的上下文。這是什么意思呢?舉例來說,“聰明”和“伶俐”兩個詞語義是相近的,那么它們的使用場景也是相似的,它們周圍的詞很大程度上是相近或相同的。
語義相近的詞有着相似的上下文,讓我們的神經網絡在訓練過程中對相近的詞產生相近的輸出向量。網絡如何做到這一點呢?答案就是訓練完成后,網絡能夠對語義相近的詞產生相近的詞向量。因為此時的輸出層已經訓練完成,不會改變了。

在tensorflow里面實現的word2Vec,vocab_szie並不是所有的word的數量,而且先統計了所有word的出現頻次,然后選取出現頻次最高的前50000的詞作為詞袋。具體操作請看代碼 tensorflow/examples/tutorials/word2vec/word2vec_basic.py,其余的詞用unkunk代替。
采用一種所謂的”負采樣”的操作,這種操作每次可以讓一個樣本只更新權重矩陣中一小部分,減小訓練過程中的計算壓力。
舉例來說:一個input output pair如:(“fox”,“quick”),由上面的分析可知,其true label為一個one−hot向量,並且該向量只是在quick的位置為1,其余的位置均為0,並且該向量的長度為vocab size,由此每個樣本都緩慢能更新權重矩陣,而”負采樣”操作只是隨機選擇其余的部分word,使得其在true label的位置為0,那么我們只更新對應位置的權重。例如我們如果選擇負采樣數量為5,則選取5個其余的word,使其對應的output為0,這個時候output只是6個神經元,本來我們一次需要更新300∗10,000參數,進行負采樣操作以后只需要更新300∗6=1800個參數。