TextRank算法抽取關鍵詞

PageRank

由於TextRank是由大名鼎鼎的Google的PageRank算法轉化而來,所以這裏先介紹一下PageRank算法。

PageRank最開始用來計算網頁的重要性。在衡量一個網頁的排名時,直覺告訴我們:
(1)一個網頁被更多網頁鏈接時,就應該越重要,其排名就應該越靠前。
(2)排名高的網頁應具有更大的表決權,即當一個網頁被排名高的網頁所鏈接時,其重要性也應該提高。
所以基於以上兩點,PageRank算法所建立的模型非常簡單:一個網頁的排名等於所有鏈接到該網頁的網頁的加權排名之和。

這裏寫圖片描述

整個www可以看作一張有向圖圖,節點是網頁。如果網頁A存在到網頁B的鏈接,那麼有一條從網頁A指向網頁B的有向邊。

S(Vi)是網頁i的中重要性(PR值)。d是阻尼係數,一般設置爲0.85。In(Vi)是存在指向網頁i的鏈接的網頁集合。Out(Vj)是網頁j中的鏈接存在的鏈接指向的網頁的集合,|Out(Vj)|是集合中元素的個數。

通俗來說也就是,對於網頁i來說,它的重要性,取決於指向網頁i的每個網頁的重要性之和來決定的。每個指向到網頁i的重要性S(Vj)還需要對所有它所指出去的頁面平分,所以要除以|Out(Vj)。同時,該頁面S(Vj)的重要性不能單單由其他的鏈接頁面決定,還包含一定的概率來決定要不要接受由其他頁面來決定,這也就是d的作用。

但是,爲了獲得某個網頁的重要性,而需要知道其他網頁的重要性,這不就等同於“是先有雞還是先有蛋”的問題了麼?幸運的是,PageRank採用power iteration方法破解了這個問題怪圈。先給定一個初始值,然後通過多輪迭代求解,迭代到最後就會收斂於一個結果。當差別小於某個閾值時,就可以結束迭代了。


TextRank

正規的TextRank公式在PageRank的公式的基礎上,引入了邊的權值的概念,代表兩個句子的相似度。

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。

由於引入了句子的相似度,所以正規的TextRank公式是用來從文章中自動抽取關鍵句,但是我們現在是想計算關鍵字,如果把一個單詞視爲一個句子的話,那麼所有句子(單詞)構成的邊的權重都是0(沒有交集,沒有相似性),所以分子分母的權值w約掉了,算法退化爲PageRank。所以說,關鍵字提取算法也就是PageRank。

這裏寫圖片描述

對於提取關鍵詞來說,假如設窗口大小爲5,In(Vi)是與詞i的距離不大於5的詞的集合。Out(Vj)是與詞j距離不大於5詞的集合,|Out(Vj)|是集合中元素的個數。


樣例

對於句子:

程序員(英文Programmer)是從事程序開發、維護的專業人員。一般將程序員分爲程序設計人員和程序編碼人員,但兩者的界限並不非常清楚,特別是在中國。軟件從業人員分爲初級程序員、高級程序員、系統分析員和項目經理四大類。

首先對這句話分詞,得出分詞結果:

[程序員/n, (, 英文/nz, programmer/en, ), 是/v, 從事/v, 程序/n, 開發/v, 、/w, 維護/v, 的/uj, 專業/n, 人員/n, 。/w, 一般/a, 將/d, 程序員/n, 分爲/v, 程序/n, 設計/vn, 人員/n, 和/c, 程序/n, 編碼/n, 人員/n, ,/w, 但/c, 兩者/r, 的/uj, 界限/n, 並/c, 不/d, 非常/d, 清楚/a, ,/w, 特別/d, 是/v, 在/p, 中國/ns, 。/w, 軟件/n, 從業/b, 人員/n, 分爲/v, 初級/b, 程序員/n, 、/w, 高級/a, 程序員/n, 、/w, 系統/n, 分析員/n, 和/c, 項目/n, 經理/n, 四/m, 大/a, 類/q, 。/w]

然後去掉裏面的停用詞,比如標點符號、常用詞、以及“名詞、動詞、形容詞、副詞之外的詞”。得出實際有用的詞語:

[程序員, 英文, 程序, 開發, 維護, 專業, 人員, 程序員, 分爲, 程序, 設計, 人員, 程序, 編碼, 人員, 界限, 特別, 中國, 軟件, 人員, 分爲, 程序員, 高級, 程序員, 系統, 分析員, 項目, 經理]

然後我們首先計算距離每個詞距離不大於窗口大小的詞的集合,然後根據公式進行迭代,直到所有詞的重要性收斂到某一個值的時候,就可以停止迭代並輸出結果。


