TextRank是一种用来做关键词提取的算法,也可以用于提取短语和自动摘要。因为TextRank是基于PageRank的,所以首先简要介绍下PageRank算法。

(1)PageRank

PageRank设计之初是用于Google的网页排名的,以该公司创办人拉里·佩奇(Larry Page)之姓来命名。Google用它来体现网页的相关性和重要性,在搜索引擎优化操作中是经常被用来评估网页优化的成效因素之一。PageRank通过互联网中的超链接关系来确定一个网页的排名,其公式是通过一种投票的思想来设计的:如果我们要计算网页A的PageRank值(以下简称PR值),那么我们需要知道有哪些网页链接到网页A,也就是要首先得到网页A的入链,然后通过入链给网页A的投票来计算网页A的PR值。这样设计可以保证达到这样一个效果:当某些高质量的网页指向网页A的时候,那么网页A的PR值会因为这些高质量的投票而变大,而网页A被较少网页指向或被一些PR值较低的网页指向的时候,A的PR值也不会很大,这样可以合理地反映一个网页的质量水平。那么根据以上思想,佩奇设计了下面的公式:

$PR(V_i) = (1-d) + d * \sum_{j \in In(V_i)} \frac{1}{|Out(V_j)|}PR(V_j)$

该公式中,Vi表示某个网页,Vj表示链接到Vi的网页(即Vi的入链),S(Vi)表示网页Vi的PR值,In(Vi)表示网页Vi的所有入链的集合,Out(Vj)表示网页,d表示阻尼系数,是用来克服这个公式中“d *”后面的部分的固有缺陷用的:如果仅仅有求和的部分,那么该公式将无法处理没有入链的网页的PR值,因为这时,根据该公式这些网页的PR值为0,但实际情况却不是这样,所有加入了一个阻尼系数来确保每个网页都有一个大于0的PR值,根据实验的结果,在0.85的阻尼系数下,大约100多次迭代PR值就能收敛到一个稳定的值,而当阻尼系数接近1时,需要的迭代次数会陡然增加很多,且排序不稳定。公式中S(Vj)前面的分数指的是Vj所有出链指向的网页应该平分Vj的PR值,这样才算是把自己的票分给了自己链接到的网页.

(2)TextRank算法提取关键词

TextRank是由PageRank改进而来,其公式有颇多相似之处,这里给出TextRank的公式:

$WS(V_i) = (1-d) + d * \sum_{j \in In(V_i)} \frac{w_{ji}}{\sum_{V_k \in Out(V_j)} w_{jk}} WS(V_j)$

可以看出,该公式仅仅比PageRank多了一个权重项Wji,用来表示两个节点之间的边连接有不同的重要程度。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”加入关键词序列。

实现细节,具体可以看看源码.下面是源码中的一些细节:

首先定义一个无向有权图,然后对句子进行分词;依次遍历分词结果,如果某个词i满足过滤条件(词性在词性过滤集合中,并且词的长度大于等于2,并且词不是停用词),然后将这个词之后窗口长度为5范围内的词j(这些词也需要满足过滤条件),将它们两两(词i和词j)作为key,出现的次数作为value,添加到共现词典中;

然后,依次遍历共现词典,将词典中的每个元素,key = (词i,词j),value = 词i和词j出现的次数,其中词i,词j作为一条边起始点和终止点,共现的次数作为边的权重,添加到之前定义的无向有权图中。

然后对这个无向有权图进行迭代运算textrank算法,最终经过若干次迭代后,算法收敛,每个词都对应一个指标值;

如果设置了权重标志位,则根据指标值值对无向有权图中的词进行降序排序,最后输出topK个词作为关键词;

其中,无向有权图的的定义及实现是在UndirectWeightedGraph类中实现的。根据UndirectWeightedGraph类的初始化函数__init__,我们可以发现,所谓的无向有权图就是一个词典,词典的key是后续要添加的词,词典的value,则是一个由(起始点,终止点,边的权重)构成的三元组所组成的列表,表示以这个词作为起始点的所有的边。

无向有权图添加边的操作是在addEdge函数中完成的,因为是无向图,所以我们需要依次将start作为起始点,end作为终止点,然后再将start作为终止点,end作为起始点,这两条边的权重是相同的。

