非监督文本摘要

本文将介绍如何利用sentence embedding来做文本摘要。

什么是文本摘要?

文本摘要是从一些文本资源中抽取重要信息并生成“主旨、概要”的过程。

我们小时候的语文课上,老师要求概括段落大意,指出主题思想。所以,人类很擅长“文本摘要”这样的工作,
首先通过理解文字内容的含义,抽取重要信息,并用自己的语言概括。在这样一个信息爆炸的时代,人类已经
没有足够的精力阅读海量文本,自动文本摘要算法应运而生:

文本摘要节省阅读时间
文本摘要精简了文本信息,让文本检索过程更简单,提升了检索效率
文本摘要算法更加客观,减少了人类的主观“偏见”
QA系统中的文本摘要,利于理解用户个性化的需求

文本摘要的类型

文本摘要的类型有很多种,根据输入、意图、输出可以分为以下几类:
[图片上传失败...(image-c85cf9-1542199235016)]

根据输入类型划分

单文档文本摘要

输入是单个文档,输入的文本长度不长,可以是一篇新闻。早起的文本摘要系统都是处理单文档的。

多文档文本摘要

输入时多个文档,可以是多篇新闻。

根据意图划分

通用文本摘要

输入文本可以是各种各样的文本,比如新闻、小说等。这一部分有大量的研究工作。

特定领域文本摘要

文本摘要模型要融入领域相关知识,来生成更准确的摘要,比如生物医学文档的文本摘要生成。

基于用户请求的文本摘要

根据用户与会话机器人的对话内容,生成会话内容相关的文本摘要。

基于输出类型的文本摘要

抽取式

从文本中抽取句子,然后组合成为摘要。这是目前广泛使用的文本摘要算法,这种文本摘要算法产生的句子一般没有语病、逻辑问题(因为是从文本中抽取的嘛)。

生成式

生成式的文本摘要首先从模型中,抽取phrase和sentence,然后生成摘要(很像人们概括段落大意的方式)。所以,这种方法更有趣、更贴近实际,当然也更加难。

很多文本摘要算法的训练都需要有raw text和对应的summary作为训练数据。但是在大量的nlp任务中,很难获得训练数据,或者获得训练数据的代价非常大。所以在实际的业务中,更倾向于unsupervised的方法。这里简单介绍一下Kushal Chauhan在邮件文本summary方面的工作,介绍他是如何使用sentence embedding来做email summary的。

Kushal Chauhan为什么要用unsupervised text summarization方法呢?通用的文本摘要无法在邮件文本中发挥作用。一方面,数据分布是不同的;另一方面,Kushal面对的事多语言的文档包含English, Danish, French等语言。他借鉴了《Unsupervised Text Summarization Using Sentence Embeddings》这篇论文中的方法,构建了如下Pipeline:


image.png
步骤1:email数据清洗

首先,我们来看一下email文本:

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

email开头的 Hi name以及结尾的 best regards + name对文本摘要是没有用处的。邮件中的开头和结尾“形态各异”,需要相应的正则表达式去识别并删除。mailgun/talon在其github repo中实现了删除邮件开头、结尾的功能,支持多种语言(对于中文的支持,尚需检验)。这里我们给出代码示例:

# clean() is a modified version of extract_signature() found in bruteforce.py in the GitHub repository linked above
cleaned_email, _ = clean(email)

lines = cleaned_email.split('\n')
lines = [line for line in lines if line != '']
cleaned_email = ' '.join(lines)

上面的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.
步骤2: 语言检测

不同语言的sentence tokenization不同,不能都当做英文进行处理。所以,首先使用polyglot, langdetect 或 textblob来进行语言检测。langdetect的相关内容,我在funNLP中介绍过,它支持55种不同的语言识别,如下所示:

from langdetect import detect
lang = detect(cleaned_email) # lang = 'en' for an English email
步骤3: 句子分割

识别出email使用的语言后,使用对应语言的tokenization方法,对email进行句子分割。对于英文email,可以使用nltk中的sent_tokenize进行分割即可:

from nltk.tokenize import sent_tokenize
sentences = sent_tokenize(email, language = lang)
句子分割
步骤4: skip-thought encoder

这一部分介绍如何对句子进行编码。句子的编码方式多种多样,最简单的莫过于将句子中的每个词对应的词向量加起来,再平均,类似于fasttext的做法。在此基础上,复杂一点的,不同的词有不同的权重,比如 and, the 这类词权重就应该低一些。此时,可以使用tf-idf相关信息做权重。论文《A SIMPLE BUT TOUGH-TO-BEAT BASELINE FOR SEN- TENCE EMBEDDINGS》就是这么做的。

上述方法,没有考虑句子中的词的顺序,这很可能会影响summarization的模型表现,所以采用了skip-thought sentence encoder,并以wikipedia作为训练数据。skip-thought sentence encoder包含两个部分:

  1. encoder network: GRU-RNN用于生成固定长度的句子S_i向量表示。
  2. decoder network: 用于产生S_i的前后句S_{i-1}S_{i+1},前后两句采用分别的decoder:previous decoder和next decoder。
    image.png

跟skip gram有点像,给定中间的元素,预测前后的元素。skip-thought encoder最小化句子重构的Loss,这样encoder就能够学会正确地编码句子。对于具有类似意思的句子,其编码后的向量依然相近。《Skip-Thought Vectors》论文中阐述了更多的细节。
[图片上传失败...(image-932a53-1542199235016)]](https://upload-images.jianshu.io/upload_images/2528310-637b6b05249bcd13.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

作者使用了theano开源代码来做训练,pytorch版本开源代码

Skip-Thoughts Encoder-Decoder Architecture

将句子编码的代码就变得非常简单了:

# The 'skipthoughts' module can be found at the root of the GitHub  repository linked above
import skipthoughts

# You would need to download pre-trained models first
model = skipthoughts.load_model()

encoder = skipthoughts.Encoder(model)
encoded =  encoder.encode(sentences)
步骤5: 聚类

将email中的每一个句子编码后,可以使用k-means进行聚类。聚类的数目等于summary的句子数目。

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)
步骤5: 形成摘要

对于几个cluster,选取距离cluster中心最近的句子。选出句子后,如何对这些句子排序呢?分析每个cluster中的全部句子在原文中的顺序,如果该cluster中的多数句子排在第一,那么该cluster中选取的那个句子也排在第一句,代码如下:

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])

上文中的邮件,summary结果如下:

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.

skip-thought encoder的训练语料来自wikipedia。email summarization的源代码详见github

总结一下,本文使用了wikipedia的数据,训练了skip-thought encoder,用来做sentence embedding,拿到embedding后,做clustering,然后挑选句子,并根据cluster中的所有句子的位置 进行排序。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容