不可思議的Word2Vec之係列四- 不一樣的“相似”
I. 相似度的定義
當用Word2Vec得到詞向量後,一般我們會用餘弦相似度來比較兩個詞的相似程度,定義為
有了這個相似度概念,我們既可以比較任意兩個詞之間的相似度,也可以找出跟給定詞最相近的詞語。這在gensim的Word2Vec中,由most_similar函數實現。
等等!我們很快給出了相似度的計算公式,可是我們居然!連相似都沒有定義,怎麼就得到了評估相似度的數學公式了呢?
要注意,這不是一個可以隨意忽略的問題。很多時候我們都不知道我們幹的是什麼,就直接去幹了。好比上一篇文章說到提取關鍵詞,相信很多人都未曾想過,什麼是關鍵詞,難道就僅僅說關鍵詞就是很“關鍵”的詞?而如果想到,關鍵詞就是用來估計文章大概講什麼的,這樣我們就得到一種很自然的關鍵詞定義
進而可以用各種方法對它建模。
回到本文的主題來,相似度怎麼定義呢?答案是:看場景定義所需要的相似。
那麼,通過餘弦相似度給出的相似,又是什麼情況呢?事實上,Word2Vec本質上來說,還是使用上下文的平均分布描述當前詞(因為Word2Vec是不考慮詞序的),而餘弦值與向量模長沒關係,因此它描述的是“相對一致”。那麼,餘弦相似度大,事實上意味著,或者更粗糙講,那就是在同一句話中,兩個詞具有可替換性。比如,“廣州”最相近的詞語是“東莞”、“深圳”,那是因為很多場景下,直接將矩陣中的“廣州”直接換成“東莞”、“深圳”,這個句子還是合理的(是句子本身的合理,但這個句子不一定是事實,比如“廣州是廣東的省會”,變成“東莞是廣東的省會”,這個句子是合理的,但是這個句子並非是事實)。
>>> s = u'廣州'
>>> pd.Series(model.most_similar(s))
0 (東莞, 0.840889930725)
1 (深圳, 0.799216389656)
2 (佛山, 0.786817014217)
3 (惠州, 0.779960155487)
4 (珠海, 0.735232532024)
5 (廈門, 0.725090026855)
6 (武漢, 0.724122405052)
7 (汕頭, 0.719602525234)
8 (增城, 0.713532149792)
9 (上海, 0.710560560226)
II. 相關:另一種相似
前麵已經說了,相似度的定義事實上要看場景的,餘弦相似度隻是其中之一。有時候我們會覺得“東莞”和“廣州”壓根就沒聯係,對於“老廣州”來說,“白雲山”、“白雲機場”、“廣州塔”這些詞才是跟“廣州”最相似的,這種場景也是很常見的,比如做旅遊的推薦,旅遊來到廣州後,自然是希望輸入“廣州”後,自動輸出來“白雲山”、“白雲機場”、“廣州塔”這些廣州相關的詞語,而不是輸出“東莞”、“深圳”這些詞語。
這種“相似”,準確來說是“相關”,應該怎麼描述呢?答案是互信息,定義為
互信息越大,說明x,yx,y兩個詞經常一起出現。
這樣,在給定詞xx的情況下,我們就可以找出經常跟詞xx一起出現的詞,這個也完全可以由Word2Vec中的Skip-Gram+Huffman Softmax模型來完成。代碼如下
import numpy as np
import gensim
model = gensim.models.word2vec.Word2Vec.load('word2vec_wx')
def predict_proba(oword, iword):
iword_vec = model[iword]
oword = model.wv.vocab[oword]
oword_l = model.syn1[oword.point].T
dot = np.dot(iword_vec, oword_l)
lprob = -sum(np.logaddexp(0, -dot) + oword.code*dot)
return lprob
from collections import Counter
def relative_words(word):
r = {i:predict_proba(i, word)-np.log(j.count) for i,j in model.wv.vocab.iteritems()}
return Counter(r).most_common()
這時候,“廣州”的相關詞為
>>> s = u'廣州'
>>> w = relative_words(s)
>>> pd.Series(w)
0 (福中路, -17.390365773)
1 (OHG, -17.4582544641)
2 (林寨鎮, -17.6119545612)
3 (坪山街道, -17.6462214199)
4 (東圃鎮, -17.6648893759)
5 (西翼, -17.6796614955)
6 (北京西, -17.6898282385)
7 (⇋, -17.6950761384)
8 (K1019, -17.7259853233)
9 (景泰街道, -17.7292421556)
10 (PSW3, -17.7296432222)
11 (廣州鐵路職業技術學院, -17.732288911)
12 (13A06, -17.7382891287)
13 (5872, -17.7404719442)
14 (13816217517, -17.7650583156)
15 (未遂案, -17.7713452536)
16 (增城市, -17.7713832873)
17 (第十甫路, -17.7727940473)
18 (廣州白雲機場, -17.7897457043)
19 (Faust, -17.7956389314)
20 (國家檔案館, -17.7971039916)
21 (w0766fc, -17.8051687721)
22 (K1020, -17.8106548248)
23 (陳寶琛, -17.8427718407)
24 (jinriGD, -17.8647825023)
25 (3602114109100031646, -17.8729896156)
可以發現,得到的結果基本上都是跟廣州緊密相關的。當然,有時候我們稍微強調一下高頻詞,因此,可以考慮將互信息公式修改為
其中αα是一個略小於1的常數。如果取α=0.9α=0.9,那麼有
from collections import Counter
def relative_words(word):
r = {i:predict_proba(i, word)-0.9*np.log(j.count) for i,j in model.wv.vocab.iteritems()}
return Counter(r).most_common()
結果重新排列如下:
>>> s = u'廣州'
>>> w = relative_words(s)
>>> pd.Series(w)
0 (福中路, -16.8342976099)
1 (北京西, -16.9316053191)
2 (OHG, -16.9532688634)
3 (西翼, -17.0521852934)
4 (增城市, -17.0523156839)
5 (廣州白雲機場, -17.0557270208)
6 (林寨鎮, -17.0867272184)
7 (⇋, -17.1061883426)
8 (坪山街道, -17.1485480457)
9 (5872, -17.1627067119)
10 (東圃鎮, -17.192150594)
11 (PSW3, -17.2013228493)
12 (Faust, -17.2178736991)
13 (紅粉, -17.2191157626)
14 (國家檔案館, -17.2218467278)
15 (未遂案, -17.2220391092)
16 (景泰街道, -17.2336594498)
17 (光孝寺, -17.2781121397)
18 (國際貨運代理, -17.2810157155)
19 (第十甫路, -17.2837591345)
20 (廣州鐵路職業技術學院, -17.2953441257)
21 (芳村, -17.301106775)
22 (檢測院, -17.3041253252)
23 (K1019, -17.3085465963)
24 (陳寶琛, -17.3134413583)
25 (林和西, -17.3150577006)
相對來說,後麵這個結果更加可讀一點。另外的一些結果,展示如下:
>>> s = u'飛機'
>>> w = relative_words(s)
>>> pd.Series(w)
0 (澳門國際機場, -16.5502216186)
1 (HawkT1, -16.6055740672)
2 (架飛機, -16.6105400944)
3 (地勤人員, -16.6764712234)
4 (美陸軍, -16.6781627384)
5 (SU200, -16.6842796275)
6 (起降, -16.6910345896)
7 (上海浦東國際機場, -16.7040362134)
8 (備降, -16.7232609719)
9 (第一架, -16.7304077856)>>> pd.Series(model.most_similar(s))
0 (起飛, 0.771412968636)
1 (客機, 0.758365988731)
2 (直升機, 0.755871891975)
3 (一架, 0.749522089958)
4 (起降, 0.726713418961)
5 (降落, 0.723304390907)
6 (架飛機, 0.722024559975)
7 (飛行, 0.700125515461)
8 (波音, 0.697083711624)
9 (噴氣式飛機, 0.696866035461)>>> s = u'自行車'
>>> w = relative_words(s)
>>> pd.Series(w)
0 (騎, -16.4410312554)
1 (放風箏, -16.6607225423)
2 (助力車, -16.8390451582)
3 (自行車, -16.900188791)
4 (三輪車, -17.1053629907)
5 (租賃點, -17.1599389605)
6 (電動車, -17.2038996636)
7 (助動車, -17.2523149342)
8 (多輛, -17.2629832083)
9 (CRV, -17.2856425014)>>> pd.Series(model.most_similar(s))
0 (摩托車, 0.737690329552)
1 (騎, 0.721182465553)
2 (滑板車, 0.7102201581)
3 (電動車, 0.700758457184)
4 (山地車, 0.687280654907)
5 (騎行, 0.666575074196)
6 (單車, 0.651858925819)
7 (騎單車, 0.650207400322)
8 (助力車, 0.635745406151)
9 (三輪車, 0.630989730358)
大家可以自己嚐試。要說明的是:很遺憾,Huffman Softmax雖然在訓練階段加速計算,但在預測階段,當需要遍曆一遍詞典時,事實上它比原生的Softmax還要慢,所以這並不是一個高效率的方案。
III. 究竟做了啥
根據前麵兩部分,我們可以看到,“相似”一般有兩種情景:1、經常跟同一批詞語搭配出現;2、經常一起出現。這兩種情景,我們都可以認為是詞語之間的相似,適用於不同的需求。
比如,在做多義詞的詞義推斷時,比如star是“恒星”還是“明星”,就可以利用互信息。我們可以事先找到star意思為“恒星”的時候的語料,找出與star互信息比較大的的詞語,這些詞語可能有sun、planet、earth,類似地,可以找到star為“明星”的時候的語料,找出與star互信息比較大的詞語,這些詞語可能有entertainment、movie等。到了新的語境,我們就可以根據上下文,來推斷究竟是哪個詞義。
總而言之,需要明確自己的需求,然後再考慮對應的方法。
最後更新:2017-09-17 22:34:42