def textrank(self, sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v'), withFlag=False):

    self.pos_filt = frozenset(allowPOS)
# 定义无向有权图
g = UndirectWeightedGraph()
# 定义共现词典
cm = defaultdict(int)
# 分词
words = tuple(self.tokenizer.cut(sentence))
# 依次遍历每个词
for i, wp in enumerate(words):
# 词i 满足过滤条件
if self.pairfilter(wp):
# 依次遍历词i 之后窗口范围内的词
for j in xrange(i + 1, i + self.span):
# 词j 不能超出整个句子
if j >= len(words):
break
# 词j不满足过滤条件,则跳过
if not self.pairfilter(words[j]):
continue
# 将词i和词j作为key,出现的次数作为value,添加到共现词典中
if allowPOS and withFlag:
cm[(wp, words[j])] += 1
else:
cm[(wp.word, words[j].word)] += 1
# 依次遍历共现词典的每个元素,将词i,词j作为一条边起始点和终止点,共现的次数作为边的权重
for terms, w in cm.items():
g.addEdge(terms[0], terms[1], w) # 运行textrank算法
nodes_rank = g.rank() # 根据指标值进行排序
if withWeight:
tags = sorted(nodes_rank.items(), key=itemgetter(1), reverse=True)
else:
tags = sorted(nodes_rank, key=nodes_rank.__getitem__, reverse=True) # 输出topK个词作为关键词
if topK:
return tags[:topK]
else:
return tags
其中,无向有权图的的定义及实现是在UndirectWeightedGraph类中实现的。根据UndirectWeightedGraph类的初始化函数__init__,我们可以发现,所谓的无向有权图就是一个词典,词典的key是后续要添加的词,词典的value,则是一个由(起始点,终止点,边的权重)构成的三元组所组成的列表,表示以这个词作为起始点的所有的边。
无向有权图添加边的操作是在addEdge函数中完成的,因为是无向图,所以我们需要依次将start作为起始点,end作为终止点,然后再将start作为终止点,end作为起始点,这两条边的权重是相同的。
def addEdge(self, start, end, weight):
# use a tuple (start, end, weight) instead of a Edge object
self.graph[start].append((start, end, weight))
self.graph[end].append((end, start, weight))
执行textrank算法迭代是在rank函数中完成的。
首先对每个结点赋予相同的权重,以及计算出该结点的所有出度的次数之和;
然后迭代若干次,以确保得到稳定的结果;
在每一次迭代中,依次遍历每个结点;对于结点n,首先根据无向有权图得到结点n的所有
入度结点(对于无向有权图,入度结点与出度结点是相同的,都是与结点n相连的结点),在前面我们已经计算出这个入度结点的所有出度的次数,而它对于结点n的权值的贡献等于它本身的权值 乘以 它与结点n的共现次数 / 这个结点的所有出度的次数 ,将各个入度结点得到的权值相加,再乘以一定的阻尼系数,即可得到结点n的权值;
迭代完成后,对权值进行归一化,并返回各个结点及其对应的权值。
def rank(self):
ws = defaultdict(float)
outSum = defaultdict(float) wsdef = 1.0 / (len(self.graph) or 1.0)
# 初始化各个结点的权值
# 统计各个结点的出度的次数之和
for n, out in self.graph.items():
ws[n] = wsdef
outSum[n] = sum((e[2] for e in out), 0.0) # this line for build stable iteration
sorted_keys = sorted(self.graph.keys())
# 遍历若干次
for x in xrange(10): # 10 iters
# 遍历各个结点
for n in sorted_keys:
s = 0
# 遍历结点的入度结点
for e in self.graph[n]:
# 将这些入度结点贡献后的权值相加
# 贡献率 = 入度结点与结点n的共现次数 / 入度结点的所有出度的次数
s += e[2] / outSum[e[1]] * ws[e[1]]
# 更新结点n的权值
ws[n] = (1 - self.d) + self.d * s (min_rank, max_rank) = (sys.float_info[0], sys.float_info[3]) # 获取权值的最大值和最小值
for w in itervalues(ws):
if w < min_rank:
min_rank = w
if w > max_rank:
max_rank = w # 对权值进行归一化
for n, w in ws.items():
# to unify the weights, don't *100.
ws[n] = (w - min_rank / 10.0) / (max_rank - min_rank / 10.0) return ws

完结

 

TextRank算法的更多相关文章

  1. Textrank算法介绍

    先说一下自动文摘的方法.自动文摘(Automatic Summarization)的方法主要有两种:Extraction和Abstraction.其中Extraction是抽取式自动文摘方法,通过提取 ...

  2. TextRank算法提取关键词的Java实现

    转载:码农场 » TextRank算法提取关键词的Java实现 谈起自动摘要算法,常见的并且最易实现的当属TF-IDF,但是感觉TF-IDF效果一般,不如TextRank好. TextRank是在 G ...

  3. PageRank算法与TextRank算法详解

    PageRank算法: 该算法本质上属于有向带权图. 对于某个互联网网页A来说,该网页PageRank的计算基于以下两个基本假设: 数量假设:在Web图模型中,如果一个页面节点接收到的其他网页指向的入 ...

  4. 基于TextRank算法的文本摘要

    本文介绍TextRank算法及其在多篇单领域文本数据中抽取句子组成摘要中的应用. TextRank 算法是一种用于文本的基于图的排序算法,通过把文本分割成若干组成单元(句子),构建节点连接图,用句子之 ...

  5. TextRank算法及生产文本摘要方法介绍

    TextRank 算法是一种用于文本的基于图的排序算法,其基本思想来源于谷歌的 PageRank算法,通过把文本分割成若干组成单元(句子),构建节点连接图,用句子之间的相似度作为边的权重,通过循环迭代 ...

  6. 自然语言处理工具hanlp关键词提取图解TextRank算法

    看一个博主(亚当-adam)的关于hanlp关键词提取算法TextRank的文章,还是非常好的一篇实操经验分享,分享一下给各位需要的朋友一起学习一下! TextRank是在Google的PageRan ...

  7. TextRank:关键词提取算法中的PageRank

    很久以前,我用过TFIDF做过行业关键词提取.TFIDF仅仅从词的统计信息出发,而没有充分考虑词之间的语义信息.现在本文将介绍一种考虑了相邻词的语义关系.基于图排序的关键词提取算法TextRank [ ...

  8. 关键词提取算法TextRank

    很久以前,我用过TFIDF做过行业关键词提取.TFIDF仅仅从词的统计信息出发,而没有充分考虑词之间的语义信息.现在本文将介绍一种考虑了相邻词的语义关系.基于图排序的关键词提取算法TextRank. ...

  9. 关键字提取算法TF-IDF和TextRank(python3)————实现TF-IDF并jieba中的TF-IDF对比,使用jieba中的实现TextRank

    关键词:    TF-IDF实现.TextRank.jieba.关键词提取数据来源:    语料数据来自搜狐新闻2012年6月—7月期间国内,国际,体育,社会,娱乐等18个频道的新闻数据    数据处 ...

随机推荐

  1. Fiddler简介及web抓包

    1.Fiddler界面如下   2.Fiddler开关 界面左下角或点击F12控制Fiddler开关,开关是“Capturing”: 启动之后,Fiddler代理永远是开着的.     3.浏览器代理 ...

  2. python获取文件路径、文件名、后缀名的实例

    def jwkj_get_filePath_fileName_fileExt(filename): (filepath,tempfilename) = os.path.split(filename); ...

  3. JDBC MVC框架实现用户登录

    MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写 1.实体entity package com.uplooking. ...

  4. 数据结构与算法之PHP查找算法(二分查找)

    二分查找又称折半查找,只对有序的数组有效. 优点是比较次数少,查找速度快,平均性能好,占用系统内存较少: 缺点是要求待查表为有序表,且插入删除困难. 因此,折半查找方法适用于不经常变动而查找频繁的有序 ...

  5. Lucene.Net 学习(搜索部分)(低要求,写给自己看)

    1. 搜索 排序:lucene 提供了Sort类对结果进行排序 提供了Filter类对查询条件进行限制 你或许会不自觉地拿它跟SQL语句进行比较:“lucene能执行and.or.order by.w ...

  6. mongodb副本集修改配置问题

    因虚拟机地址被占用,需要重新设置ip地址,这时需要修改副本集中的IP地址配置: 1: 查看配置rs.config():需要找到primary主机,在该主节点服务器上才有权限修改配置 2:rs.remo ...

  7. [luogu P3195] [HNOI2008]玩具装箱TOY

    [luogu P3195] [HNOI2008]玩具装箱TOY 题目描述 P教授要去看奥运,但是他舍不下他的玩具,于是他决定把所有的玩具运到北京.他使用自己的压缩器进行压缩,其可以将任意物品变成一堆, ...

  8. PHP闭包函数

    # 提到闭包就不得不想起匿名函数,也叫闭包函数(closures),貌似PHP闭包实现主要就是靠它.声明一个匿名函数是这样: $func = function() { }; //带结束符 # 可以看到 ...

  9. Linux查看某个命令属于哪个包

    有时修我们需要某个命令但其没有安装,提供该命令的包名也与命令名相差很大直接查找命令名找不到包,如rexec. 此时我们就非常需要这样一个工具:可以根据最终的命令查找提供该命令的软件包. 类型 命令 说 ...

  10. java 一些容易忽视的小点-类和对象

    构造器 通过new关键字调用 构造器虽然有返回值,但是不能定义返回值类型(返回值的类型肯定是本类),不能在构造器里使用return返回某个值. 构造器是有权限的,也就是可以添加public,也可以添加 ...