白話word2vec

白話word2vec

背景介紹和一些直觀的理解

word2vec 是2012年被被Google提出來的將文本生成詞向量模型,其中包括了兩個模型,continous bag of words(CBOW)和Skip Gram。兩個模型分別從兩個角度來建立詞的預測模型,CBOW是通過一個或多個單詞的上下文來進行這個詞語的預測,而Skip Gram模型是通過一個或多個單詞來進行上下文的預測。

首先我們需要理解的一個問題,為什麼我們需要對詞進行編碼,也就是所謂的詞向量化?我們知道任何數學模型,其輸入都需要是數值型的,而在自然語言處理中,我們面對的是文字,而文字是無法直接被數學模型所直接利用的。所以我們需要將文字進行編碼,而編碼就是給每一個字元一個向量來進行表示。在word2vec出來之前,我們常用的主要是one hot encoding的方法,也就是對於每一個單詞,我們用在一個位置為1,其餘位置為0的向量進行表示。而向量的維度就是我們單詞量的大小。而向量的每一個位置,只能用來表示唯一的一個單詞。舉個例子,假設我們的有詞庫只有10個單詞,分別是:今,天,是,你,我,他,買,水,果,家。這裡我們分別用one hot encoding的方法來表示每一個詞,那麼有下面的結果:

[egin{array}{ccc} 今&
ightarrow&[1,0,0,0,0,0,0,0,0,0]\ 天&
ightarrow&[0,1,0,0,0,0,0,0,0,0]\ 是&
ightarrow&[0,0,1,0,0,0,0,0,0,0]\ 你&
ightarrow&[0,0,0,1,0,0,0,0,0,0]\ 我&
ightarrow&[0,0,0,0,1,0,0,0,0,0]\ 他&
ightarrow&[0,0,0,0,0,1,0,0,0,0]\ 買&
ightarrow&[0,0,0,0,0,0,1,0,0,0]\ 水&
ightarrow&[0,0,0,0,0,0,0,1,0,0]\ 果&
ightarrow&[0,0,0,0,0,0,0,0,1,0]\ 家&
ightarrow&[0,0,0,0,0,0,0,0,0,1]\ end{array}]

可以看到對於每一個單詞,我們用唯一的一個向量對它進行了表示。那麼很顯然這種表示方法至少有下面的一些缺陷

  1. 在常見的距離下面,單詞與單詞間的距離都是沒有差別的,「今」和「天」的距離和「今」和「果」的距離是一樣的
  2. 其次隨著單詞量的增加,向量的維度也隨之增加,而且對於詞庫中沒有的新詞,都無無法有唯一的向量與之一一對應
  3. 當單詞量較大時,也即向量的維度過高時,勢必加大了任何機器學習模型的計算量,降低了計算效率

    ......

所以,詞向量的提出目的就是解決上面提到的這些問題。而詞向量主要有以下一些特點

  1. 如果單詞量為N,那麼可以用一個n維的向量來表示每一個單詞,並且n遠遠小於N,常見的n為100到300,也可以更具具體和需求問題設定
  2. 詞向量每一個位置不再是只能取0和1的數值,而是可以取任意的實數
  3. 詞向量之間的差在一定程度上是有意義的,比如,中國的詞向量為 v_1 ,北京的詞向量為 v_2 , 美國的詞向量為 s_1 , 華盛頓的詞向量為 s_2 ,通過word2vec學習出來的這些詞向量大致有這樣的特徵 v_1 - v_2 approx s_1 - s_2 。這個是很漂亮的一個近似關係,相當於說 v_1 - v_2 近似的等於首都這種關係,也正是受到word2vec的啟發,在知識圖譜表示學習中,衍生了一些名為Trans的編碼演算法
  4. 除此之外,地名和地名在詞向量空間中的距離比地名和動物的詞向量距離近,等等,換句話說就是描述同一屬性和種類的詞向量的距離要小於不同屬性和種類的詞向量的距離

CBOW模型的介紹

前面也說了,CBOW模型是用於一個單詞的上下文來進行單詞的預測。換句話說,就是看了一個或多個單詞的上下文,我們希望能對詞庫中所有的單詞有個概率的預測,而我們想要預測的一個或多個單詞,它的概率要儘可能的大。這不正好就掉入了極大釋然估計的管轄範圍了嗎?如果我么有一大堆的上下文和要對應要預測的一個或多個單詞,既然我們的模型對於每一個或多個單詞都有一個概率的估計,那麼我們根據我們已有的樣本,把它們的概率全部乘起來,然後通過調整模型的參數最大化這個概率。

