一文詳解 Word2vec 之 Skip-Gram 模型(實現篇)

Posted by

雷鋒網按:這是一個關於 Skip-Gram 模型的系列教程,共分為結構、訓練和實現三個部分,本文為最後一部分:實現篇。原文作者天雨粟,原載於作者知乎專欄,雷鋒網已獲授權。

前言

上一篇的專欄介紹了Word2Vec中的Skip-Gram模型的結構和訓練,如果看過的小夥伴可以直接開始動手用TensorFlow實現自己的Word2Vec模型,本篇文章將利用TensorFlow來完成Skip-Gram模型。還不是很了解Skip-Gram思想的小夥伴可以先看一下上一篇的專欄內容。

本篇實戰代碼的目的主要是加深對Skip-Gram模型中一些思想和trick的理解。由於受限於語料規模、語料質量、算法細節以及訓練成本的原因,訓練出的結果顯然是無法跟gensim封裝的Word2Vec相比的,本代碼適合新手去理解與練習Skip-Gram模型的思想。

工具介紹

● 語言:Python 3

● 包:TensorFlow(1.0版本)及其它數據處理包(見代碼中)

● 編輯器:jupyter notebook

● 線上GPU:floyd ()

● 數據集:經過預處理後的維基百科文章(英文)

正文部分

文章主要包括以下四個部分進行代碼構造:

– 數據預處理

– 訓練樣本構建

– 模型構建

– 模型驗證

1 數據預處理

關於導入包和加載數據在這裡就不寫了,比較簡單,請參考git上的代碼。

數據預處理部分主要包括:

  • 替換文本中特殊符號並去除低頻詞
  • 對文本分詞
  • 構建語料
  • 單詞映射表

首先我們定義一個函數來完成前兩步,即對文本的清洗和分詞操作。

上面的函數實現了替換標點及刪除低頻詞操作,返回分詞後的文本。

下面讓我們來看看經過清洗後的數據:

有了分詞後的文本,就可以構建我們的映射表,代碼就不再贅述,大家應該都比較熟悉。

我們還可以看一下文本和詞典的規模大小:

整個文本中單詞大約為1660萬的規模,詞典大小為6萬左右,這個規模對於訓練好的詞向量其實是不夠的,但可以訓練出一個稍微還可以的模型。

2 訓練樣本構建

我們知道skip-gram中,訓練樣本的形式是(input word, output word),其中output word是input word的上下文。為了減少模型噪音並加速訓練速度,我們在構造batch之前要對樣本進行採樣,剔除停用詞等噪音因素。

採樣

在建模過程中,訓練文本中會出現很多「the」、「a」之類的常用詞(也叫停用詞),這些詞對於我們的訓練會帶來很多噪音。在上一篇Word2Vec中提過對樣本進行抽樣,剔除高頻的停用詞來減少模型的噪音,並加速訓練。

我們採用以下公式來計算每個單詞被刪除的機率大小:

i 的出現頻次。t為一個閾值,一般介於1e-3到1e-5之間。

上面的代碼計算了樣本中每個單詞被刪除的機率,並基於機率進行了採樣,現在我們手裡就拿到了採樣過的單詞列表。

構造batch

我們先來分析一下skip-gram的樣本格式。skip-gram不同於CBOW,CBOW是基於上下文預測當前input word。而skip-gram則是基於一個input word來預測上下文,因此一個input word會對應多個上下文。我們來舉個栗子「The quick brown fox jumps over lazy dog」,如果我們固定skip_window=2的話,那麼fox的上下文就是[quick, brown, jumps, over],如果我們的batch_size=1的話,那麼實際上一個batch中有四個訓練樣本。

上面的分析轉換為代碼就是兩個步驟,第一個是找到每個input word的上下文,第二個就是基於上下文構建batch。

首先是找到input word的上下文單詞列表:

我們定義了一個get_targets函數,接收一個單詞索引號,基於這個索引號去查找單詞表中對應的上下文(默認window_size=5)。請注意這裡有一個小trick,我在實際選擇input word上下文時,使用的窗口大小是一個介於[1, window_size]區間的隨機數。這裡的目的是讓模型更多地去關注離input word更近詞。

我們有了上面的函數後,就能夠輕鬆地通過input word找到它的上下文單詞。有了這些單詞我們就可以構建我們的batch來進行訓練:

注意上面的代碼對batch的處理。我們知道對於每個input word來說,有多個output word(上下文)。例如我們的輸入是「fox」,上下文是[quick, brown, jumps, over],那麼fox這一個batch中就有四個訓練樣本[fox, quick], [fox, brown], [fox, jumps], [fox, over]。

3 模型構建

數據預處理結束後,就需要來構建我們的模型。在模型中為了加速訓練並提高詞向量的質量,我們採用負採樣方式進行權重更新。

輸入層到嵌入層

輸入層到隱層的權重矩陣作為嵌入層要給定其維度,一般embeding_size設置為50-300之間。

嵌入層的 lookup 通過 TensorFlow 中的 embedding_lookup 實現,詳見:

嵌入層到輸出層

在skip-gram中,每個input word的多個上下文單詞實際上是共享一個權重矩陣,我們將每個(input word, output word)訓練樣本來作為我們的輸入。為了加速訓練並且提高詞向量的質量,我們採用negative sampling的方法來進行權重更新。

TensorFlow中的sampled_softmax_loss,由於進行了negative sampling,所以實際上我們會低估模型的訓練loss。詳見:

請注意代碼中的softmax_w的維度是vocab_size x embedding_size,這是因為TensorFlow中的sampled_softmax_loss中參數weights的size是[num_classes, dim]。

4 模型驗證

在上面的步驟中,我們已經將模型的框架搭建出來,下面就讓我們來訓練訓練一下模型。為了能夠更加直觀地觀察訓練每個階段的情況。我們來挑選幾個詞,看看在訓練過程中它們的相似詞是怎麼變化的。

訓練模型:

在這裡注意一下,儘量不要經常去讓代碼列印驗證集相似的詞,因為這裡會多了一步計算步驟,就是計算相似度,會非常消耗計算資源,計算過程也很慢。所以代碼中我設置1000輪列印一次結果。

從最後的訓練結果來看,模型還是學到了一些常見詞的語義,比如one等計數詞以及gold之類的金屬詞,animals中的相似詞也相對準確。

上面的圖中通過TSNE將高維的詞向量按照距離遠近顯示在二維坐標系中,該圖已經在git庫中,想看原圖的小夥伴去git看~

我們來看一下細節:

上面是顯示了整張大圖的局部區域,可以看到效果還不錯。

關於提升效果的技巧:

  • 增大訓練樣本,語料庫越大,模型學習的可學習的信息會越多。
  • 增加window size,可以獲得更多的上下文信息。
  • 增加embedding size可以減少信息的維度損失,但也不宜過大,我一般常用的規模為50-300。

附錄:

git代碼中還提供了中文的詞向量計算代碼。同時提供了中文的一個訓練語料,語料是我從某招聘網站上爬取的招聘數據,做了分詞和去除停用詞的操作(可從git獲取),但語料規模太小,訓練效果並不好。

上面是我用模型訓練的中文數據,可以看到有一部分語義被挖掘出來,比如word和excel、office很接近,ppt和project、文字處理等,以及邏輯思維與語言表達等,但整體上效果還是很差。一方面是由於語料的規模太小(只有70兆的語料),另一方面是模型也沒有去調參。如果有興趣的同學可以自己試下會不會有更好的效果。

Related Post

廣告

Comments

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料