TextRank算法学习及使用


   文本关键词抽取、文本摘要生成是自然语言处理(NLP)的应用之一,一定会对我们的生活产生巨大影响。随着数字媒体的发展和出版业的不断增长,谁还会有时间完整地浏览整篇文章、文档、书籍来决定它们是否有用呢?值得高兴的是,这项技术已经在这里了。也就是今天我们要学习的TextRank算法。

   文本摘要可以大致分为两类——抽取型摘要抽象型摘要

   抽取型摘要: 这种方法依赖于从文本中提取几个部分,例如短语、句子,把它们堆叠起来创建摘要。因此,这种抽取型的方法最重要的是识别出适合总结文本的句子。显然,TextRank算法进行摘要抽取属于抽取式的。

   抽象型摘要:这种方法应用先进的NLP技术生成一篇全新的总结。可能总结出的文本没有在原文中出现。

目前主要方法有

  • 基于统计:统计词频,位置等信息,计算句子权值,再简选取权值高的句子作为文摘,特点:简单易用,但对词句的使用大多仅停留在表面信息。
  • 基于图模型:构建拓扑结构图,对词句进行排序。例如,TextRank/LexRank
  • 基于潜在语义:使用主题模型,挖掘词句隐藏信息。例如,采用LDA,HMM
  • 基于整数规划:将文摘问题转为整数线性规划,求全局最优解。

一、算法思想

   TextRank算法是由PageRank算法改进而来的,二者的思想有相同之处,区别在于:PageRank算法根据网页之间的链接关系构造网络,而TextRank算法根据词之间的共现关系构造网络;PageRank算法构造的网络中的边是有向无权边,而TextRank算法构造的网络中的边是无向有权边

   TextRank 算法是一种用于文本的基于图的排序算法,通过把文本分割成若干组成单元(句子/词),构建节点连接图,用句子(词)之间的相似度作为边的权重,通过循环迭代计算句子(词)的TextRank值,最后抽取排名高的句子(词)组合成文本摘要。

   上一篇博客我们已经学习了PageRank算法的原理。还未搞清楚的可以移步这里

   用TextRank提取来提取关键词/摘要,用PageRank的思想来解释它:

  • 如果一个单词出现在很多单词后面的话,那么说明这个单词比较重要
  • 一个TextRank值很高的单词后面跟着的单词,那么这个单词的TextRank值会相应地因此而提高。

   这样 T e x t R a n k TextRank TextRank 的公式就可以由 PageRank 公式改写为:

S ( v i ) = ( 1 − d ) + d ∑ ( j , i ) ∈ ϵ w j i ∑ v k ∈ o u t ( v j ) w j k S ( v j ) S(v_{i})=(1-d)+d \sum _{(j,i) \in \epsilon} \frac {w_{ji}}{\sum _{v_{k} \in out(v_{j})}w_{jk}} S(v_{j}) S(vi)=(1d)+d(j,i)ϵvkout(vj)wjkwjiS(vj)
   T e x t R a n k TextRank TextRank中一个单词 i \large i i 的权重取决于与在 i \large i i 前面的各个单词 j \large j j 组成的 ( j , i ) \large (j,i) (j,i) 这条边的权重,以及 j \large j j这个单词到其他边的权重之和。

   T e x t R a n k TextRank TextRank算法是一种抽取式的无监督的文本摘要方法。让我们看一下TextRank算法的流程:
在这里插入图片描述

  1. 第一步是把所有文章整合成文本数据
  2. 接下来把文本分割成单个句子
  3. 然后,我们将为每个句子找到向量表示(词向量)。
  4. 计算句子向量间的相似性并存放在矩阵中
  5. 然后将相似矩阵转换为以句子为节点、相似性得分为边的图结构,用于句子TextRank计算。
  6. 最后,一定数量的排名最高的句子构成最后的摘要。

二、python代码实现

   jieba和TextRank4zh这2个开源库的中都有实现TextRank。但是两者无论写法和运算规则都有很大出入,结合公式来说jieba做的更符合公式,TextRank4zh更具有准确性,因为TextRank4zh在公式上面做了一定的优化。

Jieba库中的TextRank:

  • 对每个句子进行分词和词性标注处理
  • 过滤掉除指定词性外的其他单词,过滤掉出现在停用词表的单词,过滤掉长度小于2的单词
  • 将剩下的单词中循环选择一个单词,将其与其后面4个单词分别组合成4条边。