一個上下文預測一個單詞的情況

我們先來理解這種最簡單的惡情況,這個理解清楚了,推廣到其它複雜的情況其實是很直接和水到渠成的事情了。我們以兩個簡單的句子來作為例子:

  1. 「他 今天 很 開心」。
  2. 「她 好像 不 太 高興」

一般中文中訓練詞向量時,都會將句子進行分詞,上面我用空格將詞進行了分割,這裡不對分詞進行介紹。在CBOW模型中,有一個窗口參數,具體意思就是上下文的定義在距離這個單詞左右不超過窗口的距離。這裡我們窗口設置為2。那麼按照這個規則,我們可以生成下面的訓練樣本,第一個位置為上下文,第二單詞為中心辭彙

(今天,他),(很,他)

(他,今天),(很,今天),(開心,今天)

(他,很),(今天,很),(開心,很)

(今天,開心),(很,開心)

(好像,她),(不,她),

(她,好像),(不,好像),(太,好像),

(她, 不),(好像,不),(太,不),(高興,不),(好像,太),(不,太),(高興,太)

(不,高興),(太,高興)

在這次例子中,我們的單詞總量一共是九個(簡單器件就假設這為我們全部的單詞),分別為」他」,」今天」,」很」,」開心」,」她」,」好像」,」不」,」太」,」高興」,並且有下面的one hot encoding。

[egin{array}{lcc} 他&
ightarrow& [1,0,0,0,0,0,0,0,0]\ 今天&
ightarrow& [0,1,0,0,0,0,0,0,0]\ 很&
ightarrow& [0,0,1,0,0,0,0,0,0]\ 開心&
ightarrow& [0,0,0,1,0,0,0,0,0]\ 她&
ightarrow& [0,0,0,0,1,0,0,0,0]\ 好像&
ightarrow& [0,0,0,0,0,1,0,0,0]\ 不&
ightarrow& [0,0,0,0,0,0,1,0,0]\ 太&
ightarrow& [0,0,0,0,0,0,0,1,0]\ 高興&
ightarrow& [0,0,0,0,0,0,0,0,1]\ end{array} ]

假設我們最後需要的詞向量為維度為3的向量,那麼我們可以構建如下圖所示的全鏈接的三層的神經網路(9×3×9),最後一層嵌套一個softmax函數進行每個單詞概率的輸出。

三層全連接的神經網路

從輸入層到隱藏層的變換,我們可以用一個9×3的矩陣來表示,初始化為

[W^i = left[egin{array}{ccc} 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ end{array} 
ight] ]

從隱藏層到輸出層,我們可以用一個3×9的矩陣來表示,初始化為

[ W^o = left[ egin{array}{ccccccccc} 0.1&0.1&0.1&0.1&0.1&0.1&0.1&0.1&0.1\ 0.1&0.1&0.1&0.1&0.1&0.1&0.1&0.1&0.1\ 0.1&0.1&0.1&0.1&0.1&0.1&0.1&0.1&0.1\ end{array} 
ight] ]

有了上面的這些符號,那麼我們來看我們第一個樣本(今天,他)經過我們的模型變換會得到什麼樣的結果。因為今天= [0,1,0,0,0,0,0,0,0] ,那麼從輸入層到隱藏層,進行矩陣乘法

egin{equation} [0,1,0,0,0,0,0,0,0] 	imes left[egin{array}{ccc} 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ 0.1& 0.2& 0.3\ end{array} 
ight] = [0.1, 0.2, 0.3] end{equation}

[0.1,0.2,0.3] 就是「今天」在CBOW下的編碼,當然因為這裡只是初始化的值,後面模型會隨著樣本的訓練而調整這個數值,從而得到最後「今天」真正的編碼。

