當前位置:
首頁 > 新聞 > 手把手教你NumPy來實現Word2vec

手把手教你NumPy來實現Word2vec

本文為 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的鏈接。

手把手教你NumPy來實現Word2vec

圖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相比,不常見的單詞將只是用於預測目標單詞的上下文單詞集合的一部分。因此,該模型將給不常現的單詞分配一個低概率。

手把手教你NumPy來實現Word2vec

圖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 )組成的列表,並忽略太短或過長的詞語。

手把手教你NumPy來實現Word2vec

在預處理之後,我們開始對語料庫進行分詞。我們按照單詞間的空格對我們的語料庫進行分詞,結果得到一個單詞列表:


[「natural」, 「language」, 「processing」, 「 and」, 「 machine」, 「 learning」, 「 is」, 「 fun」, 「and」, 「 exciting」]

2.超參數

在進入word2vec的實現之前,讓我們先定義一些稍後需要用到的超參數。

手把手教你NumPy來實現Word2vec

[window_size/窗口尺寸]:如之前所述,上下文單詞是與目標單詞相鄰的單詞。但是,這些詞應該有多遠或多近才能被認為是相鄰的呢?這裡我們將窗口尺寸定義為2,這意味著目標單詞的左邊和右邊最近的2個單詞被視為上下文單詞。參見下面的圖3,可以看到,當窗口滑動時,語料庫中的每個單詞都會成為一個目標單詞。

手把手教你NumPy來實現Word2vec

圖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)中的每一個。每個窗口都由目標單詞及其上下文單片語成,分別用橙色和綠色高亮顯示。

手把手教你NumPy來實現Word2vec

圖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內部,我們進行以下操作:

  1. self.v_count: 辭彙表的長度(注意,辭彙表指的就是語料庫中不重複的單詞的數量)

  2. self.words_list: 在辭彙表中的單片語成的列表

  3. self.word_index: 以辭彙表中單詞為key,索引為value的字典數據

  4. self.index_word: 以索引為key,以辭彙表中單詞為value的字典數據

  5. for循環給用one-hot表示的每個目標詞和其的上下文詞添加到training_data中,one-hot編碼用的是word2onehot函數。

手把手教你NumPy來實現Word2vec

手把手教你NumPy來實現Word2vec

4.模型訓練

手把手教你NumPy來實現Word2vec

圖5,Word2Vec——skip-gram的網路結構

擁有了training_data,我們現在可以準備訓練模型了。訓練從w2v.train(training_data)開始,我們傳入訓練數據,並執行train函數。

Word2Vec2模型有兩個權重矩陣(w1和w2),為了展示,我們把值初始化到形狀分別為(9x10)和(10x9)的矩陣。這便於反向傳播誤差的計算,這部分將在後文討論。在實際的訓練中,你應該隨機初始化這些權重(比如使用np.random.uniform())。想要這麼做,把第九第十行注釋掉,把11和12行取消注釋就好。

手把手教你NumPy來實現Word2vec

訓練——向前傳遞

接下來,我們開始用第一組訓練樣本來訓練第一個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行)。

手把手教你NumPy來實現Word2vec

我附上一些截圖展示第一窗口(#1)中第一個訓練樣本的計算,其中目標詞是「natural」,上下文單詞是「language」和「processing」。可以在這裡查看Google Sheet中的公式。

手把手教你NumPy來實現Word2vec

圖6,計算隱藏層,輸出層和softmax

訓練——誤差,反向傳播和損失(loss)

誤差——對於y_pred、h 和u,我們繼續計算這組特定的目標詞和上下文詞的誤差。這是通過對y_pred 與在w_c 中的每個上下文詞之間的差的加合來實現的。

手把手教你NumPy來實現Word2vec

圖7,計算誤差——上下文單詞是「language」和「processing」

反向傳播——接下來,我們使用反向傳播函數backprop ,通過傳入誤差EI 、隱藏層h 和目標字w_t 的向量,來計算我們所需的權重調整量。

為了更新權重,我們將權重的調整量(dl_dw1 和dl_dw2 )與學習率相乘,然後從當前權重(w1 和w2 )中減去它。

手把手教你NumPy來實現Word2vec

圖8,反向傳播——計算W1和W2的增量

手把手教你NumPy來實現Word2vec

圖9,反向傳播——調整權重以得到更新後的W1和W2

手把手教你NumPy來實現Word2vec

損失——最後,根據損失函數計算出每個訓練樣本完成後的總損失。注意,損失函數包括兩個部分。第一部分是輸出層(在softmax之前)中所有元素的和的負數。第二部分是上下文單詞的數量乘以在輸出層中所有元素(在 exp之後)之和的對數。

手把手教你NumPy來實現Word2vec

圖10,Skip-gram的損失函數。

引用至:https://arxiv.org/pdf/1411.2738.pdf

5. 推論和總結(Inferencing)

既然我們已經完成了50個epoch的訓練,兩個權重(w1和w2)現在都準備好執行推論了。

獲取單詞的向量

有了一組訓練後的權重,我們可以做的第一件事是查看辭彙表中單詞的詞向量。我們可以簡單地通過查找單詞的索引來對訓練後的權重(w1)進行查找。在下面的示例中,我們查找單詞「machine」的向量。

手把手教你NumPy來實現Word2vec

> 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 。

手把手教你NumPy來實現Word2vec

> 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編碼樹)表示,其中輸出層中的節點表示為葉子,其節點由與其子節點的相應的概率表示。

手把手教你NumPy來實現Word2vec

圖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-pythonA very simple, bare-bones, inefficient, implementation of skip-gram word2vec from scratch with Python …github.com

Word2vec from Scratch with Python and NumPyTL;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 wordsThanks 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 tellingThanks 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 類資源千萬不能錯過
喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 雷鋒網 的精彩文章:

阿里巴巴在內蒙古旱區試水物聯網灌溉技術,一年省出1.5個西湖
華為如何繪製自動駕駛版圖?

TAG:雷鋒網 |