TextRank学习笔记

article/2025/8/28 14:35:14

TextRank起源与PageRank

TextRank的灵感来源于大名鼎鼎的PageRank算法,这是一个用作网页重要度排序的算法。

并且,这个算法也是基于图的,每个网页可以看作是一个图中的结点,如果网页A能够跳转到网页B,那么则有一条A->B的有向边。这样,我们就可以构造出一个有向图了。

然后,利用公式:

经过多次迭代就可以获得每个网页对应的权重。下面解释公式每个元素的含义:

S ( V i ) S(V_i) S(Vi) : 网页 V i V_i Vi的重要度(权重),初始值可设为1。
d d d : 阻尼系数,一般为0.85。
I n ( V i ) In(V_i) In(Vi):能跳转到网页 V i V_i Vi的页面,在图中对应入度对应的点。
O u t ( V j ) Out(V_j) Out(Vj):网页 V j V_j Vj能够跳转到的页面,在图中对应出度的点。

可以发现,这个方法只要构造好图,对应关系自然就有了,这实际上是一个比较通用的算法。那么对于文本来说,也是同样的,只要我们能够构造出一个图,图中的结点是单词or句子,只要我们通过某种方法定义这些结点存在某种关系,那么我们就可以使用上面的算法,得到一篇文章中的关键词or摘要。

使用TextRank提取关键词

提取关键词,和网页中选哪个网页比较重要其实是异曲同工的,so,我们只需要想办法把图构建出来就好了。

图的结点其实比较好定义,就是单词喽,把文章拆成句子,每个句子再拆成单词,以单词为结点。

那么边如何定义呢?这里就可以利用n-gram的思路,简单来说,某个单词,只与它附近的n个单词有关,即与它附近的n个词对应的结点连一条无向边(两个有向边)。

另外,还可以做一些操作,比如把某类词性的词删掉,一些自定义词删掉,只保留一部分单词,只有这些词之间能够连边。

下面是论文中给出的例子:

当构图成功以后,就可以利用上面的公式进行迭代求解了。

使用TextRank提取文章摘要

提取关键词以单词为结点,很显然,提取文章摘要自然就是以句子为结点了。那么边呢?如何定义呢?上面的方法似乎不是很适用了,因为两个句子即使相邻,也可以去讲完全不同的两件事。

在论文里,作者给出了一个方法,那就是计算两个句子的相似度。我的理解是这样的,这个计算相似度,其实就是一个比较粗略的方法来判断这两个句子是不是在讲同一个事情,如果两个句子是讲同一个事情,那么肯定会使用相似的单词之类的,这样就可以连一个边了。

既然有了相似度,那么就会有两个句子很相似,两个句子不太相似的情况了,因此,连的边也需要是带权值的边了。

下面是论文中给出的相似度的公式:

S i S_i Si:第i个句子。
w k w_k wk:第k个单词。
∣ S i ∣ |S_i| Si:句子i中单词数。

简单来说就是,两个句子单词的交集除以两个句子的长度(至于为什么用log,没想明白,论文里也没提)。然后还有一点,就是,其他计算相似度的方法应该也是可行的,比如余弦相似度,最长公共子序列之类的,不过论文里一笔带过了。

由于使用了带权的边,因此公式也要进行相应的修改:

上面的公式基本上就是把原来对应边的部分添加了权重,边的数量和改成了权重和,很好理解。

TextRank文章摘要代码实现

我这里只实现了文章摘要的代码,关键词提取也是类似的。要知道,TextRank是不需要训练的,因此,只需要一份测试数据就可以了,数据我是在这里下的:

http://tcci.ccf.org.cn/conference/2018/taskdata.php

上面链接中,Task3就是一个文章摘要的问题,直接下测试集就可以了。

有了测试集,还要有评估方法,这里我用了ROUGE的评价方法,大家有兴趣可以搜一下,这里贴一下简单的代码,如果有误,欢迎指出:

