閱讀599 返回首頁    go 阿裏雲 go 技術社區[雲棲]


不可思議的Word2Vec之係列三-提取關鍵詞

說到提取關鍵詞,一般會想到TF-IDF和TextRank,大家是否想過,Word2Vec還可以用來提取關鍵詞?而且,用Word2Vec提取關鍵詞,已經初步含有了語義上的理解,而不僅僅是簡單的統計了,而且還是無監督的!

I. 什麼是關鍵詞?

誠然,TF-IDF和TextRank是兩種提取關鍵詞的很經典的算法,它們都有一定的合理性,但問題是,,估計很難能夠從零把它們構造出來。也就是說,這兩種算法雖然看上去簡單,但並不容易想到。試想一下,沒有學過信息相關理論的同學,估計怎麼也難以理解為什麼IDF要取一個對數?為什麼不是其他函數?又有多少讀者會破天荒地想到,用PageRank的思路,去判斷一個詞的重要性?

說到底,問題就在於:提取關鍵詞和文本摘要,看上去都是一個很自然的任務,有誰真正思考過,關鍵詞的定義是什麼?這裏不是要你去查漢語詞典,獲得一大堆文字的定義,而是問你數學上的定義。

很顯然,關鍵詞也好,摘要也好,我們希望能夠盡可能快地獲取文章的大意,如果一篇文章的關鍵詞是“深度學習”,我們就會知道,這篇文章不可能大談特談“鋼鐵是怎麼練成的”,也就是說,我們可以,用數學來表示,那就是條件概率

p(s|wi)p(s|wi)

這裏的ss代表著一段文本,wiwi是文本中的某個詞,如果wiwi是文本的關鍵詞,那麼應該使得上述概率最大。也就是說,我們隻需要對句子中所有的詞,算一遍上述概率,然後降序排列,就可以提取關鍵詞了。說白了,關鍵詞就是最能讓我們猜到原文的詞語。,如果ssnn個詞w1,w2,,wnw1,w2,…,wn組成,那麼
p(s|wi)=p(w1,w2,,wn|wi)=k=1np(wk|wi)p(s|wi)=p(w1,w2,…,wn|wi)=∏k=1np(wk|wi)

這樣,我們隻需要估算詞與詞之間的轉移概率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的公式,每個節點的概率對數是:

==log(11+exθ)1d(111+exθ)d(1d)log(1+exθ)dlog(1+exθ)dxθlog(1+exθ)dxθlog⁡(11+e−x⊤θ)1−d(1−11+e−x⊤θ)d=−(1−d)log⁡(1+e−x⊤θ)−dlog⁡(1+e−x⊤θ)−dx⊤θ=−log⁡(1+e−x⊤θ)−dx⊤θ

這裏的θθ是節點向量,xx是輸入詞向量,dd是該節點的編碼(非0即1)。但是,官方的score_sg_pair函數並不是這樣寫的,那是因為
===log(1+exθ)dxθlog[edxθ(1+exθ)]log(edxθ+e(d1)xθ)log(1+e(1)dxθ)−log⁡(1+e−x⊤θ)−dx⊤θ=−log⁡[edx⊤θ(1+e−x⊤θ)]=−log⁡(edx⊤θ+e(d−1)x⊤θ)=−log⁡(1+e−(−1)dx⊤θ)

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

  上一篇:go  不可思議的Word2Vec之係列四- 不一樣的“相似”
  下一篇:go  不可思議的Word2Vec係列二訓練好的模型