【Hello NLP】CS224n學習筆記[3]:共現矩陣、SVD與GloVe詞向量

  • 2020 年 3 月 11 日
  • 筆記

相比於計算機視覺,NLP可能看起來沒有那麼有趣,這裡沒有酷炫的圖像識別、AI作畫、自動駕駛,我們要面對的,幾乎都是枯燥的文本、語言、文字。但是,對於人工智能的征途來說,NLP才是皇冠上的那顆珍珠,它美麗卻又難以摘取,當NLP的問題解決了,機器才真正具備了理解、思考的能力,我們才敢說實現了真正的「智能」。

SimpleAI 【HelloNLP】系列筆記,主要參考各知名網課(Stanford CS224n、DeepLearning.ai、李宏毅機器學習等等),並配合NLP的經典論文和研究成果、我的個人項目實踐經驗總結而成。希望能和各位NLP愛好者一起探索這顆AI皇冠的明珠!

CS224n筆記[3]:共現矩陣、SVD與GloVe詞向量

作者:郭必揚

在第一節(CS224n學習筆記[1]:詞向量從而何來中,我們討論了人們對詞語的幾種表示方法,有WordNet這樣的電子詞典法,還有one-hot這樣的離散表示法,後來我們介紹了Word2Vec詞向量這樣的低維分佈式表示法。實際上,還有另外一派做法。

基於共現矩陣的詞向量

我們再回顧一下Word2Vec的思想:

讓相鄰的詞的向量表示相似。

我們實際上還有一種更加簡單的思路——使用「詞語共現性」,來構建詞向量,也可以達到這樣的目的。即,我們直接統計哪些詞是經常一起出現的,那麼這些詞肯定就是相似的。那麼,每一個詞,都可以做一個這樣的統計,得到一個共現矩陣。這裡直接貼一個cs224n上的例子:

共現矩陣構造舉例(圖源自cs224n課程slides)

上面的例子中,給出了三句話,假設這就是我們全部的語料。我們使用一個size=1的窗口,對每句話依次進行滑動,相當於只統計緊鄰的詞。這樣就可以得到一個共現矩陣。

共現矩陣的每一列,自然可以當做這個詞的一個向量表示。這樣的表示明顯優於one-hot表示,因為它的每一維都有含義——共現次數,因此這樣的向量表示可以求詞語之間的相似度。

然後這樣表示還有有一些問題:

  • 維度=詞彙量大小,還是太大了;
  • 還是太過於稀疏,在做下游任務的時候依然不夠方便。

但是,維度問題,我們有解決方法——「SVD矩陣分解」!我們將巨大的共現矩陣進行SVD分解後,只選取最重要的幾個特徵值,得到每一個詞的低維表示。

SVD分解降維示意圖(圖源自http://www.imooc.com/article/267351)

圖中的A在我們的場景中就是共現矩陣,、、就是分解出的三個矩陣。我們只「選擇U矩陣的前r維來作為詞的向量表示」

上述的過程使用python編程十分簡單,這裡也是直接引用cs224n課程中的例子:

可見,即使這麼簡單的三句話構建的語料,我們通過構建共現矩陣、進行SVD降維、可視化,依然呈現出了類似Word2Vec的效果。

但是,由於共現矩陣巨大,SVD分解的計算代價也是很大的。另外,像a、the、is這種詞,與其他詞共現的次數太多,也會很影響效果。

所以,我們需要使用很多技巧,來改善這樣的詞向量。例如,直接把一些常見且意義不大的詞忽略掉;把極度不平衡的計數壓縮到一個範圍;使用皮爾森相關係數,來代替共現次數。等等很多技巧。因此就有了2005年的論文《An Improved Model of Semantic Similarity Based on Lexical Co-Occurrence》提出的COALS模型。這個模型訓練得到的詞向量,也表現出了很多有趣的性質,跟我們熟悉的Word2Vec十分類似。

基於共現矩陣的詞向量 vs. Word2Vec詞向量

上面的介紹中,我們發現基於共現矩陣的詞向量,也可以表現出很多優秀的性質,它也可以得到一個低維的向量表示,進行相似度的計算,甚至也可以做一定的推理(即存在man is to king as women is to queen這樣的關係)。但是,它主要的問題在於兩方面:

  1. SVD要分解一個巨型的稀疏矩陣(共現矩陣),計算開銷大,甚至無法計算;
  2. 需要進行複雜麻煩的預處理,例如計數的規範化、清除常見詞、使用皮爾森係數等等。

「Word2Vec」的算法,「不需要一次性處理這麼大量的數據」,而是通過「迭代」的方式,一批一批地進行處理,不斷迭代詞向量參數,使得我們可以處理海量的語料,構建十分穩健的詞向量。所以在實驗中,Word2Vec的表現,一般都要優於傳統的SVD類方法。

但是,「基於共現矩陣的方法也有其優勢」,那就是「充分利用了全局的統計信息」。因為我們進行矩陣分解,是對整個共現矩陣進行分解,這個矩陣中包含着全局的信息。而Word2Vec由於是一個窗口一個窗口(或幾個窗口)地進行參數的更新,所以學到的詞向量更多的是局部的信息。

總之,二者各有優劣,這啟發了斯坦福的一群研究者,GloVe詞向量就是在這樣的動機下產生的。

GloVe詞向量

GloVe是斯坦福團隊來2014年提出一個新的詞向量,GloVe的全名叫「Global Vectors」,重點在於這個global,即它是直接利用全局的統計信息進行訓練的。

理解GloVe詞向量,有兩種思路:

  1. 一種是由Word2Vec的skip-gram算法改進而來(思路較為清晰);
  2. 一種是由詞語見的「共現概率比」構造出來(過程較為複雜)。

這裡為了簡便,「我按照第一種思路來講解」

GloVe會用到全局的詞語之間共現的統計信息,因此我們需要首先構建「共現矩陣」,我們設:

  • 代表詞和詞共現的次數
  • 代表詞出現的次數
  • 代表詞出現在詞周圍的概率,即共現概率

回到skip-gram算法中,我們是這樣構造由中心詞預測上下文詞的概率的:

其中,v就代表詞向量(為了表示簡便,這裡也就使用一套詞向量)。

這樣,整體的損失函數可以寫為:

這些大家應該很熟悉了,在第一篇筆記的末尾有詳細的公式介紹。

實際上,對於上面的損失函數,我們可以有一種更加高效的計算方法,因為會出現次,所以我們不用一個窗口一個窗口慢慢地滑動計算,而是直接把這些重複的項一起計算:

上面可以根據可以進一步變形:

這個公式中的我們仔細定睛一看,就會發現,這就是「交叉熵」嘛!

交叉熵,只是眾多損失函數中的一種,而交叉熵損失函數天然有一些缺陷:由於它是處理兩個分佈,而很多分佈都具有「長尾」的性質,這使得基於交叉熵的模型常常會給那些不重要、很少出現的情形給予過高的權重。另外,由於我們需要計算概率,所以「必須進行合理的規範化」(normalization),規範化,就意味着要除以一個「複雜的分母」,像Softmax中,我們需要遍歷所有的詞彙來計算分母,這樣的開銷十分巨大。

所以,我們可以考慮使用一個新的損失函數,比如——「平方損失」(least squares)函數,則損失函數就變為:

其中,和其實就是「沒有經過規範化」的和。相當於我們把複雜的分母都一起丟掉了。「在平方損失中,我們可以不進行規範化處理」,因為我們處理的是兩者之間的差異,使差異最小化,那經不經過規範化,都不影響。但是在交叉熵中就必須進行規範化了,因為我們處理的是概率。

由於的取值範圍非常大,這樣會不容易優化,所以我們再進行取「對數處理」

最後,對於那個權重項,其實我們可以更加優化的用一個同時依賴於i,j的函數:來代替,來讓我們更精細地調整不同頻率的詞的損失。GloVe論文中f函數的圖像長這樣:

這樣的函數使得過於高頻的詞權重不會過高。

「千呼萬喚始出來」,我們終於得到了GloVe的損失函數:

(當然,其實還有一點不完整,那就是我們可以在內部再添加一些偏置項,bias term,但是這個不重要了)

GloVe詞向量好在哪?

上面詳細講述了GloVe詞向量如何通過改進Word2Vec的skip-gram算法得來。最主要的,就是我們把交叉熵損失函數替換成了平方損失函數。這樣,就明顯可以讓我們的計算更簡單。

另外,GloVe詞向量的訓練,是直接面對,即共現矩陣,進行優化的。也就是它是直接朝着全局的統計信息進行訓練優化的。這樣有什麼好處呢?

「a」 更充分的利用統計信息 「b」 充分利用語料中的大量重複信息來簡化計算

第二點怎麼理解?在Word2Vec中,我們是通過滑動窗口來進行計算的,我們在遍歷整個語料的過程中,同樣一對<中心詞i,上下文詞j>可能會出現在多個窗口中,這些計算我們都存在重複,而如果利用統計信息,我們可以只計算一次,然後乘以次數即可。

對於GloVe,模型的計算複雜度依賴於共現矩陣中非零元素的個數,其「上限」為,而skip-gram的複雜度為。其中V是詞彙量大小,C是語料庫的長度,一般情況下,.

但是,實際的語料中,一般是十分稀疏的,非零元素相比之下是很小的。經過對實際語料的研究,我們發現GloVe的實際複雜度大概為,顯然還是小於skip-gram的複雜度的。這也進一步印證了上的第二點好處。

以上。