#-*- encoding:utf-8 -*-import os, sysclass Rouge(object):"""docstring for Rouge"""n_gram = 1def __init__(self, n_gram=2):super(Rouge, self).__init__()self.n_gram = n_gramdef get_ngrams(self, text):ngram_set = set()text_length = len(text)max_index_ngram_start = text_length - self.n_gramfor i in xrange(max_index_ngram_start + 1):ngram_set.add(text[i:i + self.n_gram])return ngram_setdef get_test_result(self, evaluated_text, reference_text):evaluated_ngram_set = self.get_ngrams(evaluated_text)reference_ngram_set = self.get_ngrams(reference_text)evaluated_count = len(evaluated_ngram_set)reference_count = len(reference_ngram_set)overlapping_ngrams = evaluated_ngram_set.intersection(reference_ngram_set)overlapping_count = len(overlapping_ngrams)if evaluated_count == 0:precision = 0.0else:precision = overlapping_count * 1.0 / evaluated_countif reference_count == 0:recall = 0.0else:recall = overlapping_count / (reference_count + 1e-8)f1_score = 2.0*precision*recall/(precision + recall + 1e-8)return {"f1_score": f1_score, "precision": precision, "recall": recall}

接下来是TextRank的实现,这里有两个问题,一个是分句,一个是分词。分句的话,我就是用简单的结束符号来分割(。?!)。分词的话,我使用了jieba分词,在github上可以搜一下。另外,由于一个句子中的单词其实并不多的,所以我就用了邻接矩阵来实现(主要是懒),这样写还有个好处就是,上面的公式可以用矩阵乘法实现了,写起来很简洁。

#-*- encoding:utf-8 -*-import os, sys, math
import numpy as np
import jiebaclass SplitWords(object):"""docstring for SplitWords"""sentence_end_words = u'。?!'def __init__(self):super(SplitWords, self).__init__()def split_sentence(self, text):results = []n = len(text)now_text = ''for i in xrange(n):ch = text[i]if ch in self.sentence_end_words:results.append({'sentence': now_text, 'key_words':set()})now_text = ''else:now_text += chif now_text != '':results.append({'sentence': now_text, 'key_words':set()})return resultsdef process(self, text):results = self.split_sentence(text)for item in results:seg_list = jieba.cut(item['sentence'], cut_all=False)for word in seg_list:item['key_words'].add(word)return resultsclass TextRank(object):"""docstring for TextRank"""def __init__(self, iter_times=20, d=0.85):super(TextRank, self).__init__()self.iter_times = iter_timesself.d = dself.word_spilter = SplitWords()def cal_sentence_similarity(self, sentence_item1, sentence_item2):intersect_item = sentence_item1['key_words'].intersection(sentence_item2['key_words'])intersect_count = len(intersect_item)n1 = len(sentence_item1['key_words'])n2 = len(sentence_item2['key_words'])# print intersect_count, n1, n2similarity_value = intersect_count / (math.log(n1 + 1e-8) + math.log(n2 + 1e-8) + 1e-8)return similarity_valuedef sort_sentences(self, splited_words, ver_ws):temp_list = []for i, item in enumerate(splited_words):temp_list.append((ver_ws[i][0], item['sentence']))temp_list.sort()return temp_listdef process(self, text):splited_words = self.word_spilter.process(text)sentence_length = len(splited_words)weights = np.zeros((sentence_length, sentence_length), dtype=float)mul_mat = np.zeros((sentence_length, sentence_length), dtype=float)for i in range(sentence_length):for j in range(i + 1, sentence_length):weights[i][j] = weights[j][i] = self.cal_sentence_similarity(splited_words[i], splited_words[j])for i in range(sentence_length):for j in range(sentence_length):if weights[i][j] == 0:continuemul_mat[i][j] = weights[j][i]/(weights[j].sum() + 1e-8)ver_ws = np.ones((sentence_length, 1), dtype=float)for i in range(self.iter_times):ver_ws = (1 - self.d) + self.d * np.dot(mul_mat, ver_ws)# print ver_wsreturn self.sort_sentences(splited_words, ver_ws)

最后是测试代码:

