PaperWeekly 第53期 | 更別緻的詞向量模型:Simpler GloVe - Part 2

作者丨蘇劍林

學校丨中山大學碩士生

研究方向丨NLP,神經網路

個人主頁丨kexue.fm

前言

本文作者在更別緻的詞向量模型:Simpler GloVe - Part 1一文中提出了一個新的類似 GloVe 的詞向量模型 — Simpler GloVe

本期我們將帶來該系列的後半部分,包括對該詞向量模型的詳細求解、結果展示,以及代碼和語料分享。

模型的求解

損失函數

現在,我們來定義 loss,以便把各個詞向量求解出來。用 P? 表示 P 的頻率估計值,那麼我們可以直接以下式為 loss:

相比之下,無論在參數量還是模型形式上,這個做法都比 GloVe 要簡單,因此稱之為 Simpler GloVe。GloVe模型是:

在 GloVe 模型中,對中心詞向量和上下文向量做了區分,然後最後模型建議輸出的是兩套詞向量的求和,據說這效果會更好,這是一個比較勉強的 trick,但也不是什麼毛病。最大的問題是參數 bi,b?j 也是可訓練的,這使得模型是嚴重不適定的。我們有:

這就是說,如果你有了一組解,那麼你將所有詞向量加上任意一個常數向量後,它還是一組解。這個問題就嚴重了,我們無法預估得到的是哪組解,一旦加上的是一個非常大的常向量,那麼各種度量都沒意義了(比如任意兩個詞的 cos 值都接近1)。

事實上,對 GloVe 生成的詞向量進行驗算就可以發現,GloVe 生成的詞向量,停用詞的模長遠大於一般詞的模長,也就是說一堆詞放在一起時,停用詞的作用還明顯些,這顯然是不利用後續模型的優化的。(雖然從目前的關於 GloVe 的實驗結果來看,是我強迫症了一些。)

互信息估算

為了求解模型,首先要解決的第一個問題就是 P(wi,wj),P(wi),P(wj) 該怎麼算呢?P(wi),P(wj) 簡單,直接統計估計就行了,但 P(wi,wj) 呢?怎樣的兩個詞才算是共現了?

當然,事實上不同的用途可以有不同的方案,比如我們可以認為同出現在一篇文章的兩個詞就是碰過一次面了,這種方案通常會對主題分類很有幫助,不過這種方案計算量太大。更常用的方案是選定一個固定的整數,記為 window,每個詞前後的 window 個詞,都認為是跟這個詞碰過面的。

一個值得留意的細節是:中心詞與自身的共現要不要算進去?窗口的定義應該是跟中心詞距離不超過 window 的詞,那麼應該要把它算上的,但如果算上,那沒什麼預測意義,因為這一項總是存在,如果不算上,那麼會降低了詞與自身的互信息。

所以我們採用了一個小 trick:不算入相同的共現項,讓模型自己把這個學出來。也就是說,哪怕上下文(除中心詞外)也出現了中心詞,也不算進 loss中,因為數據量本身是遠遠大於參數量的,所以這一項總可以學習出來。

權重和降採樣

GloVe 模型定義了如下的權重公式:

其中 Xij 代表詞對 (wi,wj) 的共現頻數,Xmax,α 是固定的常數,通常取 Xmax=100,α=3/4,也就是說,要對共現頻數低的詞對降權,它們更有可能是噪音,所以最後 GloVe 的 loss 是:

在本文的模型中,繼續沿用這一權重,但有所選擇。首先,對頻數作 α 次冪,相當於提高了低頻項的權重,這跟 word2vec 的做法基本一致。值得思考的是 min 這個截斷操作,如果進行這個截斷,那麼相當於大大降低了高頻詞的權重,有點像 word2vec 中的對高頻詞進行降採樣,能夠提升低頻詞的學習效果。

但可能帶來的後果是:高頻詞的模長沒學好。我們可以在《模長的含義》這一小節中看到這一點。總的來說,不同的場景有不同的需求,因此我們在最後發布的源碼中,允許用戶自定義是否截斷這個權重

Adagrad

跟 GloVe 一樣,我們同樣使用 Adagrad 演算法進行優化,使用 Adagrad 的原因是因為它大概是目前最簡單的自適應學習率的演算法。

但是,我發現 GloVe 源碼中的 Adagrad 演算法寫法是錯的。我不知道 GloVe 那樣寫是刻意的改進,還是筆誤(感覺也不大可能筆誤吧?)。

總之,如果我毫不改動它的迭代過程,照搬到本文的 Simpler GloVe 模型中,很容易就出現各種無解的 nan,如果寫成標準的 Adagrad,nan 就不會出現了。