例如 : [‘有’,‘媒体’, ‘曝光’,‘高圆圆’, ‘和’, ‘赵又廷’,‘现身’, ‘台北’, ‘桃园’,‘机场’,‘的’, ‘照片’]
对于‘媒体‘这个单词,就有(‘媒体’, ‘曝光’)、(‘媒体’, ‘圆’)、(‘媒体’, ‘和’)、(‘媒体’, ‘赵又廷’)4条边,且每条边权值为1,当这条边在之后再次出现时,权值再在基础上加1.

  • 有了这些数据后,我们就可以构建出候选关键词图 G = ( V , E ) G=(V,E) G=(V,E)
  • 这样我们就可以套用TextRank的公式,迭代传播各节点的权值,直至收敛。
  • 对结果中的Rank值进行倒序排序,筛选出前面的几个单词,就是我们需要的关键词了。

Jieba库中的TextRank源码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
from __future__ import absolute_import, unicode_literals
import sys
from operator import itemgetter
from collections import defaultdict
import jieba.posseg
from .tfidf import KeywordExtractor
from .._compat import *
 
 
class UndirectWeightedGraph:
    d = 0.85
 
    def __init__(self):
        self.graph = defaultdict(list)
 
    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))
 
    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]:
                    s += e[2] / outSum[e[1]] * ws[e[1]]
                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
 
 
class TextRank(KeywordExtractor):
 
    def __init__(self):
        self.tokenizer = self.postokenizer = jieba.posseg.dt
        self.stop_words = self.STOP_WORDS.copy()
        self.pos_filt = frozenset(('ns', 'n', 'vn', 'v'))
        self.span = 5
 
    def pairfilter(self, wp):
        return (wp.flag in self.pos_filt and len(wp.word.strip()) >= 2
                and wp.word.lower() not in self.stop_words)
 
    def textrank(self, sentence, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v'), withFlag=False):
        """
        Extract keywords from sentence using TextRank algorithm.
        Parameter:
            - topK: return how many top keywords. `None` for all possible words.
            - withWeight: if True, return a list of (word, weight);
                          if False, return a list of words.
            - allowPOS: the allowed POS list eg. ['ns', 'n', 'vn', 'v'].
                        if the POS of w is not in this list, it will be filtered.
            - withFlag: if True, return a list of pair(word, weight) like posseg.cut
                        if False, return a list of words
        """
        self.pos_filt = frozenset(allowPOS)
        g = UndirectWeightedGraph()
        cm = defaultdict(int)
        words = tuple(self.tokenizer.cut(sentence))
        for i, wp in enumerate(words):
            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
 
        for terms, w in cm.items():
            g.addEdge(terms[0], terms[1], w)
        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)
 
        if topK:
            return tags[:topK]
        else:
            return tags
 
    extract_tags = textrank

三、TextRank算法使用

   jieba 分词提供的基于TextRank的关键词提取工具。 snownlp
也实现了关键词提取和摘要生成。这里主要介绍 textrank4zh模块中TextRank的使用

1、textrank4zh模块的安装

这里介绍几种安装Python模块的方法,仅供参考。

1)python setup.py install --user
2)sudo python setup.py install
3)pip install textrank4zh --user
4)sudo pip install textrank4zh

textrank4zh模块在python2或python3中均可使用,它所依赖的其他模块要求满足:

jieba >= 0.35; numpy >= 1.7.1;networkx >= 1.9.1
2、实例介绍

textrank4zh对关键字、关键短语和摘要进行抽取.

用到的数据如下图所示:

11月30日,“西学东渐与中国近现代科技转型”研讨会在京举行。中国人民大学哲学院主办,中国人民大学出版社、北京自然辩证法研究会、中国自然辩证法研究会科技哲学史专业委员会、中国人民大学人文社会科学发展研究中心共同协办了此次会议。来自全国38所高校及科研院所的哲学、历史、宗教、文化等领域的70余位专家学者参加了会议。
在开幕式上,中国人民大学哲学院院长郝立新、中国自然辩证法研究会副秘书长董春雨、北京自然辩证法研究会理事长李建军、中国人民大学出版社学术出版中心主任杨宗元以及中国人民大学一级教授刘大椿先后致辞。开幕式由中国人民大学哲学院科技哲学教研室主任刘劲杨主持。
研讨会的第一个议题围绕“西学东渐与史学新观念”展开。中国科学院大学韩琦教授、中国社科院刘国鹏教授和中国科学院刘益东教授对此进行了探讨。
韩琦从全球史的视野对康熙时代的科学传播进行探讨。康熙在位60多年是中外交流最为频繁的时期,荷兰、葡萄牙、俄国、罗马教廷曾派遣使节,法国国王路易十四也派遣“国王数学家”来华。出于“用其技艺”的目的,康熙皇帝在宫廷聘用了很多传教士,不仅编制历算著作,在技术交流方面对中国影响最大的包括火器、玻璃和珐琅技术。刘国鹏则从全球化的事实、知识和心理——晚明至晚清中华帝国面对西学冲击的认知与反馈机制进行阐述。在15世纪全球化的影响下,当时中华帝国基于在政治上的朝贡体系和文化上的华夷之辨总体表现为被动接受下的反馈,进而呈现出两种文明系统以及两种普世价值中的较量。刘益东结合刘大椿的《西学东渐》和《师夷长技》这两本著作,讨论了其在科技史研究上的方法论启示。学者们一致认为,对西学东渐的研究会带来新的史学观念。
第二个议题是关于“西学东渐与东西文化交流”。参与研讨的有:中国社会科学院哲学研究所研究员段伟文、中国人民大学聂敏里教授、山东财经大学王静讲师等。
聂敏里在主旨报告中讨论了西学东渐与两种社会变革模式的重叠与交织:一种是前现代的社会周期性变革模式,另一种是现代的社会加速增长模式。王静作了题为“晚明‘经世致用’内涵的革新与突破”的报告。她认为“经世致用”作为儒学精神,在晚明时因与西方科学互动而有重大革新与突破,不仅是儒学自身发展的逻辑要求,还在于西方科学知识、方法、精神的不断融入。 对于东西文化交流,段伟文在主旨报告中提出了独特的思考视角:“居间人”的互接受性运作。“居间人”就是在联结中国与世界以及科技与社会、政治、经济、文化、教育各个领域都发挥了作用的关键人物。从西学东渐到师夷长技乃至整个中国现当代的科技转型的过程中,“居间人”极大地促成了科技转型过程中科技与各地因素的互接受性。
会议的第三个议题是关于“西学东渐中的历史文本与人物”。参与该议题研讨的有北京大学张大庆教授、山西大学高策教授、山西大学赵云波副教授、中国政法大学郑云艳讲师等。
张大庆在主旨报告中介绍了对《钦定格体全录》的翻译以及传播的再考察。他对照了研究者曾提及的多部解剖著作,进一步明确《周身血脉图》的参考底本,并讨论了该文本的影响和价值。郑云艳考证了《四库全书总目》所录著的真正属于泰西人的著述,认为中、西关系探源需从这些作品的作者身份出发考究,中西学者对“西学”看法的区别须引起重视。赵云波分析了史料《格致书院课艺》中的“西方科学史”内容及当时国人对“西方科学史”的认知及知识来源。高策在主旨报告中论述了杨振宁对西学东渐的认识。
研讨会的第四个议题为“反思近现代科技转型”。参与研讨的专家有:北京师范大学刘孝廷教授、中国人民大学王伯鲁教授、北京航空航天大学徐治立教授、天津行政学院凃明君教授、哈尔滨工业大学(深圳)戴吾三教授、西南大学邱德胜教授、东南大学讲师黄婷、上饶师范学院吴晓东讲师等。
刘孝廷在主旨报告中把中国近现代科学发展阶段概括为:“传教士科学”、“洋务科学”、“唯科学主义”、“救亡科学”、“双料科学”。凃明君讨论了自鸦片战争到甲午战争间中国程式观念现代转型的起步。黄婷在民国“农工之争”历程的基础上,提出民国科技转型在该争论中呈现出哲学理念、认识与实践、表面与实际的“三重错位”的“科技”形象。邱德胜对古代刻漏史料进行了梳理,他认为明朝帝王的抵制、西洋钟表的冲击、时间观念的转变、刻漏技术本身的局限和依附性是中国古代刻漏最终走向衰落的主要原因。戴吾三认为师夷长技催生了洋务运动,洋务运动启动了中国的四次工业化与时代变革,科技与工业的“引进—模仿—创新—输出”模式,启发我们做科技大历史研究,也可作为“西学东渐与中国近代科技转型”的后续研究。王伯鲁指出当代中国科学技术的发展仍呈现基础科学薄弱、工程技术强盛的“跛足”状态,这可视为现代版的李约瑟难题。导致该问题的主要原因包括学以致用的文化基因、资本的选择与市场经济体制不利于基础科学的发展,同时也包括文化、教育等其他因素。徐治立认为新中国以来的科技政策理念体现了“一个中心两个基本点”特征与“持续创新”主线。吴晓东认为目前中国的科技发展迎来转型时期,已逐渐从追赶者成为领跑者,中国科技应获得与其自身实力相匹配的科研话语权,这对中国科技的顺利转型意义重大。
会议闭幕式由中科院科技战略咨询研究院刘海波研究员主持,刘大椿对本次学术研讨会做出总结,会议在热烈的掌声中圆满结束。

import sys
from imp import reload

try:
    reload(sys)
    sys.setdefaultencoding('utf-8')
except:
    pass

import codecs
from textrank4zh import TextRank4Keyword, TextRank4Sentence

file = r'01.txt'

text = codecs.open(file, 'r', 'utf-8').read()

# 创建分词类实例
tr4w = TextRank4Keyword()
# 对文本进行分析,设定窗口大小为2,并将英文单词小写
tr4w.analyze(text=text, lower=True, window=2)

