天天看點

word2vec原理&代碼 詳細全面總結

目錄

    • 一、統計語言模型---必備基礎知識
    • 二、詞的表示學習類型
      • 1. 獨熱編碼 one hot
      • 2. 分布式表示學習 distributed representation
    • 三、 word2vec
      • 1. CBOW
      • 2. skip gram
      • 訓練優化方法
    • 四、word2vec代碼實作
    • 詞嵌入技術的發展
    • 參考文獻

本文介紹word2vec必備基礎知識,原理,結構,模型訓練及代碼。

表示學習:将研究對象的語義資訊表示為稠密低維實值向量。在該低維向量空間中,2個對象距離越近則說明其語義相似度越高。

word2vec:2013年,由Google團隊提出。word2vec 是一種詞嵌入(word embedding)技術,即把一個詞語轉換成其對應的向量表達,進而友善計算機處理。word2vec是詞嵌入技術發展的重要裡程碑。

一、統計語言模型—必備基礎知識

統計語言模型(Statistical Language Model)是自然語言處理的基礎模型,是從機率統計角度出發,解決自然語言上下文相關的特性的數學模型。統計語言模型的核心就是判斷一個句子在文本中出現的機率。

假定 S S S表示某個有意義的句子,由一連串特定順序排列的詞 ( ω 1 , ω 2 , … , ω n ) (\omega_{1}, \omega _{2}, \ldots, \omega_{n}) (ω1​,ω2​,…,ωn​)組成, n n n是句子的長度。将 S S S在文本中出現機率表示為 P ( S ) P(S) P(S),則 P ( S ) = P ( ω 1 , ω 2 , … , ω n ) P(S)=P(\omega_{1}, \omega _{2}, \ldots, \omega_{n}) P(S)=P(ω1​,ω2​,…,ωn​)

利用條件機率公式:

P ( ω 1 , ω 2 , … , ω n ) = P ( ω 1 ) ⋅ P ( ω 1 ∣ ω 2 ) ⋅ P ( ω 3 ∣ ω 1 , ω 2 ) … P ( ω n ∣ ω 1 , ω 2 … , ω n − 1 ) P(\omega_{1}, \omega _{2}, \ldots, \omega_{n})=P(\omega_{1})\cdot P(\omega_{1}|\omega_{2})\cdot P(\omega_{3}|\omega_{1},\omega_{2}) \ldots P(\omega_{n}|\omega_{1},\omega_{2} \ldots, \omega_{n-1}) P(ω1​,ω2​,…,ωn​)=P(ω1​)⋅P(ω1​∣ω2​)⋅P(ω3​∣ω1​,ω2​)…P(ωn​∣ω1​,ω2​…,ωn−1​)

将序列的聯合機率轉化為一系列條件機率的乘積。那麼問題變成了如何去預測這些給定previous words下的條件機率 P ( ω 1 , ω 2 … , ω n − 1 ) P(\omega_{1},\omega_{2} \ldots, \omega_{n-1}) P(ω1​,ω2​…,ωn−1​)。

當 n n n太大時,上式難以計算。是以在此基礎上馬爾可夫提出了,一種馬爾可夫假設:假設一個詞出現的機率隻與前面N-1個詞相關。當N=2時(隻考慮前一個詞),就是簡單的二進制模型(Bigram Model),N=3(隻考慮前2個詞),三元模型(Trigram Model),當N=N時(考慮前N-1個詞),就是常說的N元模型(N-gram Model)。

二、詞的表示學習類型

1. 獨熱編碼 one hot

one hot 向量,次元大小為整個詞彙表的大小,對于每個具體的詞彙表中的詞,将對應的位置設定為1,其他位置為0。如,句子*(今年,夏天,超級,熱),詞語夏天*的one hot向量為(0,1,0,0).

獨熱編碼的缺點:稀疏(很多0)、孤立(無法表示出在語義層面上詞語之間的相關資訊)、高維(詞彙表很大時,詞向量高維)。

2. 分布式表示學習 distributed representation

這裡分布式可以了解為單看向量的某個次元沒有意義,但綜合起來就能表示詞的意義,解決了one hot存在的問題。例:某個詞的向量表示 [0.31343242, 0.65464122, 0.12343425, …, -1.324344]

詞的分布式表示主要可以分為三類:基于矩陣的分布表示、基于聚類的分布表示和基于神經網絡的分布表示。

接下來隻介紹word2vec,一種基于神經網絡的分布表示。

