先說一下自動文摘的方法。自動文摘(Automatic Summarization)的方法主要有兩種:Extraction和Abstraction。其中Extraction是抽取式自動文摘方法,通過提取文檔中已存在的關鍵詞,句子形成摘要;Abstraction是生成式自動文摘方法,通過建立抽象的語意表示,使用自然語言生成技術,形成摘要。由於生成式自動摘要方法需要複雜的自然語言理解和生成技術支持,應用領域受限。一般採用抽取式的自動文摘方法。
目前主要方法有:
- 基於統計:統計詞頻,位置等信息,計算句子權值,再選取權值高的句子作爲文摘,特點:簡單易用,但對詞句的使用大多僅停留在表面信息。
- 基於圖模型:構建拓撲結構圖,對詞句進行排序。例如,TextRank/LexRank
- 基於潛在語義:使用主題模型,挖掘詞句隱藏信息。例如,採用LDA,HMM
- 基於整數規劃:將文摘問題轉爲整數線性規劃,求全局最優解。
TextRank:
算法TextRank受到網頁之間關係PageRank算法啓發,利用局部詞彙之間關係(共現窗口)構建網絡,計算詞的重要性,選取權重大的做爲關鍵詞。常用流程: 原始語料 → 分詞 → 停用詞過濾 → 關鍵詞抽取
先說下PageRank
PageRank
PageRank最開始用來計算網頁的重要性。整個www可以看作一張有向圖圖,節點是網頁。如果網頁A存在到網頁B的鏈接,那麼有一條從網頁A指向網頁B的有向邊。構造完圖後,使用下面的公式:
S(Vi)是網頁i的重要性(PR值)。d是阻尼係數,一般設置爲0.85。In(Vi)是存在指向網頁i的鏈接的網頁集合。Out(Vj)是網頁j中的鏈接存在的鏈接指向的網頁的集合。|Out(Vj)|是集合中元素的個數。
PageRank需要使用上面的公式多次迭代才能得到結果。初始時,可以設置每個網頁的重要性爲1。上面公式等號左邊計算的結果是迭代後網頁i的PR值,等號右邊用到的PR值全是迭代前的。
舉個例子:
上圖表示了三張網頁之間的鏈接關係,直覺上網頁A最重要。可以得到下面的表:
結束\起始 | A | B | C |
A | 0 | 1 | 1 |
B | 0 | 0 | 0 |
C | 0 | 0 | 0 |
橫欄代表其實的節點,縱欄代表結束的節點。若兩個節點間有鏈接關係,對應的值爲1。
根據公式,需要將每一豎欄歸一化(每個元素/元素之和),歸一化的結果是:
結束\起始 | A | B | C |
A | 0 | 1 | 1 |
B | 0 | 0 | 0 |
C | 0 | 0 | 0 |
上面的結果構成矩陣M。我們用matlab迭代100次看看最後每個網頁的重要性:最終A的PR值爲0.4050,B和C的PR值爲0.1500。 如果把上面的有向邊看作無向的(其實就是雙向的),結果也是一樣。
TextRank
TextRank算法是一種用於文本的基於圖的排序算法。其基本思想來源於谷歌的PageRank算法, 通過把文本分割成若干組成單元(單詞、句子)並建立圖模型, 利用投票機制對文本中的重要成分進行排序, 僅利用單篇文檔本身的信息即可實現關鍵詞提取、文摘。和 LDA、HMM 等模型不同, TextRank不需要事先對多篇文檔進行學習訓練, 因其簡潔有效而得到廣泛應用。
- 如果一個單詞出現在很多單詞後面的話,那麼說明這個單詞比較重要
- 一個TextRank值很高的單詞後面跟着的一個單詞,那麼這個單詞的TextRank值會相應地因此而提高
TextRank 一般模型可以表示爲一個有向有權圖 G =(V, E), 由點集合 V和邊集合 E 組成, E 是V ×V的子集。圖中任兩點 Vi , Vj 之間邊的權重爲 wji , 對於一個給定的點 Vi, In(Vi) 爲 指 向 該 點 的 點 集 合 , Out(Vi) 爲點 Vi 指向的點集合。點 Vi 的得分定義如下:
其中, d 爲阻尼係數, 取值範圍爲 0 到 1, 一般取值爲 0.85,作爲每次迭代時的數據平滑,促進迭代穩定收斂。使用TextRank 算法計算圖中各點的得分時, 需要給圖中的點指定任意的初值, 並遞歸計算直到收斂, 即圖中任意一點的誤差率小於給定的極限值時就可以達到收斂, 一般該極限值取 0.0001。
1. 基於TextRank的關鍵詞提取
關鍵詞抽取的任務就是從一段給定的文本中自動抽取出若干有意義的詞語或詞組。TextRank算法是利用局部詞彙之間關係(共現窗口)對後續關鍵詞進行排序,直接從文本本身抽取。其主要步驟如下:
(1)把給定的文本T按照完整句子進行分割,即
(2)對於每個句子,進行分詞和詞性標註處理,並過濾掉停用詞,只保留指定詞性的單詞,如名詞、動詞、形容詞,即,其中是保留後的候選關鍵詞。
(3)構建候選關鍵詞圖G = (V,E),其中V爲節點集,由(2)生成的候選關鍵詞組成,然後採用共現關係(co-occurrence)構造任兩點之間的邊,兩個節點之間存在邊僅當它們對應的詞彙在長度爲K的窗口中共現,K表示窗口大小,即最多共現K個單詞。
(4)根據上面公式,迭代傳播各節點的權重,直至收斂。
(5)對節點權重進行倒序排序,從而得到最重要的T個單詞,作爲候選關鍵詞。
(6)由(5)得到最重要的T個單詞,在原始文本中進行標記,若形成相鄰詞組,則組合成多詞關鍵詞。例如,文本中有句子“Matlab code for plotting ambiguity function”,如果“Matlab”和“code”均屬於候選關鍵詞,則組合成“Matlab code”加入關鍵詞序列。
關於窗口的補充:
網頁之間的鏈接關係可以用圖表示,那麼怎麼把一個句子(可以看作詞的序列)構建成圖呢?TextRank將某一個詞與其前面的N個詞、以及後面的N個詞均具有圖相鄰關係(類似於N-gram語法模型)。具體實現:設置一個長度爲N的滑動窗口,所有在這個窗口之內的詞都視作詞結點的相鄰結點;則TextRank構建的詞圖爲無向圖。下圖給出了由一個文檔構建的詞圖(去掉了停用詞並按詞性做了篩選):
網上別人的評估:
接下來將評估TextRank在關鍵詞提取任務上的準確率、召回率與F1-Measure,並與TFIDF做對比;準確率計算公式如下:
測試集是由劉知遠老師提供的網易新聞標註數據集,共有13702篇文檔。Jieba完整地實現了關鍵詞提取TFIDF與TextRank算法,基於Jieba-0.39的評估實驗代碼如下:
import jieba.analyse
import json
import codecs
def precision_recall_fscore_support(y_true, y_pred):
"""
evaluate macro precision, recall and f1-score.
"""
doc_num = len(y_true)
p_macro = 0.0
r_macro = 0.0
for i in range(doc_num):
tp = 0
true_len = len(y_true[i])
pred_len = len(y_pred[i])
for w in y_pred[i]:
if w in y_true[i]:
tp += 1
if pred_len == 0:
p = 1.0 if true_len == 0 else 0.0
else:
p = tp / pred_len
r = 1.0 if true_len == 0 else tp / true_len
p_macro += p
r_macro += r
p_macro /= doc_num
r_macro /= doc_num
return p_macro, r_macro, 2 * p_macro * r_macro / (p_macro + r_macro)
file_path = 'data/163_chinese_news_dataset_2011.dat'
with codecs.open(file_path, 'r', 'utf-8') as fr:
y_true = []
y_pred = []
for line in fr.readlines():
d = json.loads(line)
content = d['content']
true_key_words = [w for w in set(d['tags'])]
y_true.append(true_key_words)
# for w in true_key_words:
# jieba.add_word(w)
key_word_pos = ['x', 'ns', 'n', 'vn', 'v', 'l', 'j', 'nr', 'nrt', 'nt', 'nz', 'nrfg', 'm', 'i', 'an', 'f', 't',
'b', 'a', 'd', 'q', 's', 'z']
extract_key_words = jieba.analyse.extract_tags(content, topK=2, allowPOS=key_word_pos)
# trank = jieba.analyse.TextRank()
# trank.span = 5
# extract_key_words = trank.textrank(content, topK=2, allowPOS=key_word_pos)
y_pred.append(extract_key_words)
prf = precision_recall_fscore_support(y_true, y_pred)
print('precision: {}'.format(prf[0]))
print('recall: {}'.format(prf[1]))
print('F1: {}'.format(prf[2]))
其中,每個文檔提取的關鍵詞數爲2,並按詞性做過濾;span表示TextRank算法中的滑動窗口的大小。評估結果如下:
方法 | Precision | Recall | F1-Measure |
---|---|---|---|
TFIDF | 0.2697 | 0.2256 | 0.2457 |
TextRank span=5 | 0.2608 | 0.2150 | 0.2357 |
TextRank span=7 | 0.2614 | 0.2155 | 0.2363 |
如果將標註關鍵詞添加到自定義詞典,則評估結果如下:
方法 | Precision | Recall | F1-Measure |
---|---|---|---|
TFIDF | 0.3145 | 0.2713 | 0.2913 |
TextRank span=5 | 0.2887 | 0.2442 | 0.2646 |
TextRank span=7 | 0.2903 | 0.2455 | 0.2660 |
直觀感受下關鍵詞提取結果(添加了自定義詞典):
// TFIDF, TextRank, labelled
刪了,好像命中審覈邏輯,不通過了
從上述兩組實驗結果,可以發現:
- TextRank與TFIDF均嚴重依賴於分詞結果——如果某詞在分詞時被切分成了兩個詞,那麼在做關鍵詞提取時無法將兩個詞黏合在一起(TextRank有部分黏合效果,但需要這兩個詞均爲關鍵詞)。因此是否添加標註關鍵詞進自定義詞典,將會造成準確率、召回率大相徑庭。
- TextRank的效果並不優於TFIDF。
- TextRank雖然考慮到了詞之間的關係,但是仍然傾向於將頻繁詞作爲關鍵詞。
此外,由於TextRank涉及到構建詞圖及迭代計算,所以提取速度較慢。