在進行自然語言處理中,需要對文章的中的語義進行分析,於是迫切需要一些模型去描述詞匯的含義,很多人可能都知道word2vector算法,誠然,word2vector是一個非常優秀的算法,並且被廣泛運用,為人們熟知,然而,從結果的優劣性來看,其實word2vector並非唯一的優秀方案,斯坦福大學提出的GloVe就是其中之一。今天我來為大家介紹一下GloVe模型,但是重點,還是放在實現上。
原論文:http://www.eecs.wsu.edu/~sji/classes/DL16/CNN-text/glove.pdf
簡單地說一下原理
這里的原理我主要參考了兩篇博客,感謝兩位優秀的博主。
前者會比較通俗,后者則比較深刻。
共現關系
和word2vector不同,GloVe更傾向於進行分析前后語境之間的共現關系,通過共現關系抽象出詞向量。
所謂的共現,共同出現,其實就是看一個詞有沒有在另一個詞的附近出現,所謂的附近,其實就是一個移動窗口的概念,定義窗口的半徑(從中心詞到邊緣的距離)后,看看方圓多少范圍內出現詞的個數,就是共現,現在看看例子。
假設語料庫就只有下面一行:
i love you but you love him i am sad
設半徑為2,於是移動窗口的滑動就有下面的形式:
以窗口5為例,此處就可以認為,love分別和but, you, him, i共同出現了一次,通過這種方式去計數,就能知道任意兩個詞之間的共現關系(一般是可逆的),構成共現矩陣X,一般地,X是一個對稱矩陣。
詞向量的產生
首先,模型的損失函數長這樣的:

vi和vj是詞匯i和j的詞向量,bi和bj是常數項,f是特定的權重函數,N是詞匯表大小。
這個損失函數怎么來的,我覺得上面的第一個鏈接講的非常清楚,看的時候注意一個核心,就是考慮兩個詞匯的共現關系與詞向量之間的關系(映射)盡可能接近,於是就構造了上面的損失函數。
GloVe的Python實現
在pypi里面看到了很多GloVe的包,但是很多都有坑,我直接說一個我自己已經走通的包mittens。
下載方式還是比較簡單的, pip install mittens
基本沒什么問題,想要去看看源碼的話,在這里:
一般而言GloVe按照計算共現矩陣和GloVe訓練兩大模塊,而mittens里面其實只提供了后者,前者還是需要自己寫,這是我寫的部分內容,給大家詳細講講(復雜度啥的基本沒做什么優化,歡迎提出一些意見)。
共現矩陣的計算
將之前事先說明一下,現在讀進來的數據,即代碼中的“data”變量,每行不是對應的單詞或者短語,而是已經對應在詞典中的該短語的index(自己構建詞典,一般設置為0-(N-1),N為詞典中詞語的個數),尤其在后面的cooccurrence的統計,即如果句子數組中的第i個詞語是詞典中的第j個詞,則句子向量中第i個位置就是數字j,這種方式對cooccurrence的統計非常方便。
# 構建空的詞表 coWindow = 3 # 共現窗口大小(半徑) tableSize = 1000 # 共現矩陣維度 cooccurrence = np.zeros((tableSize, tableSize), "int64" )
首先是數據初始化,這里不詳細說數據載入了,但是共現矩陣當然是需要初始化的(np是numpy別忘了)。
# 開始統計 flag = 0 for item in data: itemInt = [int(x) for x in item] for core in range(1, len(item)): if core <= coWindow + 1: # 左窗口不足 window = itemInt[1:core + coWindow + 1] coreIndex = core - 1 cooccurrence = countCOOC(cooccurrence, window, coreIndex) elif core >= len(item) - 1 - coWindow: # 右窗口不足 window = itemInt[core - coWindow:(len(item))] coreIndex = coWindow cooccurrence = countCOOC(cooccurrence, window, coreIndex) else: # 左右均沒有問題 window = itemInt[core - coWindow:core + coWindow + 1] coreIndex = coWindow cooccurrence = countCOOC(cooccurrence, window, coreIndex) flag = flag + 1 if flag % 1000 == 0: endTime = datetime.datetime.now() print("已經計算了%s條數據,用時%s" % (flag, endTime - startTime))
這一塊里面主要是為了設置移動窗口來進行挪動識別,具體統計移動窗口內部的共現,是在countCOOC函數里面做的。
def countCOOC(cooccurrence, window, coreIndex): # cooccurrence:當前共現矩陣 # window:當前移動窗口數組 # coreIndex:當前移動窗口數組中的窗口中心位置 for index in range(len(window)): if index == coreIndex: continue else: cooccurrence[window[coreIndex]][window[index]] = cooccurrence[window[coreIndex]][window[index]] + 1 return cooccurrence
countCOOC用來當前移動窗口的共現,一個一個計數即可。
GloVe的訓練
# 包的引入 from mittens import GloVe # 初始化模型 vecLength=100 # 矩陣長度 max_iter=100000 # 最大迭代次數 display_progress=1000 # 每次展示 glove_model = GloVe(n=vecLength, max_iter=max_iter, display_progress=display_progress) # 模型訓練與結果輸出 embeddings = glove_model.fit(coocMatric)
引入包之后,配置相應的參數,然后可以開始訓練,訓練完的返回值embeddings就是得到的詞向量詞典,通過詞向量詞典,就能夠將每篇文本的每一個單詞轉化為詞向量,從而進行進一步分析。
小結
GloVe終於寫完了,不知道大家覺得怎么樣,關於原理寫的人相對比較多,也理解的比我好我就不再解釋了,而代碼這塊,網上寫的不多,所以我寫得詳細一些,這也是我把結果寫出來的核心代碼,有什么問題我來回答,歡迎通過下面的聯系方式聯系我。
作者:機智的叉燒
鏈接:https://www.jianshu.com/p/d0cb367752e8
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯系作者獲得授權並注明出處。