代碼

  1. package Test;
  2.  
  3. import java.util.*;
  4. import java.util.Map.Entry;
  5.  
  6. /**
  7.  * 基於TextRank算法的關鍵字提取,適用於單文檔
  8.  */
  9. public class TextRankKeyword {
  10.     final static float d = 0.85f;//阻尼係數,一般取值爲0.85
  11.     final static int max_iter = 200;//最大迭代次數
  12.     final static float min_diff = 0.001f;//最小區別值,當收斂程度小於這個值結束迭代
  13.  
  14.     /**
  15.      * 使用已經分好的詞來計算rank
  16.      */
  17.     public static Map<String, Float> getRank(List<String> termList, int windowSize) {
  18.         List<String> wordList = new ArrayList<String>(termList.size());//去掉停用詞後的詞序列
  19.         for (String str : termList) {
  20.                wordList.add(str);
  21.         }
  22.         //System.out.println(wordList);
  23.         Map<String, Set<String>> words = new LinkedHashMap<String, Set<String>>();//詞和它對應的鄰居們
  24.         Queue<String> que = new LinkedList<String>();//用一個隊列來表示窗口的移動
  25.         for (String w : wordList) {
  26.            if (!words.containsKey(w)) {
  27.                words.put(w, new TreeSet<String>());
  28.            }
  29.            if (que.size() >= windowSize) {//如果隊列長度大於窗口大小了,則把隊列頭元素移除
  30.                que.poll();
  31.            }
  32.            for (String qWord : que) {
  33.                if (w.equals(qWord)) {
  34.                    continue;
  35.                }
  36.                //既然是鄰居,那麼關係是相互的,遍歷一遍即可
  37.                words.get(w).add(qWord);
  38.                words.get(qWord).add(w);
  39.            }
  40.            que.offer(w);//w添加到隊列尾部
  41.         }
  42.         //System.out.println(words);
  43.         Map<String, Float> score = new LinkedHashMap<String, Float>();//每一輪迭代後的詞和對應的權值
  44.         for (int i = 0; i < max_iter; ++i) {
  45.            Map<String, Float> m = new LinkedHashMap<String, Float>();//此次迭代中的結果
  46.            float max_diff = 0;//此次迭代中變化最大的權值
  47.            for (Map.Entry<String, Set<String>> entry : words.entrySet()) {
  48.                String key = entry.getKey();
  49.                Set<String> value = entry.getValue();
  50.                m.put(key, 1 - d);
  51.                for (String element : value) {
  52.                    int size = words.get(element).size();
  53.                    if (key.equals(element) || size == 0) continue;
  54.                    m.put(key, m.get(key) + d / size * (score.get(element) == null ? 0 : score.get(element)));
  55.                        //按照公式計算新的權值
  56.                }
  57.                max_diff = Math.max(max_diff, Math.abs(m.get(key) - (score.get(key) == null ? 0 : score.get(key))));
  58.            }
  59.            score = m;//新一輪的迭代結果存進score
  60.            if (max_diff <= min_diff) break;//變化最大的權值小於min_diff,結束迭代
  61.         }
  62.  
  63.         return score;
  64.     }
  65.  
  66.     public static void main(String[] args) {
  67.         String[] s={"程序員", "英文", "程序", "開發","維護", "專業",
  68.            "人員", "程序員", "分爲", "程序", "設計", "人員",
  69.            "程序", "編碼", "人員", "界限", "特別", "中國",
  70.            "軟件", "人員", "分爲", "程序員", "高級", "程序員",
  71.            "系統", "分析員", "項目", "經理"};
  72.         List<String> termList = Arrays.asList(s);
  73.         int windowSize=5;//窗口大小
  74.         Map<String, Float> map=getRank(termList,windowSize);
  75.         for (Entry<String, Float> entry : map.entrySet()) {
  76.            String key = entry.getKey();
  77.            float value = entry.getValue();
  78.            System.out.println(key+"="+value);
  79.         }
  80.     }
  81.  
  82. }

輸出結果

  1. 程序員=1.9249978
  2. 英文=0.7098714
  3. 程序=1.4025854
  4. 開發=0.82074183
  5. 維護=0.9321688
  6. 專業=0.9321688
  7. 人員=1.629035
  8. 分爲=1.4027836
  9. 設計=0.6992446
  10. 編碼=0.82671607
  11. 界限=0.8220693
  12. 特別=0.9335202
  13. 中國=0.9341458
  14. 軟件=0.93525416
  15. 高級=0.97473735
  16. 系統=0.885048
  17. 分析員=0.7710108
  18. 項目=0.7710108
  19. 經理=0.64640945

對結果進行Map排序之後的結果:

  1. 程序員=1.9249978
  2. 人員=1.629035
  3. 分爲=1.4027836
  4. 程序=1.4025854
  5. 高級=0.97473735
  6. 軟件=0.93525416
  7. 中國=0.9341458
  8. 特別=0.9335202
  9. 維護=0.9321688
  10. 專業=0.9321688
  11. 系統=0.885048
  12. 編碼=0.82671607
  13. 界限=0.8220693
  14. 開發=0.82074183
  15. 分析員=0.7710108
  16. 項目=0.7710108
  17. 英文=0.7098714
  18. 設計=0.6992446
  19. 經理=0.64640945

可以看出結果還是有一定說服性的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章