基於word2vec模型學習詞的語義向量表示,已在NLP許多任務中都發揮了重要的做用,接下來對詞向量學習中的hierarchical softmax的應用作一個分析和學習網絡
CBOW(Continuous Bag-of-Word)
One-word context
假設咱們vocabulary size 爲 V V V,hidden layer 神經元個數爲 N N N,假設咱們只有一個上下文單詞,則根據這個上下文單詞預測目標詞,相似於一個bigram model,以下圖所示:
輸入是一個one-hot編碼的vector(大小爲 V V V),假設只給定一個上下文word,對於輸入編碼, { x 1 , x 2 , . . . , x v } \{x_1, x_2,...,x_v\} {x1,x2,...,xv},只有一個爲1,其它都爲0。如上圖所示,第一層的參數權重 W V ∗ N W_{V*N} WV∗N, W W W中的每一行是一個 N N N維度的向量,表明的就是單詞 w w w的向量 v w v_w vw表示。
從hidden layer到output layer,也有一個不一樣的權重矩陣 W ′ = { w i j ′ } W^{'}=\{w_{ij}^{'}\} W′={wij′},是一個 N ∗ V N*V N∗V的矩陣,第 j j j列表明瞭詞 w j w_j wj的 N N N維度向量,用這個向量和hidden layer輸出向量相乘,就獲得在 V V V中的每一個詞的分值 u j u_j uj u j = v w j ′ T h (1) u_j = {v_{w_j}^{'}}^Th \text { { }(1)} uj=vwj′Th (1)
而後用softmax一個log 線性分類器,獲得每一個詞的分佈機率 p ( w j ∣ W I ) = y j = e x p ( u j ) ∑ j ′ = 1 V e x p ( u j ′ ) (2) p(w_j|W_I) = y_j = \frac{exp{(u_j)}}{\sum_{j^{'}=1}^Vexp{(u_j^{'})}} \text { { }(2)} p(wj∣WI)=yj=∑j′=1Vexp(uj′)exp(uj) (2)
其中 w I w_I wI是上下文單詞, w j w_j wj是目標詞, y j y_j yj是output layer層的第 j j j個神經元的輸出。
v w v_w vw和 v w ′ v_w^{'} vw′是詞 w w w的兩個詞向量表示, v w v_w vw來自input–>hidden的權重矩陣 W W W的行,而 v w ′ v_w^{'} vw′來自hidden—>output的權重矩陣 W ′ W^{'} W′的列,經過上面的語言模型,第一層權重 W W W就是咱們學習的詞向量矩陣。
首先讓咱們來看下從hidden—>output層的權重 W ′ W^{'} W′訓練過程更新公式,這裏就不作詳細推導了,若想詳細推導能夠參考論文:word2vec Parameter Learning Explained, ∂ E ∂ u j = y j − t j : = e j (3) \frac {\partial E}{\partial u_j}=y_j-t_j:=e_j \text { { }(3)} ∂uj∂E=yj−tj:=ej (3) ∂ E ∂ w i j ′ = ∂ E ∂ u j . ∂ u j ∂ w i j ′ = e j . h i (4) \frac {\partial E}{\partial w_{ij}^{'}} = \frac {\partial E}{\partial u_j}. \frac {\partial u_j} {\partial w_{ij}^{'}} = e_j.h_i \text { { }(4)} ∂wij′∂E=∂uj∂E.∂wij′∂uj=ej.hi (4) v w j ′ ( n e w ) = v w j ′ ( o l d ) − η . e j . h (5) {v_{w_j}^{'}}^{(new)} = {v_{w_j}^{'}}^{(old)} - \eta.e_j.h \text { { }(5)} vwj′(new)=vwj′(old)−η.ej.h (5)
其中E爲 loss function, e j e_j ej就是說的殘差, t j t_j tj就是真實label(不是1就是0)從公式(2)和(5)可知,更新最後一層的權重梯度, 咱們必須計算詞典 V V V中的每一個詞,當 V V V很大的時候,最後一層softmax計算量會很是的耗時
函數
Multi-word context
上面介紹了只有一個上下文詞的狀況,當多個上下文詞的狀況的時候,只須要將多個上下文詞的向量求平均做爲輸入就行,以下圖所示:
其中 h = 1 C W T ( x 1 + x 2 + . . . + x C ) h = \frac {1}{C} W^T(x_1+x_2+...+x_C) h=C1WT(x1+x2+...+xC)
學習
Skip-gram
skip-gram model訓練詞向量方式和cbow輸入和輸出目標反過來,cbow用上下文詞預測中心詞,而skip-gram 用中心詞預測上下文詞,以下圖所示:
理解了cbow模型,skip-gram也就不難理解了。有一點須要說明的是,由於一箇中心詞對應的上下文詞可能有多個,因此正樣本會有多個pair對<input_word,output_context1>,<input_word,output_context2>等,因此相同的語料,skip-gram比cbow訓練的要更久
優化
hierarchical softmax
從上面的公式(2)能夠看出,softmax分母那項歸一化,每次須要計算全部的V的輸出值,才能夠獲得當前j節點的輸出,當 V V V很大的時候, O ( V ) O(V) O(V)的計算代價會很是高。因此在訓練word2vec模型的時候,用到了兩個tricks,一個是negative sampling,每次只採少許的負樣本,不須要計算所有的 V V V;另一個是hierarchical softmax,經過構建赫夫曼tree來作層級softmax,從複雜度 O ( V ) O(V) O(V)下降到 O ( l o g 2 V ) O(log_2V) O(log2V),接下來重點來講明下hierachical softmax的原理以及怎麼訓練hierachical softmax網絡模型。編碼
赫夫曼tree
赫夫曼tree的講解和實現能夠參考個人另一篇博文:赫夫曼tree構建,這裏就再也不重複講解了。Hierarchical softmax怎麼經過赫夫曼tree來構建呢?以下圖所示:
首先對全部在 V V V詞表的詞,根據詞頻來構建赫夫曼tree,詞頻越大,路徑越短,編碼信息更少。tree中的全部的葉子節點構成了詞 V V V,中間節點則共有V-1個,上面的每一個葉子節點存在惟一的從根到該節點的path,如上圖所示, 詞 w 2 w_2 w2的path n ( w 2 , 1 ) , n ( w 2 , 2 ) , n ( w 3 , 3 ) {n(w_2,1), n(w_2,2), n(w_3,3)} n(w2,1),n(w2,2),n(w3,3),其中 n ( w , j ) n(w,j) n(w,j)表示詞 w w w的path的第 j j j個節點。
spa
葉子節點詞的機率表示
上圖假設咱們須要計算 w 2 w_2 w2的輸出機率,咱們定義從根節點開始,每次通過中間節點,作一個二分類任務(左邊或者右邊),因此咱們定義中間節點的 n n n左邊機率爲 p ( n , l e f t ) = σ ( v n ′ T . h ) p(n, left) = σ({v_n^{'}}^T.h) p(n,left)=σ(vn′T.h)
其中 v n ′ v_n^{'} vn′是中間節點的向量,那麼右邊機率 p ( n , r i g h t ) = 1 − σ ( v n ′ T . h ) = σ ( − v n ′ T . h ) p(n, right)=1-σ({v_n^{'}}^T.h)= σ(-{v_n^{'}}^T.h) p(n,right)=1−σ(vn′T.h)=σ(−vn′T.h) 從根節點到 w 2 w_2 w2,咱們能夠計算機率值爲:
p ( w 2 = w O ) = p(w_2=w_O) = p(w2=wO)=
p ( n ( w 2 , 1 ) , l e f t ) . p ( n ( w 2 , 2 ) , l e f t ) . p ( n ( w 3 , 3 ) , r i g h t ) p(n(w_2,1), left).p(n(w_2,2),left).p(n(w_3, 3), right) p(n(w2,1),left).p(n(w2,2),left).p(n(w3,3),right) = σ ( v n ( w 2 , 1 ) ′ T . h ) . σ ( v n ( w 2 , 2 ) ′ T . h ) . σ ( − v n ( w 3 , 3 ) ′ T . h ) =σ({v_{n(w_2,1)}^{'}}^T.h). σ({v_{n(w_2,2)}^{'}}^T.h). σ(-{v_{n(w_3,3)}^{'}}^T.h) =σ(vn(w2,1)′T.h).σ(vn(w2,2)′T.h).σ(−vn(w3,3)′T.h)
.net
其中 σ爲sigmoid函數視頻
各葉子節點機率值相加爲1
從論文裏講解的公式能夠推導得出 ∑ i = 1 V p ( w i = w O ) = 1 \sum_{i=1}^{V}p(w_i=w_O) = 1 i=1∑Vp(wi=wO)=1
爲了直觀的理解,咱們能夠簡單的計算一下,以下圖所示:
假設有三個詞 w 1 , w 2 , w 3 w_1,w_2,w_3 w1,w2,w3,因爲每一箇中間節點是一個二分類,則p(left)+p(right)=1,如上圖所示: w 3 = 0.4 w_3=0.4 w3=0.4, w 1 = 0.6 ∗ 0.5 = 0.3 , w 2 = 0.6 ∗ 0.5 = 0.3 w_1=0.6*0.5=0.3 , w_2=0.6*0.5=0.3 w1=0.6∗0.5=0.3,w2=0.6∗0.5=0.3,則: w 1 + w 2 + w 3 = 1 w_1+w_2+w_3=1 w1+w2+w3=1,或者用公式表示爲:
w 1 + w 2 + w 3 = l 2 ∗ l 3 + l 2 ∗ l 4 + l 1 = l 2 ( l 3 + l 4 ) + l 1 = l 2 ∗ 1 + l 1 = 1 w_1+w_2+w_3 = l_2*l_3+l2*l_4+l_1=l_2(l_3+l_4)+l_1=l_2*1+l_1=1 w1+w2+w3=l2∗l3+l2∗l4+l1=l2(l3+l4)+l1=l2∗1+l1=1
因此每次預測全部葉子節點的機率之和爲1,是一個分佈,與softmax一致
blog
怎麼訓練hierarchical softmax
借鑑Chris在youtube講解的視頻的圖片,根據赫夫曼編碼對每一個葉子節點進行了編碼,以下所示:
假設咱們的詞典有word [the, of ,respond, active, plutonium, ascetic, arbitrarily, chupacabra] 共8個單詞。接下對hierarchical softmax訓練作一個講解,主要步驟以下:
圖片
預處理:構建haffman樹
根據語料中的每一個word的詞頻構建赫夫曼tree,詞頻越高,則離樹根越近,路徑越短。如上圖所示,詞典 V V V中的每一個word都在葉子節點上,每一個word須要計算兩個信息:路徑(通過的每一箇中間節點)以及赫夫曼編碼,例如:
「respond」的路徑通過的節點是(6,4,3),編碼label是(1,0,0)
構建完赫夫曼tree後,每一個葉子節點都有惟一的路徑和編碼,hierarchical softmax與softmax不一樣的是,在hierarchical softmax中,不對 V V V中的word詞進行向量學習,而是對中間節點進行向量學習,而每一個葉子上的節點能夠經過路徑中通過的中間節點去表示。
模型的輸入
輸入部分,在cbow或者skip-gram模型,要麼是上下文word對應的id詞向量平均,要麼是中心詞對應的id向量,做爲hidden層的輸出向量
樣本label
不一樣softmax的是,每一個詞word對應的是一個 V V V大小的one-hot label,hierarchical softmax中每一個葉子節點word,對應的label是赫夫曼編碼,通常長度不超過 l o g 2 V log_2V log2V,在訓練的時候,每一個葉子節點的label統一編碼到一個固定的長度,不足的能夠進行pad
訓練過程
咱們用一個例子來描述,假如一個訓練樣本以下:
input | output |
---|---|
chupacabra [0,0,0,…,1,0,0,…] | active [1,0,1] |
假如咱們用skip-gram模型,則第一部分,根據"chupacabra" 詞的one-hot編碼乘以 W W W權重矩陣,獲得「chupachabra」的詞向量表示,也就是hidden的輸出,根據目標詞「active」從赫夫曼tree中獲得它的路徑path,即通過的節點(6,4,3),而這些中間節點的向量是模型參數須要學習的,共有 V − 1 V-1 V−1個向量,經過對應的節點id,取出相應的向量,假設是 w ′ ( 3 ∗ N ) w^{'}(3{*}N) w′(3∗N)(這裏設詞向量維度爲N),分別與hidden輸出向量相乘,再通過sigmoid函數,獲得一個3*1的score分值,與實際label [1,0,1], 經過以下公式:
p a t h _ s c o r e = t f . l o g ( t f . m u l t i p l y ( l a b e l , s c o r e ) + t f . m u l t i p l y ( 1 − l a b e l , 1 − s c o r e ) ) path\_score = tf.log(tf.multiply(label, score)+tf.multiply(1-label, 1-score)) path_score=tf.log(tf.multiply(label,score)+tf.multiply(1−label,1−score))
l o s s = t f . r e d u c e _ s u m ( t f . n e g a t i v e ( p a t h _ s c o r e ) ) loss = tf.reduce\_sum(tf.negative(path\_score)) loss=tf.reduce_sum(tf.negative(path_score))
t f . m u l t i p l y ( l a b e l , s c o r e ) tf.multiply(label, score) tf.multiply(label,score)點乘,是獲取該葉子節點路徑中走右邊各分支對應的機率分值, t f . m u l i t p l y ( 1 − l a b e l , 1 − s c o r e ) tf.mulitply(1-label, 1-score) tf.mulitply(1−label,1−score)獲取路徑中走左邊各分支對應的機率分值,相加就是該路徑[1,0,1]對應的[右分支—左分支—右分支]分別對應的機率分值 [ s c o r e 1 , s c o r e 2 , s c o r e 3 ] [score_1, score_2, score_3] [score1,score2,score3],因爲小於1的分值相乘,會容易溢出,則取log,乘法變加法,loss則是與咱們指望的機率值越大相反,須要取負用tf.negative實現,計算出loss,則就能夠用優化器來計算模型中各參數的梯度進行更新了
模型預測
訓練過程當中,每一個word咱們都提早知道了赫夫曼編碼,因此訓練的時候咱們平均大概只要計算 l o g 2 V log_2V log2V的中間節點,就能夠獲得結果。可是在預測階段,因爲咱們須要計算每一個葉子節點輸出機率值,而後取最大的一個機率,因此在預測階段並不能省時間。那麼怎麼計算每一個葉子節點也就是word的機率值?
從輸入層到hidden層,獲得輸出向量,而後分別計算與每一個葉子節點score分值。與訓練過程同樣,hidden向量與葉子節點的path通過的中間節點向量相乘,再sigmoid,而後用以下公式:
p a t h _ s c o r e = t f . l o g ( t f . m u l t i p l y ( l a b e l , s c o r e ) + t f . m u l t i p l y ( 1 − l a b e l , 1 − s c o r e ) ) path\_score =tf.log(tf.multiply(label, score)+tf.multiply(1-label, 1-score)) path_score=tf.log(tf.multiply(label,score)+tf.multiply(1−label,1−score))
s o c r e = t f . r e d u c e _ s u m ( p a t h s c o r e ) socre = tf.reduce\_sum(path_score) socre=tf.reduce_sum(pathscore)
計算全部葉子節點的score分值,取分值最大的則就是預測的word label