不可思議的Word2Vec之係列三-提取關鍵詞
說到提取關鍵詞,一般會想到TF-IDF和TextRank,大家是否想過,Word2Vec還可以用來提取關鍵詞?而且,用Word2Vec提取關鍵詞,已經初步含有了語義上的理解,而不僅僅是簡單的統計了,而且還是無監督的!
I. 什麼是關鍵詞?
誠然,TF-IDF和TextRank是兩種提取關鍵詞的很經典的算法,它們都有一定的合理性,但問題是,,估計很難能夠從零把它們構造出來。也就是說,這兩種算法雖然看上去簡單,但並不容易想到。試想一下,沒有學過信息相關理論的同學,估計怎麼也難以理解為什麼IDF要取一個對數?為什麼不是其他函數?又有多少讀者會破天荒地想到,用PageRank的思路,去判斷一個詞的重要性?
說到底,問題就在於:提取關鍵詞和文本摘要,看上去都是一個很自然的任務,有誰真正思考過,關鍵詞的定義是什麼?這裏不是要你去查漢語詞典,獲得一大堆文字的定義,而是問你數學上的定義。
很顯然,關鍵詞也好,摘要也好,我們希望能夠盡可能快地獲取文章的大意,如果一篇文章的關鍵詞是“深度學習”,我們就會知道,這篇文章不可能大談特談“鋼鐵是怎麼練成的”,也就是說,我們可以,用數學來表示,那就是條件概率
這裏的ss代表著一段文本,wiwi是文本中的某個詞,如果wiwi是文本的關鍵詞,那麼應該使得上述概率最大。也就是說,我們隻需要對句子中所有的詞,算一遍上述概率,然後降序排列,就可以提取關鍵詞了。說白了,關鍵詞就是最能讓我們猜到原文的詞語。,如果ss由nn個詞w1,w2,…,wnw1,w2,…,wn組成,那麼
這樣,我們隻需要估算詞與詞之間的轉移概率p(wk|wi)p(wk|wi),就可以得到條件概率p(s|wi)p(s|wi)了,從而完成關鍵詞的提取。
這跟Word2Vec又有什麼聯係呢?為了估算p(wk|wi)p(wk|wi),需要有大量的文本進行統計,好在這個過程是無監督的,統計是件很簡單的事情。然而,我們有更好的工具,什麼工具最擅長於對p(wk|wi)p(wk|wi)的建模呢?讀者可能就已經猜到了,顯然就是Word2Vec呀!Word2Vec的Skip-Gram模型,不就是用來對這個概率進行建模的麼?鑒於Word2Vec的“快準狠”的特性,我們沒理由棄之不用呀!(當然,按照文章開頭的說法,並不一定要用貝葉斯假設,更加不一定要用Word2Vec來算,但是,關鍵詞的定義本身應該是合理的)
II. Word2Vec算概率
這時候讀者應該會明白,為什麼我在前兩篇文章中會那麼強調Skip-Gram + Huffman Softmax這個組合了,因為這個組合就是對p(wk|wi)p(wk|wi)進行建模的。當然,由於Huffman Softmax的特性,我們要算p(wk|wi)p(wk|wi),需要費一些周折,參考代碼如下:
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
這基本上就是直接參考gensim中的Word2Vec的score_sg_pair函數寫的,簡單的流程是:取出wkwk的Huffman編碼(路徑),取出wiwi的詞向量,然後根據路徑,要把路徑上每個節點的概率都算出來,然後乘起來,得到p(wk|wi)p(wk|wi)。這是算得是概率對數,應該乘法變為加法。最後的概率是怎麼計算的呢?事實上,按照Word2Vec的公式,每個節點的概率對數是:
這裏的θθ是節點向量,xx是輸入詞向量,dd是該節點的編碼(非0即1)。但是,官方的score_sg_pair函數並不是這樣寫的,那是因為
III. 實踐為上
有了上麵的鋪墊,現在算關鍵詞就簡單了:
from collections import Counter
def keywords(s):
s = [w for w in s if w in model]
ws = {w:sum([predict_proba(u, w) for u in s]) for w in s}
return Counter(ws).most_common()
import pandas as pd #引入它主要是為了更好的顯示效果
import jieba
s = u'太陽是一顆恒星'
pd.Series(keywords(jieba.cut(s)))
輸出結果是
0 (恒星, -27.9013707845)
1 (太陽, -28.1072913493)
2 (一顆, -30.482187911)
3 (是, -36.3372344659)
其它例子:
>>> s=u'昌平區政府網站顯示,明十三陵是世界上保存完整、埋葬皇帝最多的墓葬群,1961年被國務院公布為第一批全國重點文物保護單位,並於2003年被列為世界遺產名錄。'
>>> pd.Series(keywords(jieba.cut(s)))
0 (文物保護, -261.691625676)
1 (名錄, -272.297758506)
2 (世界遺產, -273.943120665)
3 (第一批, -280.781786703)
4 (列為, -281.663865896)
5 (明十三陵, -286.298893108)
6 (墓葬群, -287.463013816)
...>>> s=u'雄安新區橫空出世,吸引了眾多外地炒房客前去購房。然而,在當地政府重拳遏製非法炒房、樓市凍結的背景下,那些懷揣買房錢卻在雄安新區無處下手的投資需求,被擠出到周邊地區。'
>>> pd.Series(keywords(jieba.cut(s)))
0 (炒房客, -326.997266407)
1 (樓市, -336.176584187)
2 (炒房, -337.190896137)
3 (買房, -344.613473556)
4 (購房, -346.396359454)
5 (重拳, -350.207272082)
6 (外地, -355.860419218)>>> s=u'如果給一部古裝電影設計服裝,必須要考慮故事發生在哪個朝代,漢朝為寬袍大袖,清朝則是馬褂旗袍。可在京劇舞台上,幾乎任何一個曆史人物,根據他的性別年齡、身份地位、基本性格等等,都可以在現有的服飾裏找到合適的行頭。 '
>>> pd.Series(keywords(jieba.cut(s)))
0 (朝代, -485.150966757)
1 (人物, -493.759615898)
2 (古裝, -495.478962392)
3 (漢朝, -503.409908377)
4 (清朝, -503.45656029)
5 (旗袍, -504.76313228)
6 (身份, -507.624260109)
大家可以自己嚐試。如果要在自己的語料上嚐試,那就直接在語料上訓練一個Word2Vec(Skip-Gram + Huffman Softmax)模型即可,然後調用上述代碼就可以了。
IV. 應該會有疑惑
按照我們一開始的想法,p(wk|wi)p(wk|wi)應該要在整個句子內統計計算,而Word2Vec僅僅開了個窗口來計算,這合理嗎?事實上,Word2Vec雖然僅僅開了窗口,但已經成功建立了相似詞之間的聯係,也就是說,用Word2Vec做上述過程,事實上將“”進行疊加起來進行評估,相比之下,TF-IDF的方法,僅僅是將“”疊加起來進行評估,因此,我們說Word2Vec提取關鍵詞,能夠初步結合語義來判斷了。而且,Word2Vec通過考慮p(wk|wi)p(wk|wi)來考慮了文章內部的關聯,這裏有點TextRank的味道了,是一個二元模型,而TF-IDF僅僅考慮詞本身的信息量,僅僅是一個一元模型。
而且,Word2Vec是基於神經網絡訓練的,自帶平滑功能,哪怕兩個詞語在文本中未曾共現,也能得到一個較為合理的概率。
當然這樣做的代價就是:TF-IDF算法的效率是 最後更新:2017-09-17 22:34:41 上一篇:
不可思議的Word2Vec之係列四- 不一樣的“相似”
下一篇:
不可思議的Word2Vec係列二訓練好的模型