從隱藏層到輸出層,直接繼續進行矩陣的乘法,那麼有 [ [0.1, 0.2, 0.3] 	imes left[egin{array}{ccccccccc} 0.1&0.1&0.1&0.1&0.1&0.1&0.1&0.1&0.1\ 0.1&0.1&0.1&0.1&0.1&0.1&0.1&0.1&0.1\ 0.1&0.1&0.1&0.1&0.1&0.1&0.1&0.1&0.1\ end{array} 
ight] = 0.06 	imes [1,1,1,1,1,1,1,1,1] ]

在進行softmax的變化,我們得到了最後對於每個單詞的預測概率均為19,而我們的訓練樣本是希望「他」對應的概率要盡量的高,也就是」他」的概率要為1,其它的單詞概率為0,這樣模型的輸出和真實的樣本存在了偏差,那們我們就可以直接利用這個誤差來進行反向傳遞,調整我們模型的參數 W^iW^o ,從而達到了學習調優的目的。

如果我們再回頭看上面的計算流程,其實可以這樣理解上面的矩陣 W^i W^o 。對於每一個單詞v,我們給其賦予兩個編碼,分別記為 u_v^i u_v^o ,此處我們都默認向量都是列向量,那麼 u_v^i 構成了矩陣 W^i 中單詞 v 對應的那一行, u_v^o 對應了矩陣 W^o 中單詞 v 對應的那一列。而通過單詞 v 對其它單詞任意單詞 s 預測的概率通過下面的公式求得

[ frac{e^{<u_v^i,u_s^o>}}{sum_k e^{<u_v^i,u_k^o>}} ]

其中 <u_v^i,u_s^o> 為兩向量的內積,現在我們有一系列的觀測樣本, (x_j, y_j), j=1,cdots N ,那麼基於上面的分析,我們可以構建基於這些樣本的釋然函數,有

[ L(W^i, W^o) = prod_j frac{e^{<u_{x_j}^i,u_{y_j}^o>}}{sum_k e^{<u_{x_j}^i,u_k^o>}} ]

取對數後有

[ l(W^i, W^o) = sum_j <u_{x_j}^i,u_{y_j}^o> - logleft(sum_k e^{<u_{x_j}^i,u_k^o>}
ight) ]

所以最後我們其實就是在最大化釋然函數。為什麼說計算上面的釋然函數很複雜呢?大家可以看到對數裡面的求和項,其實需要對所有單詞進行一次遍歷,也就是說如果我們詞庫有1萬個單詞,那麼每一步迭代就需要計算一萬次,如果詞庫有一百萬,一千萬甚至是上億的單詞量呢?可想而知道,這個計算量是相噹噹大的。所以有一系列的方法來逼近這個計算,後面我們也會介紹hierarchical softmax和negative sampling的方法,他們是為解決這個計算效率問題的。

多個上下文預測一個單詞的情況

前面說了那麼個多一個上下文預測一個單詞的情況,接下來我們再講講怎麼擴展到多個上下文預測一個單詞,畢竟從直觀的角度來講,多個上下文預測中心辭彙絕對要比一個上下文預測中心辭彙要靠譜一些。

首先對於任意的單詞v,其上下文的單詞集合記為 C ,這裡上下文是距離中心單詞v一定距離的所有單詞的集合,而模型基於上下文對v的概率預測我們記為 P(v|C) ,為了求這個概率,我們可以這樣做一個轉換,對於每一個單詞 xin C ,我們先求得其在隱藏層的向量 u_x ,然後計算所有上下文單詞隱藏層向量的的平均值, u_C = frac{sum_x u_x}{|C|} ,對任意單詞 v 的預測概率,我們有下面的結果

[ frac{e^{<ar{u}_C,u_v^o>}}{sum_k e^{<ar{u}_C,u_k^o>}} ]

所以,如果我們有一系列的訓練樣本 (C_j, y_j), j=1, 2, cdots, N ,基於CBOW模型,我們可以得到下面的額釋然函數

[ L(W^i, W^o) = prod_j frac{e^{<u_{C_j}^i,u_{y_j}^o>}}{sum_k e^{<u_{C_j}^i,u_k^o>}} ]

其中 u_{C_j} = frac{sum_{xin C_j } u_x}{|C|}

Skip gram 模型

Skip gram 模型和CBOW完全是相反的出發角度,Skip gram 模型是通過中心單詞預測上下文,而Google提出的Word2Vec的論文中,也推薦使用這個方法來進行詞向量的學習。同樣我們也先理解一個中心單詞預測一個上下文的情況,然後擴展到一個單詞預測多個上下文的情況。

