情感分類——glove

序言

  • glove自己很早就看完過,但是一直沒有空看看源碼,今天趁前面分類模型實驗基本結束,抽空看看它的源碼。
  • glove理論講解
  • glove源碼

Why is it?

glove簡介

  • GloVe的全稱叫Global Vectors for Word Representation,它是一個基於全局詞頻統計(count-based & overall statistics)的詞表徵(word representation)工具
  • 請注意,在看glove之前,拋棄word2vec的神經網絡思想,glove裏沒有神經網絡!!
  • 代碼沒有像word2vec那樣需要手寫,所以沒有細看,大致瞭解了他的思想。

GloVe實現的

代碼邏輯

GloVe代碼包括了四個.c文件

  • 首先執行vocab_count.c這個文件的功能是掃一遍語料,建立一個字典。
  • 執行cooccur.c文件。它的功能是從語料中建立共現矩陣,GloVe是在共現矩陣上面進行訓練的。所謂共現矩陣,在GloVe中是大量的三元組<eat, food, 150>,<of, the, 100000>…cooccur.c文件生成的三元組的順序是根據詞頻排好序的。比如<of, the, 100000>在<eat, food, 150>前面,因爲of的頻數大於eat。<of, the, 100000>在<of, mine, 1000>前面,因爲the的頻數大於mine。
  • 執行第三個文件shuffle.c。這個文件是用來打亂之前生成的共現矩陣(也就是打亂三元組的順序)。
  • 最後執行glove.c。它會在打亂順序的三元組上面訓練詞向量。

具體代碼

  • vocab_count.c:
  • vocab_count的功能就是生成詞典。它的輸入是整個語料,它的輸出是詞典。詞典的形式是單詞以及單詞在語料中出現的次數(頻數)。詞典是按照頻數從高到低排好序的。

根據語料庫(corpus)構建一個共現矩陣(Co-ocurrence Matrix)

  • 常理來說,統計共現矩陣就是一個統計問題,但是如果內存不夠呢?
  • glove採取了一種很神奇的方式解決,具體可以看下面博文的解釋。(自lu不動了…)
  • GloVe源碼解析之cooccur

思路

  • 如何存儲共現矩陣?

  • 一個很直接簡單的方法就是用二維數組去存

  • 這麼做忽略了一個問題,就是共現矩陣太過稀疏,大多數的單詞從來沒有共現過,通過二維矩陣存非常浪費內存。實際上一般內存也根本存不下甚至中等規模大小的語料。所以用二維數據存儲共現矩陣,在大規模語料上面是不可行的。

  • 既然顯示的存儲共現矩陣不行,我們可以用稀疏矩陣的方式壓縮去存儲。掃描語料的過程中不斷地更新稀疏矩陣,從而得到最終的共現矩陣。

  • 所以現在我們的目標是,壓縮存儲稀疏矩陣!

  • 常見的方式是行壓縮和列壓縮,數據結構用數組,但glove並不是!

  • glove的思路是:首先不斷地從文件中讀取單詞對(pair),讀到內存寫滿爲止。對這些pair進行排序以及彙總,然後寫出到文件。以此類推,我們就能得到很多文件,每個文件裏面存的都是對部分的語料的共現矩陣。然後我們對這些共生矩陣再進行彙總。

  • 這種方法對內存更友好(當然,這是一種大數據思想)

  • 當然,最秒的還在後面:

  • 在GloVe代碼中,它使用了混合的數據結構去存儲共現矩陣。當我們的單詞按照它們的頻數排好序以後,比如the的id是1,of的id是2然後以此類推。我們會發現共現矩陣的左上角是非常稠密的。當一個pair中兩個單詞的id都很小(它們的頻數很大),他們很有可能共現。而共現矩陣的其它地方很稀疏。比如右下角,低頻詞不大可能共現。基於這個觀察,GloVe中使用二維矩陣去顯式的存儲共現矩陣的左上角,剩下的部分用我上段中提到的方法建立共現矩陣。所以,共現矩陣的左上角是一直在內存中的,剩餘部分會寫出最後再彙總。這裏再說一下什麼是左上角,GloVe中認爲word pair中兩個單詞的id的乘積小於某個閾值時算是在左上角( d越小,詞頻越高 )。閾值時100的時候,id爲9和id爲10的單詞組成的pair就算在共現矩陣的左上角,因爲9乘以10小於100.

  • 一種"內存讀取"的思路鋪面而來。

  • 最後,還有一個很秒的地方是:

  • 共現矩陣左上角以外的部分使用一個三元組去存儲的。分別是第一個單詞的id第二個單詞的id以及他們的共現次數。GloVe對共現次數的計算是隨着單詞之間距離遞減的,比如兩個單詞距離爲5,那麼算他們共現五分之一。所以共現次數是浮點型。建立共現矩陣自然也需要詞典,所以內存中會維護詞典。詞典中記錄單詞的字符串和id,不再記錄頻數,單詞的id是根據頻數來的,頻數越高id越小

  • 這簡直就是:在有限的內存情況下,儘可能壓榨代碼的效率和空間使用情況。

  • 代碼內的hash是爲了提高查找效率。

  • 第一階段:統計共現的策略就和上面說的一樣:循環讀取單詞,按照索引存入“顯存數組”還是三元組內,達到指定大小後,就把三元組寫到文件裏。

  • 第一階段完成,進入第二階段,對臨時文件進行彙總。利用的是最小堆思想:

  • 將overflow_0000.bin、0001.bin、0002.bin整理到一起,保存到cooccurrence.bin中。整合的時候,使用priority queue(最小堆,以詞在字典中的序號爲鍵,從小到大)來實現,從每個bin文件中讀取一條記錄插入到堆中,即堆中只保存一個bin文件的一條記錄。堆建成後,取出堆頂元素保存到變量old後調整堆,從old的來源的bin文件中再讀取一條記錄保存到new,並插入堆中。此後,開始迭代處理,比較堆頂元素與old是否標記了同樣兩個詞,如果是,則將堆頂元素合併到old中,否則,將old寫入到cooccurence.bin中,並將堆頂元素賦值給old,記錄堆頂元素的來源文件i,然後刪除堆頂元素並讀入i文件的下一條記錄,並加入到堆中;重複這樣的循環過程,直到所有的bin文件都讀完。再次注意:堆只保存了每個bin文件的一條記錄。

  • 構造loss function

訓練

  • 使用的是adagrad梯度下降

參考

[ 1 ]glove詳解

[ 2 ]GloVe源碼解析之cooccur

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章