#-*- encoding:utf-8 -*-import sys
import json
import codecs
from optparse import OptionParser
from textrank4zh import TextRank4Keyword, TextRank4Sentence
from rouge import Rouge
from text_rank import TextRankdef load_data(input_file):data_list = []with codecs.open(input_file, 'r', encoding='UTF-8') as f:for line in f:line_data = json.loads(line.encode('utf-8'))# print line_data['summarization']# print line_data['article']data_list.append(line_data)return data_listdef run(options):rouge_tester = Rouge(2)data_list = load_data(options.input_file)tr4s = TextRank4Sentence()my_text_rank = TextRank()avg_result = {'f1_score': 0, "precision": 0, "recall": 0}# total_count = 0for item in data_list:article = item['article']reference_text = item['summarization']# tr4s.analyze(text=article, lower=True, source = 'all_filters')evaluated_text = ''#for item in tr4s.get_key_sentences(num=1):#    # print(item.index, item.weight, item.sentence)#    evaluated_text = item.sentencerank_results = my_text_rank.process(article)evaluated_text = rank_results[-1][1]test_result = rouge_tester.get_test_result(evaluated_text, reference_text)print evaluated_text, reference_textprint test_resultprint avg_resultfor key,value in test_result.items():avg_result[key] += valuen = len(data_list)print 'n:{}'.format(n)for key,value in test_result.items():avg_result[key] /= (n + 1e-8)print avg_resultdef init_options():parser = OptionParser()parser.add_option('-i', '--input_file', dest='input_file', default='Downloads/TTNewsCorpus_NLPCC2017/toutiao4nlpcc_eval/evaluation_with_ground_truth.txt', help='the input file')(options, arges) = parser.parse_args()print optionsreturn optionsif __name__ == '__main__':options = init_options()run(options)

http://chatgpt.dhexx.cn/article/wOvLrldC.shtml

相关文章

【TextRank】关键词提取 算法原理 公式推导 源码分析

1.前言 在介绍TextRank前,我想先给大家介绍下PageRank,实质上个人认为可以把TextRank当做PageRank2.0。 谷歌的两位创始人的佩奇和布林,借鉴了学术界评判学术论文重要性的通用方法,“那就是看论文的引用次数”。由此想到网页的重要…

NLP - 关键词提取 - TextRank

NLP - 关键词提取 - TextRank 一、TextRank介绍二、PageRank介绍三、PageRank计算过程四、关键词提取任务 一、TextRank介绍 TextRank算法则可以脱离语料库的基础,仅对单篇文档进行分析就可以提取该文档的关键词。这也是TextRank算法的重要特点。TextRank算法的基本…

textrank算法原理与提取关键词、自动提取摘要PYTHON

首先介绍原理与概念 TextRank 算法是一种用于文本的基于图的排序算法。其基本思想来源于谷歌的 PageRank算法(其原理在本文在下面), 通过把文本分割成若干组成单元(单词、句子)并建立图模型, 利用投票机制对文本中的重要成分进行排序, 仅利用单篇文档本…

TextRank算法总结

TextRank算法总结 最近在调研自动生成文本方面的内容,突然想到了自动文摘里的textRank,这里我将参考了一些资料并对这些知识点进行了整理总结,初步总结如下: 目录 PageRank简介基于TextRank的关键词提取基于TextRank的关键词短语提…

TextRank算法实践

TextRank算法实践 PageRank算法思想 TextRank算法的思想主要源于PageRank算法,PageRank算法主要用于给互联网网页排序,根据网页之间的跳转来构造一个初始权重矩阵(转移矩阵),默认每个网页质量都是1 使用一个向量v&…

TextRank算法的基本原理及textrank4zh使用实例