三、 word2vec

word2vec 包含兩個模型:跳字模型(skip-gram)和連續詞袋模型(continuous bag of words,簡稱CBOW);

兩種高效訓練的方法:負采樣(negative sampling)和層序softmax(hierarchical softmax)。

1. CBOW

CBOW 是一個三層神經網絡(是以word2vec稱為淺層模型,淺層向量表示)。如下圖所示,該模型的特點是輸入已知上下文,輸出對目前單詞的預測。

word2vec原理&代碼 詳細全面總結

輸入層

假設所有訓練資料所構成的詞典大小為 V V V。現在輸入資料為“今年夏天超級熱”,分詞後将每個詞表示為 V V V維的one hot向量,設某個詞的輸入向量/one hot向量表示為 x 1 ∗ V x_{1*V} x1∗V​。現假設根據上下文*“今年,超級,熱”來預測中心詞“夏天”*。

然後網絡初始化權重,該權重為一個矩陣表示為 W V ∗ N W_{V*N} WV∗N​,其中 N N N為詞向量的次元。該權重矩陣也稱為投影矩陣,投影到詞空間的矩陣。(多次疊代後的結果也就是我們要的詞向量矩陣)。

隐藏層

分别将輸入層的4個詞的向量*投影矩陣,即 x 1 ∗ V x_{1*V} x1∗V​ * W V ∗ N W_{V*N} WV∗N​,将這4個結果相加後求平均,即得到隐藏層的結果 h 1 ∗ N h_{1*N} h1∗N​。

然後将 h 1 ∗ N h_{1*N} h1∗N​乘以另一個投影矩陣 W N ∗ V ′ W'_{N*V} WN∗V′​,将結果傳播到輸出層。

輸出層

使用激活函數softmax,輸出結果one hot 向量 O 1 ∗ V O_{1*V} O1∗V​。将該one hot 向量與true label“夏天”的向量做比較,然後進行誤差反向傳播,不斷疊代優化權重矩陣,最終即得到我們要的詞向量矩陣(每行為一個詞所對應的詞向量,次元為N,V個詞就有V行)。

注意:這裡有兩個矩陣 W V ∗ N W_{V*N} WV∗N​和 W N ∗ V ′ W'_{N*V} WN∗V′​,一般使用前者作為我們的詞向量矩陣,也可以使用後者,或兩者相加求平均。

word2vec原理&代碼 詳細全面總結

目标為最大化對數似然函數

目标為使得每個 P ( ω i ∣ C o n t e x t ( ω i ) ) P(\omega_{i}|Context(\omega_{i})) P(ωi​∣Context(ωi​))最大,根據以上統計語言模型,可知道是相乘。這裡取對數可轉化為相加,友善計算。

word2vec原理&代碼 詳細全面總結

2. skip gram

CBOW 是一個三層神經網絡。如下圖所示,該模型的特點是已知目前詞語,預測上下文。訓練過程類似,這裡不再贅述。

word2vec原理&代碼 詳細全面總結

訓練優化方法

注意到,實際中詞彙表一般很大,那麼Word2Vec模型是一個超級大的神經網絡(權重矩陣規模非常大)。

兩種方法優化訓練過程:負采樣(negative sampling)和層序softmax(hierarchical softmax)

(1)負采樣

負采樣核心思想:不考慮出現機率低的詞,保證頻次越高的樣本越容易被采樣出來

(softmax分出來的類别數目是整個詞彙表的大小,那麼是否可以減小?)詳見word2vec的負采樣

(2)hierarchical softmax

hierarchical softmax的核心内容是将詞彙表中的每個詞使用哈夫曼樹(Huffman Tree)進行編碼,出現機率越高的符号使用較短的編碼(層次越淺),出現機率低的符号則使用較長的編碼(層次越深)。這樣做,可以使得最終是在做一層層的二分類。詳見基于hierarchical softmax的word2vec

四、word2vec代碼實作

#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
功能:使用word2vec将分詞後的資料進行文本向量化,儲存詞語的索引字典、詞向量,然後儲存為pkl檔案
word2vec:
	1.預訓練語料:已分詞的語料,輸入格式為txt(一行為一個樣本)或list of list
	2.建立一個空的模型對象;周遊一次語料庫建立詞典;第二次周遊語料庫建立并訓練模型(初始化詞向量,逐句的讀取一系列的詞,用梯度下降法更新詞向量)
	3.可儲存的:模型;根據語料得到的索引字典{索引數字: 單詞},詞向量(後兩者通過pickle庫儲存)
	4.模型評估:詞的向量表示,詞與詞之間的相似度,與某個詞最相近的前N個詞
