
本文為 AI 研習社編譯的技術部落格,原标題 :
An implementation guide to Word2Vec using NumPy and Google Sheets
作者 | Derek Chia
翻譯 | mui
校對 | 醬番梨 整理 | 鳳梨妹
原文連結:
https://medium.com/@derekchia/an-implementation-guide-to-word2vec-using-numpy-and-google-sheets-13445eebd281
Word2Vec被認為是自然語言處理(NLP)領域中最大、最新的突破之一。其的概念簡單,優雅,(相對)容易掌握。Google一下就會找到一堆關于如何使用諸如Gensim和TensorFlow的庫來調用Word2Vec方法的結果。另外,對于那些好奇心強的人,可以檢視Tomas Mikolov基于C語言的原始實作。原稿也可以在這裡找到。
本文的主要重點是詳細介紹Word2Vec。為此,我在Python上使用Numpy(在其他教程的幫助下)實作了Word2Vec,還準備了一個Google Sheet來展示計算結果。以下是代碼和Google Sheet的連結。
圖1.一步一步來介紹Word2Vec。由代碼和Google Sheet呈現
直覺上看
Word2Vec的目标是生成帶有語義的單詞的向量表示,用于進一步的NLP任務。每個單詞向量通常有幾百個次元,語料庫中每個唯一的單詞在空間中被配置設定一個向量。例如,單詞“happy”可以表示為4維向量[0.24、0.45、0.11、0.49],“sad”具有向量[0.88、0.78、0.45、0.91]。
這種從單詞到向量的轉換也被稱為單詞嵌入(word embedding)。這種轉換的原因是機器學習算法可以對數字(在向量中的)而不是單詞進行線性代數運算。
為了實作Word2Vec,有兩種風格可以選擇,Continuous Bag-of-Words(CBOW)或Skip-gram(SG)。簡單來說,CBOW嘗試從相鄰單詞(上下文單詞)猜測輸出(目标單詞),而Skip-Gram從目标單詞猜測上下文單詞。實際上,Word2Vec是基于分布假說,其認為每個單詞的上下文都在其附近的單詞中。是以,通過檢視它的相鄰單詞我們可以嘗試對目标單詞進行預測。
根據Mikolov(引用于這篇文章),以下是Skip-gram和CBOW之間的差別:
Skip-gram:能夠很好地處理少量的訓練資料,而且能夠很好地表示不常見的單詞或短語
CBOW:比skip-gram訓練快幾倍,對出現頻率高的單詞的準确度稍微更好一些
更詳細地說,由于Skip-gram學習用給定單詞來預測上下文單詞,是以萬一兩個單詞(一個出現頻率較低,另一個出現頻率較高)放在一起,那麼當最小化loss值時,兩個單詞将進行有相同的處理,因為每個單詞都将被當作目标單詞和上下文單詞。與CBOW相比,不常見的單詞将隻是用于預測目标單詞的上下文單詞集合的一部分。是以,該模型将給不常現的單詞配置設定一個低機率。
圖2—Word2Vec—CBOW和skip-gram模型架構。感謝:IDIL
實作過程
在本文中,我們将實作Skip-gram體系結構。為了便于閱讀,内容分為以下幾個部分:
1.資料準備——定義語料庫、整理、規範化和分詞
2.超參數——學習率、訓練次數、視窗尺寸、嵌入(embedding)尺寸
3.生成訓練資料——建立詞彙表,對單詞進行one-hot編碼,建立将id映射到單詞的字典,以及單詞映射到id的字典
4.模型訓練——通過正向傳遞編碼過的單詞,計算錯誤率,使用反向傳播調整權重和計算loss值
5.結論——擷取詞向量,并找到相似的詞
6.進一步的改進 —— 利用Skip-gram負采樣(Negative Sampling)和Hierarchical Softmax提高訓練速度
1.資料準備
首先,我們從以下語料庫開始:
natural language processing and machine learning is fun and exciting
簡單起見,我們選擇了一個沒有标點和大寫的橘子。而且,我們沒有删除停用詞“and”和“is”。
實際上,文本資料是非結構化的,甚至可能很“很不幹淨”清理它們涉及一些步驟,例如删除停用詞、标點符号、将文本轉換為小寫(實際上取決于你的實際例子)和替換數字等。KDnuggets 上有一篇關于這個步驟很棒的文章。另外,Gensim也提供了執行簡單文本預處理的函數——gensim.utils.simple_preprocess,它将文檔轉換為由小寫的詞語(Tokens )組成的清單,并忽略太短或過長的詞語。
在預處理之後,我們開始對語料庫進行分詞。我們按照單詞間的空格對我們的語料庫進行分詞,結果得到一個單詞清單:
[“natural”, “language”, “processing”, “ and”, “ machine”, “ learning”, “ is”, “ fun”, “and”, “ exciting”]
2.超參數
在進入word2vec的實作之前,讓我們先定義一些稍後需要用到的超參數。
[window_size/視窗尺寸]:如之前所述,上下文單詞是與目标單詞相鄰的單詞。但是,這些詞應該有多遠或多近才能被認為是相鄰的呢?這裡我們将視窗尺寸定義為2,這意味着目标單詞的左邊和右邊最近的2個單詞被視為上下文單詞。參見下面的圖3,可以看到,當視窗滑動時,語料庫中的每個單詞都會成為一個目标單詞。
圖3,在window_size為2的情況下,目标單詞用橙色高亮顯示,上下文單詞用綠色高亮顯示
[n]:這是單詞嵌入(word embedding)的次元,通常其的大小通常從100到300不等,取決于詞彙庫的大小。超過300次元會導緻效益遞減(參見圖2(a)的1538頁)。請注意,次元也是隐藏層的大小。
[epochs] :表示周遊整個樣本的次數。在每個epoch中,我們循環通過一遍訓練集的樣本。
[learning_rate/學習率]:學習率控制着損失梯度對權重進行調整的量。
3.生成訓練資料
在本節中,我們的主要目标是将語料庫轉換one-hot編碼表示,以友善Word2vec模型用來訓練。從我們的語料庫中,圖4中顯示了10個視窗(#1到#10)中的每一個。每個視窗都由目标單詞及其上下文單詞組成,分别用橙色和綠色高亮顯示。
圖4,每個目标單詞及其上下文單詞的one hot編碼
第一個和最後一個訓練視窗中的第一個和最後一個元素的示例如下所示:
# 1 [目标單詞(natural)], [上下文單詞 (language, processing)][list([1, 0, 0, 0, 0, 0, 0, 0, 0])
list([[0, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0]])]
*****#2 to #9 省略****#10
[ 目标單詞 (exciting)], [ 上下文單詞 (fun, and)]
[list([0, 0, 0, 0, 0, 0, 0, 0, 1])
list([[0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0]])]
為了生成one-hot訓練資料,我們首先初始化word2vec()對象,然後使用對象w2v通過settings 和corpus 參數來調用函數generate_training_data。
在函數generate_training_data内部,我們進行以下操作:
self.v_count: 詞彙表的長度(注意,詞彙表指的就是語料庫中不重複的單詞的數量)
self.words_list: 在詞彙表中的單詞組成的清單
self.word_index: 以詞彙表中單詞為key,索引為value的字典資料
self.index_word: 以索引為key,以詞彙表中單詞為value的字典資料
for循環給用one-hot表示的每個目标詞和其的上下文詞添加到training_data中,one-hot編碼用的是word2onehot函數。
4.模型訓練
圖5,Word2Vec——skip-gram的網絡結構
擁有了training_data,我們現在可以準備訓練模型了。訓練從w2v.train(training_data)開始,我們傳入訓練資料,并執行train函數。
Word2Vec2模型有兩個權重矩陣(w1和w2),為了展示,我們把值初始化到形狀分别為(9x10)和(10x9)的矩陣。這便于反向傳播誤差的計算,這部分将在後文讨論。在實際的訓練中,你應該随機初始化這些權重(比如使用np.random.uniform())。想要這麼做,把第九第十行注釋掉,把11和12行取消注釋就好。
訓練——向前傳遞
接下來,我們開始用第一組訓練樣本來訓練第一個epoch,方法是把w_t 傳入forward_pass 函數,w_t 是表示目标詞的one-hot向量。在forward_pass 函數中,我們執行一個w1 和w_t 的點乘積,得到h (原文是24行,但圖中實際是第22行)。然後我們執行w2和h 點乘積,得到輸出層的u( 原文是26行,但圖中實際是第24行 )。最後,在傳回預測向量y_pred和隐藏層h 和輸出層u 前,我們使用softmax把u 的每個元素的值映射到0和1之間來得到用來預測的機率(第28行)。
我附上一些截圖展示第一視窗(#1)中第一個訓練樣本的計算,其中目标詞是“natural”,上下文單詞是“language”和“processing”。可以在這裡檢視Google Sheet中的公式。
圖6,計算隐藏層,輸出層和softmax
訓練——誤差,反向傳播和損失(loss)
誤差——對于y_pred、h 和u,我們繼續計算這組特定的目标詞和上下文詞的誤差。這是通過對y_pred 與在w_c 中的每個上下文詞之間的差的加合來實作的。
圖7,計算誤差——上下文單詞是“language”和“processing”
反向傳播——接下來,我們使用反向傳播函數backprop ,通過傳入誤差EI 、隐藏層h 和目标字w_t 的向量,來計算我們所需的權重調整量。
為了更新權重,我們将權重的調整量(dl_dw1 和dl_dw2 )與學習率相乘,然後從目前權重(w1 和w2 )中減去它。
圖8,反向傳播——計算W1和W2的增量
圖9,反向傳播——調整權重以得到更新後的W1和W2
損失——最後,根據損失函數計算出每個訓練樣本完成後的總損失。注意,損失函數包括兩個部分。第一部分是輸出層(在softmax之前)中所有元素的和的負數。第二部分是上下文單詞的數量乘以在輸出層中所有元素(在 exp之後)之和的對數。
圖10,Skip-gram的損失函數。
引用至:https://arxiv.org/pdf/1411.2738.pdf
5. 推論和總結(Inferencing)
既然我們已經完成了50個epoch的訓練,兩個權重(w1和w2)現在都準備好執行推論了。
擷取單詞的向量
有了一組訓練後的權重,我們可以做的第一件事是檢視詞彙表中單詞的詞向量。我們可以簡單地通過查找單詞的索引來對訓練後的權重(w1)進行查找。在下面的示例中,我們查找單詞“machine”的向量。
> print(w2v.word_vec("machine"))
[ 0.76702922 -0.95673743 0.49207258 0.16240808 -0.4538815
-0.74678226 0.42072706 -0.04147312 0.08947326 -0.24245257]
查詢相似的單詞
我們可以做的另一件事就是找到類似的單詞。即使我們的詞彙量很小,我們仍然可以通過計算單詞之間的餘弦相似度來實作函數vec_sim 。
> w2v.vec_sim("machine", 3)
machine 1.0
fun 0.6223490454018772
and 0.5190154215400249
6.進一步改進
如果你還在讀這篇文章,做得好,謝謝!但這還沒結束。正如你在上面的反向傳播步驟中可能已經注意到的,我們需要調整訓練樣本中沒有涉及的所有其他單詞的權重。如果詞彙量很大(例如數萬),這個過程可能需要很長時間。
為了解決這個問題,您可以在Word2Vec中實作以下兩個特性,以加快速度:
Skip-gram Negative Sampling (SGNS) 有助于加快訓練時間,提高最終的詞向量的品質。這是通過訓練網絡隻修改一小部分的權重而不是全部的權重來實作。回想一下上面的示例,我們對每一個詞的權重都進行更新,若詞彙庫的尺寸很大,這可能需要很長時間。對于SGNS,我們隻需要更新目标詞和少量(例如,5到20)随機“否定”單詞的權重。
Hierarchical Softmax是用來替換原始softmax加速訓練的另一個技巧。其主要思想是,不需要對所有輸出節點進行評估來獲得機率分布,隻需要評估它的對數個數(基為2)。使用二叉樹(Huffman編碼樹)表示,其中輸出層中的節點表示為葉子,其節點由與其子節點的相應的機率表示。
圖11,Hierarchical二叉樹,被高亮的為從根到W2的路徑
除此之外,為什麼不嘗試調整代碼來實作Continuous Bag-of-Words(Continuous Bag-of-Words,CBOW)構架呢?
結論
本文是對Word2Vec的介紹,并解除了單詞嵌入(word embedding)的世界。另外還值得注意的是,有預訓練的嵌入可用,如GloVe、fastText和ELMo,你可以直接下載下傳和使用。此外還有Word2Vec的擴充,如Doc2Vec和最近的Code2Vec,在這倆方法中文檔和代碼被轉換成向量。
最後,我要感謝Ren Jie Tan、Raimi 和Yuxin抽出時間來閱讀和評論本文的草稿。
參考
nathanrooy/word2vec-from-scratch-with-python
A very simple, bare-bones, inefficient, implementation of skip-gram word2vec from scratch with Python …github.com
Word2vec from Scratch with Python and NumPy
TL;DR - word2vec is awesome, it's also really simple. Learn how it works, and implement your own version. Since joining…nathanrooy.github.io
Why word2vec maximizes the cosine similarity between semantically similar words
Thanks for contributing an answer to Cross Validated! Some of your past answers have not been well-received, and you're…stats.stackexchange.com
Hierarchical softmax and negative sampling: short notes worth telling
Thanks to unexpected and very pleasant attention the audience has paid to my last (and the only) post here dedicated to…towardsdatascience.com
感謝 Ren Jie Tan 和 Raimi Bin Karim.
想要繼續檢視該篇文章相關連結和參考文獻?
長按連結點選打開或點選底部【手把手教你NumPy來實作Word2vec】:
https://ai.yanxishe.com/page/TextTranslation/1317
AI研習社每日更新精彩内容,觀看更多精彩内容:雷鋒網雷鋒網(公衆号:雷鋒網)雷鋒網
五個很厲害的 CNN 架構
深度強化學習中的好奇心
用Pytorch做深度學習(第一部分)
手把手:用PyTorch實作圖像分類器(第二部分)
等你來譯:
對混亂的資料進行聚類
初學者怎樣使用Keras進行遷移學習
強化學習:通往基于情感的行為系統
如果你想學資料科學,這 7 類資源千萬不能錯過