TextRank算法抽取关键词

来源:互联网 发布:人族剑士捏脸数据 编辑:程序博客网 时间:2024/05/16 11:51

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

        可以看出结果还是有一定说服性的。

        原创粉丝点击
        热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 总是把单词归不成句孑怎么办 白色踏板摩托车把漆刮了怎么办 苹果手机用流量缓冲很难怎么办 谷歌浏览器安卓手机版打不开怎么办 怀孕四个月检查高型半氨酸高怎么办 猎豹cs9怎么打不开车门怎么办 孩子在学校被老师冤枉打板子怎么办 么司福利体检暗地查乙肝怎么办 上体育课时被老师罚了腿疼怎么办 义务兵学技术不好班长打他怎么办 耳朵被打了一巴掌听不见了怎么办 耳朵被打了一巴掌后有点闷怎么办 山东省教育云平台密码戳完怎么办 被舍友知道发朋友圈说她们了怎么办 苍蝇药水进眼里了眼睛疼怎么办 三十六周了胎儿还没有入骨盆怎么办 怀孕40周了宝宝还没反应怎么办 程序员不知道自己犯法了怎么办啊 大学档案学年测评有涂改痕迹怎么办 驼背怎么办要能快速矫正的方法 手臂往后扭后手臂根部疼又肿怎么办 一岁四个月宝宝有点斜颈怎么办? 六个月的宝宝脖子有点歪怎么办 情感交叉擦腿综合症心里治疗怎么办 坐月子受风了胳膊腿疼怎么办 劲椎手术两个月双腿疼痛麻这怎么办 手机里存的照片删除了怎么办 买了好几双鞋子都不防滑怎么办 电脑平车车加绒过厚不起线怎么办 碰见那种看不起人的店员应该怎么办 在食品厂上班載卫生帽头发痒怎么办 工司没给员工上保险离职后怎么办 货物被香港律政司扣了怎么办 室友考研要跟我考一样的学校怎么办 药流第一天才吃了一粒米非司怎么办 药流半月同房第二天大出血怎么办 口臭想让它变得不臭怎么办 来单位干了几天不满意想离职怎么办 药流吃药期间吃什么吐什么怎么办 养狗家里味道大怎么办养花有用吗 第一天上班站的脚疼怎么办