選定一個詞對 wi,wj 我們得到 loss:

它的梯度是:

然後根據 Adagrad 演算法的公式進行更新即可,默認的初始學習率選為 η=0.1,迭代公式為:

根據公式可以看出,Adagrad 演算法基本上是對 loss 的縮放不敏感的,換句話說,將 loss 乘上 10 倍,最終的優化效果基本沒什麼變化,但如果在隨機梯度下降中,將 loss 乘上 10 倍,就等價於將學習率乘以 10 了。

有趣的結果

最後,我們來看一下詞向量模型(15)會有什麼性質,或者說,如此煞費苦心去構造一個新的詞向量模型,會得到什麼回報呢?

模長的含義

似乎所有的詞向量模型中,都很少會關心詞向量的模長。有趣的是,我們上述詞向量模型得到的詞向量,其模長還能在一定程度上代表著詞的重要程度。我們可以從兩個角度理解這個事實。

在一個窗口內的上下文,中心詞重複出現概率其實是不大的,是一個比較隨機的事件,因此可以粗略地認為:

所以根據我們的模型,就有:

所以:

可見,詞語越高頻(越有可能就是停用詞、虛詞等),對應的詞向量模長就越小,這就表明了這種詞向量的模長確實可以代表詞的重要性。事實上,?logP(w) 這個量類似 IDF,有個專門的名稱叫 ICF,請參考論文《TF-ICF: A New Term Weighting Scheme for Clustering Dynamic Data Streams》。

然後我們也可以從另一個角度來理解它,先把每個向量分解成模長和方向:

其中 |v| 模長是一個獨立參數,方向向量 v/‖v‖ 是 n?1 個獨立參數,n 是詞向量維度。由於參數量差別較大,因此在求解詞向量的時候,如果通過調整模長就能達到的,模型自然會選擇調整模長而不是拼死拼活調整方向。因此,我們有:

對於像「的」、「了」這些幾乎沒有意義的詞語,詞向量會往哪個方向發展呢?前面已經說了,它們的出現頻率很高,但本身幾乎沒有跟誰是固定搭配的,基本上就是自己周圍逛,所以可以認為對於任意詞 wi,都有

為了達到這個目的,最便捷的方法自然就是 ‖v的‖≈0 了,調整一個參數就可以達到,模型肯定樂意。也就是說對於頻數高但是互信息整體都小的詞語(這部分詞語通常沒有特別的意義),模長會自動接近於 0,所以我們說詞向量的模長能在一定程度上代表詞的重要程度。

在用本文的模型和百度百科語料訓練的一份詞向量中,不截斷權重,把詞向量按照模長升序排列,前 50 個的結果是:

可見這些詞確實是我們稱為「停用詞」或者「虛詞」的詞語,這就驗證了模長確實能代表詞本身的重要程度。這個結果與是否截斷權重有一定關係,因為截斷權重的話,得到的排序是:

兩個表的明顯區別是,在第二個表中,雖然也差不多是停用詞,但是一些更明顯的停用詞,如「的」、「是」等反而不在前面,這是因為它們的詞頻相當大,因此截斷造成的影響也更大,因此存在擬合不充分的可能性(簡單來說,更關注了低頻詞,對於高頻詞只是「言之有理即可」。)。

那為什麼句號和逗號也很高頻,它們又上榜了?因為一句話的一個窗口中,出現兩次句號「。」的概率遠小於出現兩次「的」的概率,因此句號「。」的使用更加符合我們上述推導的假設,而相應地,由於一個窗口也可能出現多次「的」,因此「的」與自身的互信息應該更大,所以模長也會偏大。

詞類比實驗

既然我們號稱詞類比性質就是本模型的定義,那麼該模型是否真的在詞類比中表現良好?我們來看一些例子。

這裡還想說明一點,詞類比實驗,有些看起來很漂亮,有些看起來不靠譜,但事實上,詞向量反映的是語料的統計規律,是客觀的。而恰恰相反,人類所定義的一些關係,反而才是不客觀的。

對於詞向量模型來說,詞相近就意味著它們具有相似的上下文分布,而不是我們人為去定義它相似。所以效果好不好,就看「相似的上下文分布 ? 詞相近」這一觀點(跟語料有關),跟人類對相近的定義(跟語料無關,人的主觀想法)有多大差別。當發現實驗效果不好時,不妨就往這個點想想。

相關詞排序

留意式(15),也就是兩個詞的互信息等於它們詞向量的內積。互信息越大,表明兩個詞成對出現的幾率越大,互信息越小,表明兩個詞幾乎不會在一起使用。因此,可以用內積排序來找給定詞的相關詞。