TextRank算法是一种文本排序算法,由谷歌的网页重要性排序算法PageRank算法改进而来,它能够从一个给定的文本中提取出该文本的关键词、关键词组,并使用抽取式的自动文摘方法提取出该文本的关键句。其提出论文是: Mihalcea R, Tarau P. TextRank: Bringing order into texts[…

TextRank算法

TextRank算法理解 TextRank算法 TextRank算法基于PageRank,用于为文本生成关键字和摘要。其论文是: Mihalcea R, Tarau P. TextRank: Bringing order into texts[C]. Association for Computational Linguistics, 2004. 先从PageRank讲起 在浅入浅出…

TextRank

TextRank与PageRank TextRank的灵感来源于大名鼎鼎的PageRank算法,这是一个用作网页重要度排序的算法。 这个算法是基于图的,每个网页可以看作是一个图中的结点,如果网页A能够跳转到网页B,那么则有一条A->B的有向边。这样&am…

TextRank算法介绍及实现

目录 1、PageRank算法 2、TextRank算法 (1)关键词抽取(keyword extraction) (2)关键短语抽取(keyphrase extration) (3)关键句抽取(sentence…

TextRank原理解释

目录 1. PageRank原理 2. TextRank (1)TextRank需要满足的条件 (2)TextRank思想的简要理解 (3)TextRank原理及例子讲解 1. PageRank原理 在这里可以看我转载的PageRank原理链接,比较详细h…

TextRank算法原理简析、代码实现

前言—PageRank 注:PageRank原理另行查询 在介绍TextRank前,我想先给大家介绍下PageRank,实质上个人认为可以把TextRank当做PageRank2.0。   谷歌的两位创始人的佩奇和布林,借鉴了学术界评判学术论文重要性的通用方法&#xff0…

NLP学习笔记——TextRank算法

一、算法简介 TextRank算法是一种基于图的排序算法,由谷歌的网页重要性排序算法PageRank算法改进而来,主要应用有关键词提取、文本摘要抽取等。该算法的主要思想是:把文档中的词(句)看成一个网络,词&#…

机器学习——逻辑回归常见面试题整理

逻辑回归 1.介绍 逻辑回归假设数据服从伯努利分布,通过极大化似然函数的方法,运用梯队下降来求解参数,来达到将数据二分类的目的。 2.逻辑回归的损失函数和梯度下降参数迭代方法 逻辑回归的损失函数是它的极大似然函数 参数迭代方法 3.逻…

面试精选逻辑推理题总结

类似的杀人游戏 1、500张骨牌整齐地排成一行,按顺序编号为1、2、3、……、499、500。第一次拿走所有奇数位置上的骨牌,第二次再从剩余骨牌中拿走奇数位置上的骨牌,以此类推。请问最后剩下的一张骨牌的编号是?(256&…

IT科技企业逻辑思维面试题

逻辑思维面试题 一、假设有一个池塘,里面有无穷多的水。现有2个空水壶,容积分别为5升和6升。问题是如何只用这2个水壶从池塘里取得3升的水。【请描述操作过程】 答:(1)先用容积为6升的水壶装满水; &#…

面试逻辑题分享--字母数字映射关系推算题

越来越多的朋友可能会发现,在现在找工作的时候,经常会遇到一些笔试题,而且其中不乏有逻辑题,企业希望通过一些逻辑题的测试,来判断求职者的一个逻辑思维能力。今晚在群里看到有小伙伴发了一个题,一时兴起&a…

前端_逻辑题 1

题目一 5只鸡5天能下5个蛋,100天下100个蛋需要多少只鸡? 1只鸡5天能下1个蛋,1只鸡100天能下20个蛋,所以100天下100个蛋需要5只鸡。 题目二 两个盲人都各自买了一对黑袜和一对白袜,四对袜子的布质、大小完全相同&#…

华为软件测试笔试真题之变态逻辑推理题【二】华为爆火面试题

“一头牛重800公斤,一座桥承重700公斤,问牛怎么过桥?” 这个问题在知乎上被浏览过13672927次,火热程度可见一斑。 据说这是华为的面试题,看似不合理的题目和国际闻名的大厂,极大的勾起了人们的兴趣。 不像面…

二、逻辑回归LR面试题总结

1. 简单介绍一下逻辑回归? 逻辑回归主要用来解决分类问题,线性回归的结果 Y Y Y带入一个非线性变换的Sigmoid函数中,得到 [ 0 , 1 ] [0,1] [0,1]之间取值范围的数 S S S, S S S可以把它看成是一个概率值,如果我们设置…

互联网面试——.Net 面试题

提供了最常见的 .Net 面试问题和许多公司提出的答案。让我们看看顶级 Dot Net 面试问题列表。 1. 什么是.NET? .NET 是一种软件开发框架。它就像其他软件开发框架(J2EE)一样。它以类库和 API 的形式提供运行时功能和一组丰富的预构建功能。此…