目录
简易中文自动文摘系统(一):绪论
自动文摘的介绍
自动文摘分类
简易中文自动文摘系统(二):中文语料库的准备
中文语料库
jieba分词
简易中文自动文摘系统(三):模型训练
词向量
word2vec与自然语言模型
模型训练
简易中文自动文摘系统(四):TextRank算法实现
PageRank算法
TextRank
1.文本预处理
2.句子相似度计算
3.句子权重计算
4.句子排序
5.生成文摘
6.主函数
简易中文自动文摘系统(五):自动文摘实现及总结
随着大数据时代的到来,对于海量数据,自然语言处理越来越成为计算机科学和深度学习、人工智能领域重要研究方向。自然语言处理基于语言学、计算机科学、统计学等基础科学并重点集成于语言分析的人工智能一大分支。因此,自然语言处理的研究主要涉及自然语言,即人们生活中交流使用的语言,目前来说,国际上主流的语言有汉语、英语、法语、德语等等,它们的单词,句型,语法等都完全不相似,各有各的特点,因此自然语言的研究对于不同语言有着不同的处理方式,这也是自然语言处理的困难之一。总而言之,自然语言处理既与计算机科学息息相关,也与自然语言学有重要联系,但它也不完全等同于计算机科学+自然语言学。自然语言处理重点是对文章的语法语义语境进行分析处理,并经过一些算法,得能够有效分析处理人类自然语言的计算机系统及核心算法。
人工智能的出现和深度学习的普及,自动文摘的提出,让传统人工文章摘要地位受到冲击。通过获取不同的自然语言,并使用计算机系统对文档进行语法语句语义的分析处理,生成一篇文章的摘要,可以做到对文摘的主要内容进行提取,节省读者的不必要阅读时间,大大提高了文章的阅读效率。但是传统的人工编写文章摘要在大数据时代,成本过于昂贵,并且得到优秀的文章摘要需要耗费大量时间,效率低下,显得力不从心,因此使用计算机进行自然语言处理快速得到文章的摘要技术得到飞速发展。
目前自动文摘的方法主要有两种:机械文摘和理解文摘。
机械文摘是抽取式的自动文摘方法,通过提取文档中已存在的关键词和句子形成摘要。机械文摘适用范围广,已经有应用于实际工程的抽取式自动文摘系统。抽取式自动文摘,顾名思义,是从整篇文章中获取一些关键句,将这些句子有机组合,得到一篇文章摘要,期间不修改句子本身。其中涉及到的技术有关键词、关键句抽取,句子语义分析,从而完成一篇简单自动文摘的目的。
理解文摘是生成式的自动文摘方法,通过自然语言处理对文章的内容进行分机器分析、理解,再使用自然语言生成技术,生成不同于文章中原有句子的自动文章摘要。一般来说,抽理解文摘可以比抽取更有效地压缩文本,但是可以做到这一点的自动文摘系统更难以开发,因为它们需要使用自然语言生成技术,而自然语言生成技术本身就是一个仍在不断发展的技术。
自动文摘是人工智能、自然语言处理领域的一个重要研究方向,经过50多年的研究发展,自动文摘技术已经可以运用于一部分自动文摘任务,但仍需突破很多关键技术,才能提高其应用价值、扩大其应用范围。
前文已经提及,汉语的语法和句型不同于英语,在进行分析之前需要进行分词。首先我们需要的是一个中文语料库,本文使用的是维基百科的中文语料库,大小约为1.57GB,下载之后从中提取中文语料库。再使用opencc将语料库繁简体转化之后,使用jieba分词将语料库中的段落、句子进行分词得到最终用于模型训练的中文语料库。
本文使用的中文语料库为维基百科的中文语料库。通过以下链接进行下载:
http://download.wikipedia.com/zhwiki/latest/zhwiki-latest-pages-articles.xml.bz2
下载之后得到的是一个压缩包:
下载WikiExtractor.py文件,该脚本文件是意大利程序员使用Python研发的维基百科抽取器,简洁并且功能强大,可以抽取出维基百科中文语料库的内容并输出到文本文件。我们在macOS的终端运行以下命令行,使用Wikipedia Extractor抽取正文文本:
bzcat zhwiki-latest-pages-articles.xml.bz2 | python WikiExtractor.py -b 1000M -o extracted >output.txt
其中-b 1000M是将文本以1000M大小为单位进行分割;output.txt存储的是输出过程中日志信息而非所抽取的正文文本。
上图是抽取出的语料库,我们发现是繁体中文库。
我们这里使用opencc对语料库进行简化。同样,在macOS终端输入以下命令行:
opencc -i wiki_00 -o zh_wiki_00 -c zht2zhs.ini
opencc -i wiki_01 -o zh_wiki_01 -c zht2zhs.ini
得到简体中文的语料包zh_wiki_00和zh_wiki_01。简化后如下:
jieba分词是支持Python语言的中文分词组件。jieba分词在深度学习,自然语言处理中有着广泛使用,用法简单。
# encoding=utf-8
import jieba
import jieba.posseg as pseg
seg_list = jieba.cut("我来到南京邮电大学", cut_all=True)
print("Full Mode: " + "/ ".join(seg_list)) # 全模式
seg_list = jieba.cut("我来到南京邮电大学", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list)) # 精确模式
seg_list = jieba.cut("他就职于华为南京研究所") # 默认是精确模式
print("/".join(seg_list))
seg_list = jieba.cut("小明本科毕业于南京邮电大学,后在北京邮电大学深造")
print("/".join(seg_list))
seg_list = jieba.cut_for_search("小明本科毕业于南京邮电大学,后在北京邮电大学深造") # 搜索引擎模式
print("/".join(seg_list))
words = pseg.cut("我爱南京的新街口")#查看词性
for word, flag in words:
print('%s %s' %(word, flag))
输出结果如下:
运行以下代码,完成语料库的分词:
# encoding = utf-8
import jieba
import codecs
import re
f = codecs.open('cut_zh_wiki_00.txt', "a+", 'utf-8')
for line in open("zh_wiki_00"):
for i in re.sub('[a-zA-Z0-9]', '', line).split(' '):
if i != '':
data = list(jieba.cut(i, cut_all = False))
readline = ' '.join(data) + '\n'
f.write(readline)
f.close()
分词结果如下:
由于在本实验中的停用词对结果影响可以忽略,所以本文并未进行去停用词,具体的去停用词方法读者可以自行上网查阅资料参考,去除停用词后语料库的数据将更为干净,结果更为精确。
词向量是自然语言处理(Natural Language Processing)中的一组语言建模和特征学习技术的名称,其中来自语料库的单词短语对应着唯一的多维向量。理论上,词向量涉及到了从每一个单词的高维度向量空间到低维度的连续向量空间的多维向量。生成这种映射的方法包括降维词共现矩阵,神经网络,基于概率的模型以及单词所在语境显示。已经有研究表明,单词和短语向量作为基础输入表示时,可以提高NLP任务的性能,如句法分析和情感分析。
最初的词向量是冗长的,它使用的是词向量维度大小为整个单词表的大小,对于每个具体的单词,将其对应位置置1。如一个由6个单词组成的单词表,单词“北京”序号为1,“上海”序号为2,“南京”序号为3,则它们的词向量为(1,0,0,0,0,0),(0,1,0,0,0,0)和(0,0,1,0,0,0)。这种编码称之为离散表示(One hot representation)。显然,对于中文词库,需要的向量维数过于庞大,但也带来一个好处,就是在高维向量中,很多应用任务线性可分。
分布式表示(Dristributed representation)可以解决离散表示所带来的问题。它的思路是通过对于给定的某一特定自然语言的语料库进行训练,将语料库中每个单词都映射到一个相对维度较低的词向量上来。这些单词构成的低维向量集合构成了一个自然语言的向量空间,进而可以用统计学的方法来研究词与词之间的潜在关系。这个低维的词向量的维度一般需要我们在训练时自己来指定。如对于上文同样的6个单词的单词表,“北京”的词向量可能为(0.9,0.19,0.25),“上海”的词向量可能为(0.9,0.31,0.85),这样子就完成了较低维度的词向量来表示较多的单词,这个低维词向量的维度和具体值在不同的实际情况中有所不同。这种方式的词向量模型使得各个单词之间有了相似关系,对于自然语言处理产生帮助。
目前的词向量(通常是向量空间模型)的主要限制之一是单词的可能含义被合并成单个表示(语义空间中的单个矢量)。基于感知的词向量模型是解决这个问题的方法:单词的单词意义在向量空间中表示为不同的向量。但是目前来说发展较为局限,进展缓慢。
目前仍有许多科研人员和高校研究院在研究词向量模型的优化与降维。 2013年,Google公司计算机科学家Tomas Mikolov领导的一个科研团队创建了word2vec,这个词向量工具包可以比以前的方法更快地训练矢量空间模型。目前来说,许多新兴词向量技术更多的依赖于神经网络,替代了早期广泛流行的n-gram模型和无监督学习模型。
word2vec(word to vector)是Google公司于2013年推出的用于自然语言处理的深度学习开源训练集,是一组用于训练生成词向量的模型, 该神经网络模型层数不多,一般为两层,经过训练可重构语言的语境。word2vec用于将单词转换为多维词向量。它可以将百万数量级的语料库进行高效训练,通过所给语料库中各个单词的位置和词频等信息算出各个词向量。我们可以根据向量与向量之间的关系,来分析单词与单词之间的关系,挖掘文档中单词的联系。word2vec将输入的大量文本作为输入,并生成一个向量空间,通常为几百个维度,每个唯一的单词在语料库中被分配一个相应的空间向量。词向量位于向量空间中,使得在语料库中共享公共上下文的词在空间中彼此靠近。
自然语言模型,就是通过对自然语言的单词,句子进行建模,表示为计算机可以理解并处理的数学语言,使得计算机将其视为机器语言,并使用机器语言的处理方式来处理并分析人类的自然语言。word2vec可以利用两种模型架构中的任何一种来产生分布式的词语表达:CBOW连续词袋(continuous bags of words)模型或连续跳跃词(skip-gram)模型。在连续词袋结构中,模型从周围环境词的窗口中预测当前词,上下文词语的顺序不影响预测(词袋假设)。在连续跳跃式体系结构中,模型使用当前词来预测环境词的周围窗口。skip-gram体系结构权衡附近的上下文词语比距离更远的上下文词语重。相对来说,CBOW速度快,而skip-gram速度较慢,但对于处理不常见的词语,后者做得更好。
Python中word2vec的模型训练十分简单,我们只需导入gensim.models下的word2vec包,并使用之前获取的已分词的维基百科中文语料库进行训练,得到可用于中文文档的词向量模型。代码如下:
from gensim.models import word2vec
import logging
logging.basicConfig(format = '%(asctime)s : %(levelname)s : %(message)s', level = logging.INFO)
sentences = word2vec.LineSentence(u'./cut_zh_wiki_00.txt')
model = word2vec.Word2Vec(sentences, size=200, window=10, min_count=64, sg=1, hs=1, iter=10, workers=25)
model.save(u'./word2vec2');
其训练时间较长,本人的计算机配置(2.8GHz四核 Intel Core i7,16GB内存,Intel HD Graphics 630 1536 MB)所需要训练时间约为3小时。训练结束,我们得到了基于维基百科中文语料的中文词向量模型word2vec2。接下来我们使用一组简单的单词来测试模型。测试代码如下:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from gensim.models import word2vec
model = word2vec.Word2Vec.load("word2vec2")
s = model.similarity(u'南京', u'上海')
print "'南京'与'上海'的相似度为:",s
s = model.similarity(u'南京', u'北京')
print "'南京'与'北京'的相似度为:",s
print
print("与'南京'最相近的单词:")
result = model.most_similar(u'南京')
for each in result:
print each[0], each[1]
print
print("与'邮电'最相近的单词:")
result = model.most_similar(u'邮电')
for each in result:
print each[0], each[1]
print
print("与'大学'最相近的单词:")
result = model.most_similar(u'大学')
for each in result:
print each[0], each[1]
结果如下图:
可以根据实际情况分析,我们训练出的模型的词向量关系可信度较高,也说明维基百科中文语料库的资料较为准确,可以用于接下来的自动文摘研究。
PageRank是由拉里佩奇和谢尔盖布林于1996年在斯坦福大学开发的,适用于新兴搜索引擎的内核算法部分之一。谢尔盖布林认为,互联网中的所有页面都适用于一种特殊的层次结构:在一个页面中有越多的指向其他网页的链接,那么这个页面所获得的评分就越高,权值越大。由Rajeev Motwani和Terry Winograd于1998年联合撰写的一篇文章中,提出来PageRank算法思想和谷歌搜索引擎的最早版本;不久之后,拉里佩奇和谢尔盖布林基于谷歌搜索引擎成立了著名的谷歌公司。虽然PageRank算法得到的网页评分只是决定Google搜索结果排名的众多因素之一,但PageRank至今仍然是所有谷歌网络搜索工具的基础。
PageRank算法输出一个概率分布,用于表示随机点击链接的人可能会到达任何特定页面的可能性。 可以为任意大小的文档集合计算PageRank。在一些研究论文中假定在计算过程开始时,分布在集合中的所有文档中均匀分布。PageRank计算需要多次通过,称为“迭代”,通过集合来调整近似的PageRank值,以更贴近地反映理论的真实值。
概率表示为介于0和1之间的数值。通常将0.5概率表示为发生某事的“50%几率”。 因此,0.5的PageRank意味着点击随机链接的人有50%的机会将被导向0.5 PageRank的文档。
那么根据以上设计思想,佩奇设计了下面的计算公式:
这就是著名的PageRank算法公式。其中,S(V_i)是网页i的重要性(PR值);d是阻尼系数,一般为0.85;In(V_i)是整个互联网中所存在的有指向网页i的链接的网页集合;Out(V_j)是网页j中存在的指向所有外部网页的链接的集合;|Out(V_j)|是该集合中元素的个数。
上图是一个简单的PageRank网络数学模型,以百分比表示(Google使用对数标度)。即使链接到C的链接较少,页面C的PageRank也高于页面E,这是由于页面C的输入链和输出链来自一个评分很高的页面B,因此页面C也会具有较高的评分。如果从随机页面开始的网络浏览者有85%的可能性从他们正在访问的页面中选择一个随机链接,并且有15%的可能性跳到整个网络中随机选择的页面,他们最终有8.1%的概率停留在页面E。(跳到任意页面的15%可能性对应于85%的阻尼因子)没有阻尼,所有网页浏览者最终都会在页面A,B或C上结束,而所有其他页面都会使PageRank为零。在存在阻尼的情况下,即使网页没有自己的外发链接,页面A也可以有效链接到网页中的所有页面。根据上图可以绘制出如下表格:
我们把这张表格称为交叉矩阵表,它反映了在一个互联网中,各个网页之间的链接关系,通过这个表格可以直接计算每张网页被其它网页引用的次数,在使用科学的公式进行计算,可以粗略算出这张网页在互联网上的重要程度。
TextRank是一种用于自然语言处理的通用基于图的排序算法。本质上,它在专门为特定NLP任务设计的图表上运行PageRank。对于关键词提取,它使用一些文本单元集作为顶点来构建图。边是基于文本单元顶点之间的语义或词汇相似度的度量。与PageRank不同的是,边缘通常是无向的,可以加权以反映相似程度。一旦图形被构建,它就被用来形成一个随机矩阵,结合一个阻尼因子(如在“随机冲浪模型”中),并且通过找到对应于特征值1的特征向量来获得顶点的排名(即在图上随机游走的平稳分布)。
TextRank算法公式如下:
其中,WS(V_i)表示句子i的权重(weight sum),右侧的求和表示每个相邻句子对本句子的贡献程度。在单文档中,我们可以粗略认为所有句子都是相邻的,不需要像多文档一样进行多个窗口的生成和抽取,仅需单一文档窗口即可。求和的分母w_ji表示两个句子的相似度,而分母仍然表示权重,WS(V_j)代表上次迭代出的句子j的权重,因此,TextRank算法也是类似PageRank算法的、不断迭代的过程。
def cut_sentences(sentence):
puns = frozenset(u'.')
tmp = []
for ch in sentence:
tmp.append(ch)
if puns.__contains__(ch):
yield ''.join(tmp)
tmp = []
yield ''.join(tmp)
def two_sentences_similarity(sents_1, sents_2):
counter = 0
for sent in sents_1:
if sent in sents_2:
counter += 1
return counter / (math.log(len(sents_1) + len(sents_2)))
def cosine_similarity(vec1, vec2):
tx = np.array(vec1)
ty = np.array(vec2)
cos1 = np.sum(tx * ty)
cos21 = np.sqrt(sum(tx ** 2))
cos22 = np.sqrt(sum(ty ** 2))
cosine_value = cos1 / float(cos21 * cos22)
return cosine_value
def compute_similarity_by_avg(sents_1, sents_2):
if len(sents_1) == 0 or len(sents_2) == 0:
return 0.0
vec1 = model[sents_1[0]]
for word1 in sents_1[1:]:
vec1 = vec1 + model[word1]
vec2 = model[sents_2[0]]
for word2 in sents_2[1:]:
vec2 = vec2 + model[word2]
similarity = cosine_similarity(vec1 / len(sents_1), vec2 / len(sents_2))
return similarity
def calculate_score(weight_graph, scores, i):
length = len(weight_graph)
d = 0.85
added_score = 0.0
for j in range(length):
denominator = 0.0
fraction = weight_graph[j][i] * scores[j]
for k in range(length):
denominator += weight_graph[j][k]
if denominator == 0:
denominator = 1
added_score += fraction / denominator
weighted_score = (1 - d) + d * added_score
return weighted_score
def weight_sentences_rank(weight_graph):
scores = [0.5 for _ in range(len(weight_graph))]
old_scores = [0.0 for _ in range(len(weight_graph))]
while different(scores, old_scores):
for i in range(len(weight_graph)):
old_scores[i] = scores[i]
for i in range(len(weight_graph)):
scores[i] = calculate_score(weight_graph, scores, i)
return scores
def summarize(text, n):
tokens = cut_sentences(text)
sentences = []
sents = []
for sent in tokens:
sentences.append(sent)
sents.append([word for word in jieba.cut(sent) if word])
sents = filter_model(sents)
graph = create_graph(sents)
scores = weight_sentences_rank(graph)
sent_selected = nlargest(n, zip(scores, count()))
sent_index = []
for i in range(n):
sent_index.append(sent_selected[i][1])
return [sentences[i] for i in sent_index]
if __name__ == '__main__':
with open("njuptcs.txt", "r") as njuptcs:
txt = [line.strip() for line in open("njuptcs.txt", "r").readlines()]
text = njuptcs.read().replace('\n', '')
print '原文为:'
for i in txt:
print i
summarize_text = summarize(text, 5)
print '摘要为:'
for i in summarize_text:
print i
至此,TextRank算法实现的单文档自动文摘完成,下一章我们看看成果。
经过上述的工作以及编程,我们使用Python已经设计出简要的中文自动文摘系统。我们选取一段关于南京邮电大学计算机学院、软件学院、网络空间安全学院的简介,选择其中经过本文模型训练选择的权重最高的5句话作为摘要。
输出结果为:
本实验中使用的中文语料库为维基百科中文语料库,维基百科中文语料库的质量较高,而且领域广泛(适合本文研究的问题),经过测试模型的结果也是如此——对于“北京”、“南京”、“大学”等词的测试都较为准确。当然它的缺点也有,突出缺点就是语料库的语句数量较少,相比于国内的北大中文语料库、哈工大中文语料库、百度百科中文语料库和互动百科中文语料库等千万级别的语料库,数据量要少一个数量级(约91万条),最明显的是对于一些中文特有的人名的识别。的确,在实验的文章中有一个单词并未实现分词:“余人次”,故本人将该词替换为“多人”,成功识别。其中分词使用到的是jieba分词,jieba分词作为一个python的中文分词外部包,安装与使用非常方便,功能也非常强大,基本可以完成对日常中文语句的精确分词。
本文介绍了PageRank算法和TextRank算法,并重点研究了TextRank算法对单一文档中各个句子、单词的权重计算,做出评价分析进行重点语句筛选,抽取出文章摘要这一过程的原理。
文章重点研究了机械文摘的抽取式自动文摘算法,并用其实现了简单中文文档的自动文摘生成。并以一篇南京邮电大学计算机学院、软件学院、网络空间安全学院的简介为例,对该文章进行分句、分词,使用得到的模型进行训练,得到自动文摘。诚然,本文并未进行模型评价和优化,这对于在进一步的研究中可以对模型进行评价和优化。
随着大数据时代的到来,指数级的数据增长对数据信息筛选提出更高的要求。国外Google、Amazon和国内百度、腾讯等公司早已对自然语言理解进行研究,国内外各大高等院校(如麻省理工学院、北京大学、北京邮电大学、清华大学、上海交通大学、哈尔滨工业大学等)也对自动文摘进行了卓有建树的研究。因此,在大数据+人工智能的时代,从学习Python编程语言,建立集成开发环境,学习中文语料的处理方法,查阅自然语言处理的资料,学习自动文摘模型,学习TextRank算法的原理,到实现单文档自动文摘算法,都要通过不断学习技术和知识来实现。
本文参考:https://blog.csdn.net/reigns_
对自动文摘五篇进行了合集汇总和修改