更多:TextRank…
基于荣耀V10手机评论数据的Python代码实现如下所示:
# -*- coding: utf-8 -*- """ Created on Fri Feb 9 15:58:14 2018 @author: ai8py.com """ import codecs from textrank4zh import TextRank4Keyword, TextRank4Sentence #读取华为荣耀天猫旗舰店荣耀V10手机的评论文本数据 text = codecs.open('D://data/tmall/origin_tmall_review.txt', 'r', 'utf-8').read() tr4w = TextRank4Keyword() tr4w.analyze(text=text, lower=True, window=2) print( '关键词:' ) for item in tr4w.get_keywords(10, word_min_len=1): print("{} 出现的频率为:{:.6f}".format(item.word, item.weight)) print( '关键短语:' ) for phrase in tr4w.get_keyphrases(keywords_num=10, min_occur_num=5): print(phrase) tr4s = TextRank4Sentence() tr4s.analyze(text=text, lower=True, source = 'all_filters') print() print( '摘要:' ) for item in tr4s.get_key_sentences(num=3): #index是语句在文本中位置,weight是权重 print("第{}句出现的频率为:{:.6f},内容为:{}".format(item.index, item.weight, item.sentence))
输出的关键词如下图所示:
输出的关键短语如下图所示:
输出的摘要如下图所示:
从上面的输出结果可以看出,华为荣耀V10的评论信息,大多数还是比较积极、正面的,能够基本反映出用户对这款手机产品的态度。
先说一下自动文摘的方法。自动文摘(Automatic Summarization)的方法主要有两种:Extraction和Abstraction。其中Extraction是抽取式自动文摘方法,通过提取文档中已存在的关键词,句子形成摘要;Abstraction是生成式自动文摘方法,通过建立抽象的语意表示,使用自然语言生成技术,形成摘要。由于生成式自动摘要方法需要复杂的自然语言理解和生成技术支持,应用领域受限。所以本人学习的也是抽取式的自动文摘方法。
目前主要方法有:
- 基于统计:统计词频,位置等信息,计算句子权值,再简选取权值高的句子作为文摘,特点:简单易用,但对词句的使用大多仅停留在表面信息。
- 基于图模型:构建拓扑结构图,对词句进行排序。例如,TextRank/LexRank
- 基于潜在语义:使用主题模型,挖掘词句隐藏信息。例如,采用LDA,HMM
- 基于整数规划:将文摘问题转为整数线性规划,求全局最优解。
textrank算法
TextRank算法基于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次看看最后每个网页的重要性:
<code class="hljs lua">M = [<span class="hljs-number">0 <span class="hljs-number">1 <span class="hljs-number">1 <span class="hljs-number">0 <span class="hljs-number">0 <span class="hljs-number">0 <span class="hljs-number">0 <span class="hljs-number">0 <span class="hljs-number">0]; PR = [<span class="hljs-number">1; <span class="hljs-number">1 ; <span class="hljs-number">1]; <span class="hljs-keyword">for iter = <span class="hljs-number">1:<span class="hljs-number">100 PR = <span class="hljs-number">0.15 + <span class="hljs-number">0.85*M*PR; disp(iter); disp(PR); <span class="hljs-keyword">end</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code>
运行结果(省略部分):
<code class="hljs css">...... 95 0<span class="hljs-selector-class">.4050 0<span class="hljs-selector-class">.1500 0<span class="hljs-selector-class">.1500 96 0<span class="hljs-selector-class">.4050 0<span class="hljs-selector-class">.1500 0<span class="hljs-selector-class">.1500 97 0<span class="hljs-selector-class">.4050 0<span class="hljs-selector-class">.1500 0<span class="hljs-selector-class">.1500 98 0<span class="hljs-selector-class">.4050 0<span class="hljs-selector-class">.1500 0<span class="hljs-selector-class">.1500 99 0<span class="hljs-selector-class">.4050 0<span class="hljs-selector-class">.1500 0<span class="hljs-selector-class">.1500 100 0<span class="hljs-selector-class">.4050 0<span class="hljs-selector-class">.1500 0<span class="hljs-selector-class">.1500</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code>
最终A的PR值为0.4050,B和C的PR值为0.1500。
如果把上面的有向边看作无向的(其实就是双向的),那么:
<code class="hljs lua">M = [<span class="hljs-number">0 <span class="hljs-number">1 <span class="hljs-number">1 <span class="hljs-number">0.5 <span class="hljs-number">0 <span class="hljs-number">0 <span class="hljs-number">0.5 <span class="hljs-number">0 <span class="hljs-number">0]; PR = [<span class="hljs-number">1; <span class="hljs-number">1 ; <span class="hljs-number">1]; <span class="hljs-keyword">for iter = <span class="hljs-number">1:<span class="hljs-number">100 PR = <span class="hljs-number">0.15 + <span class="hljs-number">0.85*M*PR; disp(iter); disp(PR); <span class="hljs-keyword">end</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></code>
运行结果(省略部分):
<code class="hljs css">..... 98 1<span class="hljs-selector-class">.4595 0<span class="hljs-selector-class">.7703 0<span class="hljs-selector-class">.7703 99 1<span class="hljs-selector-class">.4595 0<span class="hljs-selector-class">.7703 0<span class="hljs-selector-class">.7703 100 1<span class="hljs-selector-class">.4595 0<span class="hljs-selector-class">.7703 0<span class="hljs-selector-class">.7703</span></span></span></span></span></span></span></span></span></code>
依然能判断出A、B、C的重要性。
TextRank
TextRank 算法是一种用于文本的基于图的排序算法。其基本思想来源于谷歌的 PageRank算法, 通过把文本分割成若干组成单元(单词、句子)并建立图模型, 利用投票机制对文本中的重要成分进行排序, 仅利用单篇文档本身的信息即可实现关键词提取、文摘。和 LDA、HMM 等模型不同, 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算法是利用局部词汇之间关系(共现窗口)对后续关键词进行排序,直接从文本本身抽取。其主要步骤如下:
(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源码解析
1.读入文本,并切词,对切词结果统计共现关系,窗口默认为5,保存大cm中
cm = defaultdict(int) #切词 words = tuple(self.tokenizer.cut(sentence)) for i, wp in enumerate(words): #(enumerate枚举的方式进行) #过滤词性,停用词等 if self.pairfilter(wp): for j in xrange(i + 1, i + self.span): if j >= len(words): break if not self.pairfilter(words[j]):#过滤 continue #保存到字典中 if allowPOS and withFlag: cm[(wp, words[j])] += 1 else: cm[(wp.word, words[j].word)] += 1