當然,內積是把模長也算進去了,而剛才我們說了模長代表的是詞的重要程度,如果我們不管重要程度,而是純粹地考慮詞義,那麼我們會把向量的範數歸一後再求內積,這樣的方案更加穩定:

根據概率論的知識,我們知道如果互信息為 0,也就是兩個詞的聯合概率剛好就是它們隨機組合的概率,這表明它們是無關的兩個詞。對應到式(15),也就是兩個詞的內積為 0。

而根據詞向量的知識,兩個向量的內積為 0,表明兩個向量是相互垂直的,而我們通常說兩個向量垂直,表明它們就是無關的。所以很巧妙,兩個詞統計上的無關,正好對應著幾何上的無關。這是模型形式上的美妙之一。

需要指出的是,前面已經提到,停用詞會傾向於縮小模長而非調整方向,所以它的方向就沒有什麼意義了,我們可以認為停用詞的方向是隨機的。這時候我們通過餘弦值來查找相關詞時,就有可能出現讓我們意外的停用詞了。

重新定義相似

注意上面我們說的是相關詞排序,相關詞跟相似詞不是一回事。比如「單身」、「凍成」都跟「狗」很相關,但是它們並不是近義詞;「科學」和「發展觀」也很相關,但它們也不是近義詞。

那麼如何找近義詞?事實上這個問題是本末倒置的,因為相似的定義是人為的,比如「喜歡」和「喜愛」相似,那「喜歡」和「討厭」呢?如果在一般的主題分類任務中它們應當是相似的,但是在情感分類任務中它們是相反的。再比如「跑」和「抓」,一般情況下我們認為它們不相似,但如果在詞性分類中它們是相似的,因為它們具有相同的詞性。

回歸到我們做詞向量模型的假設,就是詞的上下文分布來揭示詞義。所以說,兩個相近的詞語應該具有相近的上下文分布,前面我們討論的「機場-飛機+火車=火車站」也是基於同樣原理,但那裡要求了上下文單詞一一嚴格對應,而這裡只需要近似對應,條件有所放寬,而且為了適應不同層次的相似需求,這裡的上下文也可以由我們自行選擇。

具體來講,對於給定的兩個詞 wi,wj 以及對應的詞向量 vi,vj,我們要算它們的相似度,首先我們寫出它們與預先指定的 N 個詞的互信息,即:

和:

這裡的 N 是詞表中詞的總數。如果這兩個詞是相似的,那麼它們的上下文分布應該也相似,所以上述兩個序列應該具有線性相關性,所以我們不妨比較它們的皮爾遜積矩相關係數:

其中是 ?vi,vk?ˉ是?vi,vk? 的均值,即:

所以相關係數公式可以簡化為:

用矩陣的寫法(假設這裡的向量都是行向量),我們有:

方括弧這一塊又是什麼操作呢?事實上它就是:

也就是將詞向量減去均值後排成一個矩陣 V,然後算 V?V,這是一個 n×n 的實對稱矩陣,n 是詞向量維度,它可以分解(Cholesky分解)為:

其中 U 是 n×n 的實矩陣,所以相關係數的公式可以寫為:

我們發現,相似度還是用向量的餘弦值來衡量,只不過要經過矩陣 U 的變換之後再求餘弦值。

最後,該怎麼選擇這 N 個詞呢?我們可以按照詞頻降序排列,然後選擇前 N 個。

如果 N 選擇比較大(比如 N=10000),那麼得到的是一般場景下語義上的相關詞,也就是跟前一節的結果差不多;如果 N 選擇比較小,如 N=500,那麼得到的是語法上的相似詞,比如這時候「爬」跟「掏」、「撿」、「摸」都比較接近。

關鍵詞提取

所謂關鍵詞,就是能概括句子意思的詞語,也就是說只看關鍵詞也大概能猜出句子的整體內容。假設句子具有 k 個詞 w1,w2,…,wk,那麼關鍵詞應該要使得:

最大,說白了,就是用詞來猜句子的概率最大,而因為句子是預先給定的,因此 P(w1,w2,…,wk) 是常數,所以最大化上式左邊等價於最大化右邊。繼續使用樸素假設:

代入我們的詞向量模型,就得到:

所以最後等價於最大化:

現在問題就簡單了,進來一個句子,把所有詞的詞向量求和得到句向量,然後句向量跟句子中的每一個詞向量做一下內積(也可以考慮算 cos 得到歸一化的結果),降序排列即可。簡單粗暴,而且將原來應該是 ??(k2) 效率的演算法降到了 ??(k)。效果呢?下面是一些例子。