print('关键词为:')
# 从关键词列表中获取前20个关键词
for item in tr4w.get_keywords(num=20, word_min_len=1):
    print(item.word, item.weight)
print('\n')

print('关键短语为:')
# 从关键短语列表中获取关键短语
for phrase in tr4w.get_keyphrases(keywords_num=20, min_occur_num=2):
    print(phrase)
print('\n')


# 创建分句类的实例
tr4s = TextRank4Sentence()
# 英文单词小写,进行词性过滤并剔除停用词
tr4s.analyze(text=text, lower=True, source='all_filters')
print('摘要为:')
for item in tr4s.get_key_sentences(num=3):
    # 打印句子的索引、权重和内容
    print(item.index, item.weight, item.sentence)


# # 绘制句子的重要度曲线
# import matplotlib.pyplot as plt
# import matplotlib as mpl
# import pandas as  pd
# data = pd.DataFrame(data=tr4s.key_sentences)
# mpl.rcParams['font.sans-serif'] = ['SimHei']
# mpl.rcParams['axes.unicode_minus'] = False
# plt.figure(facecolor='w')
# plt.plot(data['weight'], 'ro-', lw=2, ms=5, alpha=0.7, mec='#404040')
# plt.grid(b=True, ls=':', color='#606060')
# plt.xlabel('句子', fontsize=12)
# plt.ylabel('重要度', fontsize=12)
# plt.title('句子的重要度曲线', fontsize=15)
# plt.show()

运行结果:
在这里插入图片描述
在这里插入图片描述

总结

人们通过实验发现:

  • TextRank与TFIDF均严重依赖于分词结果——如果某词在分词时被切分成了两个词,那么在做关键词提取时无法将两个词黏合在一起(TextRank有部分黏合效果,但需要这两个词均为关键词)。因此是否添加标注关键词进自定义词典,将会造成准确率、召回率大相径庭。
  • TextRank的效果并不优于TFIDF。
  • TextRank虽然考虑到了词之间的关系,但是仍然倾向于将频繁词作为关键词。
  • 此外,由于TextRank涉及到构建词图及迭代计算,所以提取速度较慢

参考资料:

  • 4
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是使用Python实现TextRank算法的示例代码: ```python import numpy as np import networkx as nx # 定义TextRank算法类 class TextRank: def __init__(self, sentences, d=0.85, min_diff=1e-5, steps=10): self.sentences = sentences self.d = d # 阻尼系数 self.min_diff = min_diff # 最小差异 self.steps = steps # 迭代次数 self.n_sentences = len(sentences) # 句子数量 # 计算相似度矩阵 def get_similarity_matrix(self): def sentence_similarity(s1, s2): s1 = set(s1.split()) s2 = set(s2.split()) return len(s1 & s2) / (np.log(len(s1)) + np.log(len(s2))) # 初始化相似度矩阵 similarity_matrix = np.zeros((self.n_sentences, self.n_sentences)) for i in range(self.n_sentences): for j in range(i+1, self.n_sentences): similarity_matrix[i][j] = sentence_similarity(self.sentences[i], self.sentences[j]) similarity_matrix[j][i] = similarity_matrix[i][j] # 对相似度矩阵进行归一化处理 for i in range(self.n_sentences): row_sum = sum(similarity_matrix[i]) if row_sum != 0: similarity_matrix[i] /= row_sum return similarity_matrix # 计算TextRank分数 def get_text_rank(self): similarity_matrix = self.get_similarity_matrix() scores = np.ones(self.n_sentences) / self.n_sentences for step in range(self.steps): scores_new = np.ones(self.n_sentences) * (1 - self.d) / self.n_sentences + self.d * similarity_matrix.T.dot(scores) if np.sum(np.abs(scores_new - scores)) <= self.min_diff: break scores = scores_new return scores # 获取关键句子 def get_top_sentences(self, n=3): scores = self.get_text_rank() top_idx = np.argsort(-scores)[:n] top_sentences = [self.sentences[i] for i in top_idx] return top_sentences # 获取摘要 def get_summary(self, n=3): top_sentences = self.get_top_sentences(n) summary = "。".join(top_sentences) return summary # 示例 sentences = [ "Python是一种高级编程语言。", "它已经成为数据科学、机器学习和人工智能等领域的主流编程语言。", "Python有一个简单易学的语法,使其成为初学者和专业人士的首选。", "Python还有大量的第三方库,可以大大加速开发过程。", "你可以使用Python进行Web开发、游戏开发、科学计算等。", "总之,Python是一种非常强大、灵活且易于使用的编程语言。" ] text_rank = TextRank(sentences) summary = text_rank.get_summary() print(summary) ``` 运行结果: ``` Python是一种高级编程语言。它已经成为数据科学、机器学习和人工智能等领域的主流编程语言。总之,Python是一种非常强大、灵活且易于使用的编程语言。 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值