本案例适合作为大数据专业自然语言处理课程的配套教学案例。通过本案例,能够达到以下教学效果:
前言
自然语言处理对我们的生活有着巨大的影响,文档摘要是自然语言处理的诸多应用之一。不断发展的数字化媒体以及越来越快的信息发布速度,我们没有那么多时间去阅读所有的文章、书籍,以此发现对我们有用的信息。文档摘要应运而生。
你有使用过inshorts这款新颖的App吗,它将一篇文章转化为60个词的摘要,这也是我们今天这篇文章所要讲的主题--自动文档摘要。
自动文档摘要是自然语言处理领域最有挑战和趣味的研究问题,它从书籍、文章、博客、论文等文本资源生成精准的、有意义的摘要。由于现实世界中存在着海量的文本数据,我们迫切需要自动文摘这门技术。
通过这篇文章,我们将会探索文档摘要这个领域,学习TextRank算法来完成文档摘要,并用Python进行实现,让我们开始吧。
自动文档摘要从1950年代开始获得关注。在1950年代后期,Hans Peter Luhn发表了一篇标题为 The automatic creation of literature abstracts(自动创建文章摘要)的论文,使用词语频率、词组频率等特征将重要的句子抽取出来作为文档摘要。
另外一篇重要的研究是1960年代后期 Harold P Edmundson完成的,使用了句子中是否出现了标题中的单词以及句子的位置等特征来抽取重要的句子。从那以后,自动文档摘要领域出现了很多重要的、令人激动的研究。
文档摘要可以划分为两个种类 -- 抽取式文档摘要 和 生成式文档摘要。
在这篇案例中,我们关注的是第一种--抽取式文档摘要
在开始介绍TextRank之前,我们先来讲一下于之非常相似的PageRank算法。事实上,TextRank就是受到了PageRank算法思想的启发。PageRank主要是用来给搜索引擎结果排序的。让我们从下面这个例子了解PageRank的基本思想吧。
假设我们有四个网页 -- w1, w2, w3, w4. 这四个网页包含了指向其他网页的链接。其中有一些网页可能没有指向其他网页的链接,这样的网页被称为悬挂页面(dangling pages)。
为了将这些页面排序,我们需要通过PageRank计算每个页面的得分。这个分数表明用户访问这个页面的概率。
为了得到用户从一个页面跳转到另一个页面的概率,我们先构造一个 n*n 的方矩阵 M,n是网页的数量。
矩阵中每个元素代表了用户从一个网页跳转到另一个网页的概率。举例来说,下面这个高亮的部分包含了用户从w1跳转到w2的概率。
矩阵中元素的初始化过程如下:
M[i][j]
初始化为 1/页面i包含的的不重复链接数量
M[i][j]
初始化为0M[i][j]
初始化为 1/所有网页数量在我们的例子中,矩阵 M 被初始化为:
最后,矩阵中的元素将会根据算法在不断的迭代中得到更新,从而得到页面排序
在大致了解PageRank后,现在我们来学习TextRank算法。TextRank与PageRank有很多相似之处:
TextRank是一项抽取式的无监督文档摘要技术。让我们来看一下TextRank用于文档摘要的流程吧:
作为一个网球迷,我经常尝试着浏览尽可能多的在线网球资讯,从而了解这个领域最近发生了什么。然而事实证明,这实在太难了。资讯那么多,而时间总是有限的。
因此,我决定写一个能够扫描大量文章然后给出一篇精准摘要的系统。我该怎么做呢?这就是我在这篇案例想要教给大家的。我们将运用TextRank算法,从抓取的文章集合中构造一篇简洁准确的摘要。
需要注意的是,这项任务是从多篇相同领域文章中得到一篇摘要。本文并未涉及多领域文本摘要--从给出的多篇不同领域文当中得到多篇摘要,不过学习完这个案例,你可以去尝试一下多领域文本摘要。
不多说了,在jupyter notebook中开始动手吧,实现我们上面所学的知识。
导入我们需要用到的库
import numpy as np
import pandas as pd
import nltk
nltk.download('punkt') # 执行一次就可以了
import re
现在让我们读取我们的数据集,已经上传到平台,目录是./input/tennis_articles_v4.csv
。
df = pd.read_csv("./input/tennis_articles_v4.csv")
我们大致看一下数据集中的文本。
df.head()
有三个列 -- 文章id、文章的文本内容、来源。我们感兴趣的是文章的文本内容。可以将一些文本打印出来,看看它们是怎样的。
df['article_text'][0]
现在我们有两个选择:为每篇文档单独创建一篇摘要,或者为所有文章创建一篇摘要。在这里,我们选择后一个,创建一篇总的摘要。
需要将这些文本内容分割成单独的句子,这里用到了nltk库的sent_tokenize()
函数。
from nltk.tokenize import sent_tokenize
sentences = []
for s in df['article_text']:
sentences.append(sent_tokenize(s))
sentences = [y for x in sentences for y in x] # flatten list
让我们打印一些sentences
列表中的句子看看。
sentences[:5]
Glove词向量是词语的向量化表示。这些词向量可以用来构建句子的向量化表示。我们也可以用词袋模型或者TF-IDF方法来为句子构建特征向量,但是这些方法忽略了句子中单词的顺序,而且这样的特征向量通常维数过高。
我们将会使用斯坦福大学提供的在维基百科语料上训练好的GloVe词向量,这个词向量的大小有822MB,由于文件较大,建议读者自行在本地下载,下载链接为:Glove
下面我们来提取词向量。
word_embeddings = {}
f = open('glove.6B.100d.txt', encoding='utf-8')
for line in f:
values = line.split()
word = values[0]
coefs = np.asarray(values[1:], dtype='float32')
word_embeddings[word] = coefs
f.close()
# 词向量的大小
len(word_embeddings)
这400000个词向量被我们存储在字典中,字典中键值对的key
是单词,value
是其对应词向量。
尽可能地除去文本数据的噪音是一个好习惯,下面我们来做一些基本的文本清洗工作。
# 除去标点、数字和特殊字符
clean_sentences = pd.Series(sentences).str.replace("[^a-zA-Z]", " ")
# 左右字母转换成小写
clean_sentences = [s.lower() for s in clean_sentences]
将停用词除去,例如:is, am, this, in等。先要确保已经下载了nltk的停用词,然后导入停用词。
nltk.download('stopwords')
from nltk.corpus import stopwords
stop_words = stopwords.words('english')
我们先定义一个清除句子中停用词的方法,然后应用到所有句子上。
def remove_stopwords(sen):
sen_new = " ".join([i for i in sen if i not in stop_words])
return sen_new
clean_sentences = [remove_stopwords(r.split()) for r in clean_sentences]
通过我们上面创建的词向量字典,clean_sentences
将被用来构建句子的向量表示。
我们先取出句子中单词对应的词向量,每个词向量的维度是100维,将它们相加再取平均,得到的向量就用来表示这个句子。
sentence_vectors = []
for i in clean_sentences:
if len(i) != 0:
v = sum([word_embeddings.get(w, np.zeros((100,))) for w in i.split()])/(len(i.split())+0.001)
else:
v = np.zeros((100,))
sentence_vectors.append(v)
注意:关于文本预处理的更多知识,可以看看这门课程的视频:Natural Language Processing (NLP) using Python
下一步就是计算句子间的相似性,我们将用余弦相似性来衡量句子的相似性。我们先构建一个空矩阵,然后填入句子间的余弦相似度。
初始矩阵的大小是n*n
, n
代表句子数量。
# 构建相似矩阵
sim_mat = np.zeros([len(sentences), len(sentences)])
#使用余弦相似来计算两个句子间的相似度
from sklearn.metrics.pairwise import cosine_similarity
# 使用句子间的相似度初始化矩阵
for i in range(len(sentences)):
for j in range(len(sentences)):
if i != j:
sim_mat[i][j] = cosine_similarity(sentence_vectors[i].reshape(1,100), sentence_vectors[j].reshape(1,100))[0,0]
在进一步处理前,我们现将相似矩阵sim_mat
转化为图。图中的节点用来表示句子,边用来表示句子间的相似度。在这个图上,我们使用PageRank来给句子的排序,得到句子的排序得分,得分越高的句子越重要。
import networkx as nx
nx_graph = nx.from_numpy_array(sim_mat)
scores = nx.pagerank(nx_graph)
终于到最后一步了,抽取排序后前N个句子作为这些文档的摘要。
# 按照句子PageRank分数排序
ranked_sentences = sorted(((scores[i],s) for i,s in enumerate(sentences)), reverse=True)
# 选取前10个句子作为摘要
for i in range(10):
print(ranked_sentences[i][1])
完成啦!我们为这些文档生成了多么棒的、简洁、准确、有用的摘要。
自动文摘是热门的研究领域,在这篇案例中,我们只讨论了冰山一角。再深入一步,我们将会探索生成式文档摘要技巧,深度学习将起到重要作用。此外我们还会探讨下面这些文档摘要任务。
面向特定问题
具体算法
我希望这篇文章能够帮助你了解自动文摘的概念。这项技术有大量的使用场景并且已经被用在了很多非常成功的应用当中。无论是为了提高你的业务表现,还是为了自己的知识,文档摘要是所有NLP积极分子所应该熟悉的。
源自:PRATEEK JOSHI(作者)—— An Introduction to Text Summarization using the TextRank Algorithm (with Python implementation)
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。