一個單詞預測一個上下文

這是最簡單的情況,在公式上面其實和CBOW模型一模一樣,唯一的區別就是訓練樣本從以前的(上下文單詞,中心辭彙)變成了(中心辭彙,上下文單詞)。同樣我們沿用之前的一些符號,而且也是一樣的有簡單的三層神經網路。假設我們有訓練樣本 (x_j, y_j), j=1,cdots N ,那麼基於上面的分析,我們可以構建基於這些樣本的釋然函數,有

L(W^i, W^o) = prod_j frac{e^{<u_{x_j}^i,u_{y_j}^o>}}{sum_k e^{<u_{x_j}^i,u_k^o>}}

取對數後有

l(W^i, W^o) = sum_j <u_{x_j}^i,u_{y_j}^o> - logleft(sum_k e^{<u_{x_j}^i,u_k^o>}
ight)

一個單詞預測多個上下文的情況

同樣沿用之前的一些符號,記中心單詞為 v ,上下文單集合為 C , 這次我們要預測的是上下文的概率 P(C|v) ,為了轉換成一個單詞預測一個上下文的情況,我們可以做獨立的假設,也就是有了下面的額公式

P(C|v) = prod_{v_xin C} P(v_x|v) = prod_{v_xin C} frac{e^{<u_v,u_{v_x}>}}{sum_k e^{<u_v,u_k>}}

上面第一個等式就是上下文的預測條件概率獨立的假設。同樣基於一些列的樣本, (C_j, x_j), j=1, 2 , cdots, N ,我們可以寫出其釋然函數

L(W^i, W^o) = prod_j P(C_j|v_{x_j})

一些思考

前面我們也提到了,上面兩種方法學習出來的編碼有一些很好的特徵,其中一個特徵就是詞性和語意相同的詞,他們的詞向量在詞向量空間中的距離比較近。這個通過Skip Gram模型來看,很好理解,比如有兩個詞, v_1v_2 ,他們的上下文都為 C ,而模型的第一步都是查找其隱藏層的編碼 u_{v_1}u_{v_2} ,後面計算 P(C|v_1)P(C|v_2) 的過程完全一樣,那麼理論上來講,在最優的情況下 u_{v_1}u_{v_2} 會最終一樣。如果他們的上下文不完全一樣,分別為 C_1 C_2 , 而兩者之間有大部分辭彙相似,那麼魔性最優的情況一定是 u_{v_1}u_{v_2} 的值比較接近,那樣 P(C_1|v_1)P(C_2|v_2) 才會在才在大多數情況下一致。

此外論文中推薦的額是使用Skip Gram模型,個人的理解估計是Skip Gram模型比較直接,而且實現起來架構幾乎不變,只需要把目標函數變為乘積的形式就行了(獨立的假設)。而CBOW在多個上下文預測一個中心詞的時候需要先對上下文的所有編碼進行均值的計算,這一點沒有Skip Gram模型那麼直接。其次可能是Skip Gram模型的結果在一些數據集上表現更好,解釋性更強的緣故。

詞向量編碼是近幾年很流行的方法,而現在任意的NLP的問題都會將文本進行詞向量編碼後再進行後續的建模和計算,往往效果都遠好於直接用one hot編碼。而受詞向量編碼思想的影響,網路編碼也在知識圖譜的表示和推斷方面衍生出了一些列的思想和方法。

文中有些符號估計有些混亂,還有hierarchical softmax和negative sampling也沒有敘述,後續會將這兩部分補上。無論如何,希望能對正在入門NLP和學習word2vec的你有些幫助。

參考資料

Distributed Representations of Words and Phrases and their Compositionality

word2vec Parameter Learning Explained

Word2Vec Tutorial - The Skip-Gram Model · Chris McCormick


推薦閱讀:

《AN EFFICIENT FRAMEWORK FOR LEARNING SENTENCE REPRESENTATIONS》閱讀筆記
利用知識圖譜讓你知道推薦系統在想什麼
2-3 Cost Function-Intuition I
python3機器學習經典實例-第八章解剖時間序列和時序數據29
TensorFlow.js發布:使用JS進行機器學習並在瀏覽器中運行

TAG:機器學習 | 自然語言處理 | 詞向量 |