"""

import pickle
import logging
import gensim
from gensim.models import Word2Vec, word2vec
from gensim.corpora.dictionary import Dictionary

addr = 'E:/word2vec/'

# 将日志輸出到控制台
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO) 


#==========================1.讀取預訓練語料=======================================
print("選擇分詞後的文本作為訓練語料...")
#這裡輸入資料格式為txt,一行為一個樣本
sentences = word2vec.LineSentence(r"E:/word2vec/seg_sent.txt")

#=====================2.訓練Word2vec模型(可修改參數)...====================
print('訓練Word2vec模型(可自定義參數)...')
model = Word2Vec(sentences,
                 size=100,  # 詞向量次元
                 min_count=5,  # 詞頻門檻值
                 window=5)  # 視窗大小
'''
參數解讀
#sg=1是skip—gram算法,對低頻詞敏感,預設sg=0為CBOW算法
#size是特征向量的次元。
#window是句子中目前詞與目标詞之間的最大距離,3表示在目标詞前看3-b個詞,後面看b個詞(b在0-3之間随機)
#min_count是對詞進行過濾,頻率小于min-count的單詞則會被忽視,預設值為5。
#negative和sample可根據訓練結果進行微調,sample表示更高頻率的詞被随機下采樣到所設定的門檻值,預設值為1e-3,
#negative: 如果>0,則會采用negativesamping,用于設定多少個noise words
#hs=1表示層級softmax将會被使用,預設hs=0且negative不為0,則負采樣将會被選擇使用。
#workers是線程數,此參數隻有在安裝了Cpython後才有效,否則隻能使用單核
'''
print(u"儲存w2v模型...")
model.save(addr + 'w2v_100.model')  # 儲存模型
print("儲存w2v模型的位置: ", addr + 'w2v_100.model', '\n')


'''
模型評估
print('計算與詞A的最近似的(前10個)詞:')
model.most_similar("wordA",top n=10))#計算與該詞最近似的詞,top n指定排名前n的詞

print('計算詞A和詞B的相似度:')
model.similarity("wordA","wordB") 

print('擷取詞A的詞向量:')
model ['wordA']
'''
#===================3.建立詞語字典,并傳回word2vec模型中詞語的索引,詞向量================
def create_dictionaries(p_model):
    gensim_dict = Dictionary()  # 建立詞語詞典
    gensim_dict.doc2bow(p_model.wv.vocab.keys(), allow_update=True)
    w2dict_index = {v: k + 1 for k, v in gensim_dict.items()}  # 詞語+索引。詞語的索引,從1開始編号
    w2index_dict = {k + 1: v for k, v in gensim_dict.items()}  # 索引+詞語。詞語的索引,從1開始編号
    w2vec = {word: p_model[word] for word in w2dict_index.keys()}  # 詞語的詞向量
    return w2dict_index, w2index_dict, w2vec


#====================4.從訓練好的模型中提取出索引字典、詞向量字典index_dict,==========================
dict_index, index_dict, word_vectors= create_dictionaries(model)
#注意,後續将詞向量輸入到模型中(如CNN)訓練時,實際輸入為每個詞/詞向量的索引

#===========================5.使用 pickle 存儲序列化資料 ====================================
#pickle是一個非常友善的庫 可以将py的字典、清單等等程式運作過程中的對象存儲為實體資料存儲為pkl檔案
print(u"儲存x_dict_indexpkl.pkl檔案...")
output = open(addr + "w2v_100.pkl", 'wb')
pickle.dump(dict_index, output)  # 索引字典,{單詞: 索引數字}
pickle.dump(index_dict, output)  #索引字典,{索引數字: 單詞}
pickle.dump(word_vectors, output)  # 詞向量字典
output.close()
print("儲存pkl檔案的位置: ", addr + "w2v_100.pkl", '\n')


if __name__ == "__main__":
    pass

           

詞嵌入技術的發展

word2vec之後其他詞嵌入技術:fastText,GloVe,BERT等

參考文獻

https://blog.csdn.net/yu5064/article/details/79601683

https://www.jianshu.com/p/d8bfaae28fa9

https://zhuanlan.zhihu.com/p/53194407

繼續閱讀