赞
踩
最近总算是忙完了毕业论文的事情,也放松了一段时间,很久没有写博客了。之前逛Medium有收藏了很多有意思的文章,就做个搬运和大家一起学习。这篇文章主要介绍的是作者做的一个对多种语言的邮件进行无监督摘要抽取的项目,非常详细。文本摘要也是非常有意思的NLP任务之一,可能之后会涉及相关的项目,所以就先提前学习啦~
文本摘要是从一个或多个源中提取最重要信息,并为特定用户(或多个用户)和任务(或多个任务)生成简短版本的过程。
---- Advances in Automatic Text Summarization, 1999.
文本摘要对于人类来说是非常简单的,因为人类天生地具有理解自然语言的能力,并可以提取显著特征以使用自己的文字来总结文档的重点。但是,在当今世界中数据爆炸增长,缺乏人力和时间来解析数据,因此自动文本摘要方法至关重要,主要有以下几个原因:
文本摘要方法可以被总结为以下不同的类别:
文本摘要实现主要是参考Unsupervised Text Summarization Using Sentence Embeddings这篇论文,可以分解成以下过程:
以英文邮件为例,看看是怎么得到最终的摘要的。
常规操作,永远没有干净的数据,自己动手丰衣足食。下面以常见的英文邮件为例:
Hi Jane,
Thank you for keeping me updated on this issue. I'm happy to hear that the issue got resolved after all and you can now use the app in its full functionality again.
Also many thanks for your suggestions. We hope to improve this feature in the future.
In case you experience any further problems with the app, please don't hesitate to contact me again.
Best regards,
John Doe
Customer Support
1600 Amphitheatre Parkway
Mountain View, CA
United States
可以看出,邮件起始的问候与末尾的署名对我们的文本摘要任务是毫无作用的,所以我们需要首先去除这些无关因素,否则会使得模型混淆。为此,我们可以借用Mailgun Talon github库中的部分代码,该代码还可以删除空行。
# clean()函数改写了上面github库中代码以清洗邮件
cleaned_email, _ = clean(email)
lines = cleaned_email.split('\n')
lines = [line for line in lines if line != '']
cleaned_email = ' '.join(lines)
当然,如果不想自己写clean()函数的话,也可以直接调用上面链接中的清洗函数:
from talon.signature.bruteforce import extract_signature
cleaned_email, _ = extract_signature(email)
上述原始邮件清洗后得到大概是这样的:
Thank you for keeping me updated on this issue. I’m happy to hear that the issue got resolved after all and you can now use the app in its full functionality again. Also many thanks for your suggestions. We hope to improve this feature in the future. In case you experience any further problems with the app, please don’t hesitate to contact me again.
对于不同的语言,处理的方式会有所不同,所以首先需要对邮件的语言类型进行检测。得益于python强大的第三方库,语言检测可以很容易实现,比如使用polyglot
,langdetect
,textblob
等。
from langdetect import detect
lang = detect(cleaned_email) # lang = 'en' for an English email
由上一步检测出邮件语言之后,可以针对该语言对邮件全文进行句子分割。以英文为例,可以使用NLTK包中的sen_tokenize()
方法
from nltk.tokenize import sent_tokenize
sentences = sent_tokenize(email, language = lang)
举个栗子
为了邮件文本表示成机器可以识别的输入,同时融入文本的语义信息,需要对文本进行编码,生成特定长度的向量表示,即Word Embedding。对于word embedding,常见的有word2vec,glove,fasttext等。对于句子embedding,一种简单的思路是对句子中的单词取其word embedding的加权和,认为不同的单词对整体的贡献程度不一样。例如经常出现的单词(‘and’,‘to’,‘the’等)几乎对句子信息没有贡献,一些很少出现的单词具有更大的代表性,类似于tf-idf的思想,也在这篇论文中介绍。
但是,这些无监督的方法没有将单词在句子中的顺序考虑进去,因此会造成性能损失。为了改进这一点,采用了**Skip-Thought Vectors**这篇论文提供的思路,使用wikipedia训练了一个 Skip-Thoughts句子嵌入模型:
整体框架如下所示
感谢Skip-Thought的开源,我们通过几行简单的代码就可以得到句子向量表示:
import skipthoughts
# 需要预先下载预训练模型
model = skipthoughts.load_model()
encoder = skipthoughts.Encoder(model)
encoded = encoder.encode(sentences)
在为邮件文本生成句子表示之后,将这些句子编码在高维向量空间中进行聚类,聚类的数量为摘要任务所需要的句子数量。可以将最终摘要的句子数设定为初始输入句子综述的平方根。我们可以使用K-means实现:
import numpy as np
from sklearn.cluster import KMeans
n_clusters = np.ceil(len(encoded)**0.5)
kmeans = KMeans(n_clusters=n_clusters)
kmeans = kmeans.fit(encoded)
聚类之后的每一个簇群都可以认为是一组语义相似的句子集合,而我们只需要其中的一句来表示即可。这一句子的选择为考虑距离聚类中心最接近的句子,然后将每个簇群相对应的候选句子排序,形成最终的文本摘要。摘要中候选句子的顺序由原始电子邮件中句子在其相应簇中的位置确定。 例如,如果位于其群集中的大多数句子出现在电子邮件的开头,则将候选句子选择为摘要中的第一句。
from sklearn.metrics import pairwise_distances_argmin_min
avg = []
for j in range(n_clusters):
idx = np.where(kmeans.labels_ == j)[0]
avg.append(np.mean(idx))
closest, _ = pairwise_distances_argmin_min(kmeans.cluster_centers_, encoded)
ordering = sorted(range(n_clusters), key=lambda k: avg[k])
summary = ' '.join([email[closest[idx]] for idx in ordering])
经过上述几个步骤,最终得到的摘要如下所示:
I’m happy to hear that the issue got resolved after all and you can now use the app in its full functionality again. Also many thanks for your suggestions. In case you experience any further problems with the app, please don’t hesitate to contact me again.
以上~
2020.01.14
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。