可以發現,哪怕是對於長句,這個方案還是挺靠譜的。值得注意的是,雖然簡單粗暴,但這種關鍵詞提取方案可不是每種詞向量都適用的,GloVe 詞向量就不行,因為它的停用詞模長更大,所以 GloVe 的結果剛剛是相反的:內積(或 cos)越小才越可能是關鍵詞。

句子的相似度

讓我們再看一例,這是很多讀者都會關心的句子相似度問題,事實上它跟關鍵詞提取是類似的。

兩個句子什麼時候是相似的甚至是語義等價的?簡單來說就是看了第一個句子我就能知道第二個句子說什麼了,反之亦然。這種情況下,兩個句子的相關度必然會很大。設句子 S1k 個詞w1,w2,…,wk,句子 S2l 個詞 wk+1,wk+2,…,wk+l,利用樸素假設得到:

代入我們的詞向量模型,得到:

所以最後等價於排序:

最終的結果也簡單,只需要將兩個句子的所有詞相加,得到各自的句向量,然後做一下內積(同樣的,也可以考慮用 cos 得到歸一化的結果),就得到了兩個句子的相關性了。

句向量

前面兩節都暗示了,通過直接對詞向量求和就可以得到句向量,那麼這種句向量質量如何呢?

我們做了個簡單的實驗,通過詞向量(不截斷版)求和得到的句向量+線性分類器(邏輯回歸),可以在情感分類問題上得到 81% 左右的準確率,如果中間再加一個隱層,結構為輸入 128(這是詞向量維度,句向量是詞向量的求和,自然也是同樣維度)、隱層 64(relu 激活)、輸出 1(而分類),可以得到 88% 左右的準確率。

相比之下,LSTM 的準確率是 90% 左右,可見這種句向量是可圈可點的。要知道,用於實驗的這份詞向量是用百度百科的語料訓練的,也就是說,本身是沒有體現情感傾向在裡邊的,但它依然成功地、簡明地挖掘了詞語的情感傾向。

同時,為了求證截斷與否對此向量質量的影響,我們用截斷版的詞向量重複上述實驗,結果是邏輯回歸最高準確率為 82%,同樣的三層神經網路,最高準確率為 89%,可見,截斷(也就是對高頻詞大大降權),確實能更好地捕捉語義。

代碼、分享與結語

代碼I

本文的實現位於:github.com/bojone/simpl

源碼修改自斯坦福的 GloVe 原版,筆者僅僅是小修改,因為主要的難度是在統計共現詞頻這裡,感謝斯坦福的前輩們提供了這一個經典的、優秀的統計實現案例。事實上,筆者不熟悉 C 語言,因此所作的修改可能難登大雅之台,萬望高手斧正。

此外,為了實現上一節的「有趣的結果」,在 github 中我還補充了 simpler_glove.py,裡邊封裝了一個類,可以直接讀取 C 版的 simpler glove 所導出的模型文件(txt 格式),並且附帶了一些常用函數,方便調用。

代碼II

這裡有一份利用本文的模型訓練好的中文詞向量,預料訓練自百科百科,共 100 萬篇文章,約 30w 詞,詞向量維度為 128。其中分詞時做了一個特殊的處理:把所有數字和英文都拆成單個的數字和字母了。如果需要實驗的朋友可以下載:

鏈接:pan.baidu.com/s/1jIb3yr

密碼:1ogw

結語

本文算是一次對詞向量模型比較完整的探索,也算是筆者的理論強迫症的結果,幸好最後也得到了一個理論上比較好看的模型,初步治癒了我這個強迫症。而至於實驗效果、應用等等,則有待日後進一步使用驗證了。

本文的大多數推導,都可以模仿地去解釋 word2vec 的 skip gram 模型的實驗結果,讀者可以嘗試。事實上,word2vec 的 skip gram 模型確實跟本文的模型有著類似的表現,包括詞向量的模型性質等。

總的來說,理論與實驗結合是一件很美妙的事情,當然,也是一件很辛苦的事情,因為就以上這些東西,就花了我幾個月思考時間。

關於PaperWeekly

PaperWeekly 是一個推薦、解讀、討論、報道人工智慧前沿論文成果的學術平台。如果你研究或從事 AI 領域,歡迎在公眾號後台點擊「交流群」,小助手將把你帶入 PaperWeekly 的交流群里。

微信公眾號:PaperWeekly

新浪微博:@PaperWeekly


推薦閱讀:

線下沙龍 x 北京 | NLP專題技術分享會,佔座全憑手速!
SEO詞庫清洗詞和歸類相關問題?
深度學習利器:TensorFlow與NLP模型

TAG:自然语言处理 |