文本摘要在NLP领域也是非常热门的一个领域,主要分成抽取式和生成式的,生成式的主要涉及到深度学习,个人觉得其实抽取式的文本摘要在效果上,只要算法得当,并不比生成式的差。这里我们一起来探讨一下抽取式的文本摘要,这里我们用朱自清的《背影》作为文本来提取10句这篇文章的摘要,我们来看看《背影》这篇文章:
document = '我与父亲不相见已二年余了,我最不能忘记的是他的背影。\
那年冬天,祖母死了,父亲的差使也交卸了,正是祸不单行的日子,我从北京到徐州,\
打算跟着父亲奔丧回家。到徐州见着父亲,看见满院狼藉的东西,又想起祖母,不禁簌簌地流下眼泪。\
父亲说,事已如此,不必难过,好在天无绝人之路。\
回家变卖典质,父亲还了亏空;又借钱办了丧事。这些日子,家中光景很是惨淡,一半为了丧事,一半为了父亲赋闲。\
丧事完毕,父亲要到南京谋事,我也要回北京念书,我们便同行。\
到南京时,有朋友约去游逛,勾留了一日;第二日上午便须渡江到浦口,下午上车北去。\
父亲因为事忙,本已说定不送我,叫旅馆里一个熟识的茶房陪我同去。他再三嘱咐茶房,甚是仔细。\
但他终于不放心,怕茶房不妥帖,颇踌躇了一会。\
其实我那年已二十岁,北京已来往过两三次,是没有甚么要紧的了。\
他踌躇了一会,终于决定还是自己送我去。我两三回劝他不必去;他只说,不要紧,他们去不好。\
我们过了江,进了车站。我买票,他忙着照看行李。行李太多了,得向脚夫行些小费,才可过去。\
他便又忙着和他们讲价钱。我那时真是聪明过分,总觉他说话不大漂亮,非自己插嘴不可。\
但他终于讲定了价钱;就送我上车。他给我拣定了靠车门的一张椅子;我将他给我做的紫毛大衣铺好坐位。\
他嘱我路上小心,夜里警醒些,不要受凉。又嘱托茶房好好照应我。\
我心里暗笑他的迂;他们只认得钱,托他们直是白托。而且我这样大年纪的人,难道还不能料理自己么?\
唉,我现在想想,那时真是太聪明了。\
我说道,爸爸,你走吧。他望车外看了看,说,我买几个橘子去。你就在此地,不要走动。\
我看那边月台的栅栏外有几个卖东西的等着顾客。\
走到那边月台,须穿过铁道,须跳下去又爬上去。\
父亲是一个胖子,走过去自然要费事些。\
我本来要去的,他不肯,只好让他去。\
我看见他戴着黑布小帽,穿着黑布大马褂,深青布棉袍,蹒跚地走到铁道边,慢慢探身下去,尚不大难。\
可是他穿过铁道,要爬上那边月台,就不容易了。\
他用两手攀着上面,两脚再向上缩;他肥胖的身子向左微倾,显出努力的样子。\
这时我看见他的背影,我的泪很快地流下来了。\
我赶紧拭干了泪,怕他看见,也怕别人看见。\
我再向外看时,他已抱了朱红的橘子望回走了。\
过铁道时,他先将橘子散放在地上,自己慢慢爬下,再抱起橘子走。\
到这边时,我赶紧去搀他。他和我走到车上,将橘子一股脑儿放在我的皮大衣上。\
于是扑扑衣上的泥土,心里很轻松似的,过一会说,我走了;到那边来信。我望着他走出去。\
他走了几步,回过头看见我,说,进去吧,里边没人。\
等他的背影混入来来往往的人里,再找不着了,我便进来坐下,我的眼泪又来了。\
近几年来,父亲和我都是东奔西走,家中光景是一日不如一日。\
他少年出外谋生,独力支持,做了许多大事。\
那知老境却如此颓唐。他触目伤怀,自然情不能自已。\
情郁于中,自然要发之于外;家庭琐屑便往往触他之怒。\
他待我渐渐不同往日。\
但最近两年的不见,他终于忘却我的不好,只是惦记着我,惦记着我的儿子。\
我北来后,他写了一信给我,信中说道,我身体平安,惟膀子疼痛利害,举箸提笔,诸多不便,大约大去之期不远矣。\
我读到此处,在晶莹的泪光中,又看见那肥胖的,青布棉袍,黑布马褂的背影。我不知何时再能与他相见。'
我把文章进行了换行处理,比较容易看。首先我们对比一下开源库pyhanlp的效果:
from pyhanlp import *
TextRankSentence = JClass("com.hankcs.hanlp.summary.TextRankSentence")
sentence_list = HanLP.extractSummary(document, 10)
for sentence in sentence_list:
print(sentence)
得到10条《背影》的摘要如下:
hanlp是以短句作为分割点,这么看来效果并不是很好,那么下面我们一起来探讨一下抽取式的文本摘要方法。首先可以从断句来做文章,我们可以用句号作为分割点来划分文章:
sentence_list = document.split('。')
print(sentence_list)
可以得到结果:
['我与父亲不相见已二年余了,我最不能忘记的是他的背影',
'那年冬天,祖母死了,父亲的差使也交卸了,正是祸不单行的日子,我从北京到徐州,打算跟着父亲奔丧回家',
'到徐州见着父亲,看见满院狼藉的东西,又想起祖母,不禁簌簌地流下眼泪',
'父亲说,事已如此,不必难过,好在天无绝人之路',
'回家变卖典质,父亲还了亏空;又借钱办了丧事',
'这些日子,家中光景很是惨淡,一半为了丧事,一半为了父亲赋闲',
'丧事完毕,父亲要到南京谋事,我也要回北京念书,我们便同行',
'到南京时,有朋友约去游逛,勾留了一日;第二日上午便须渡江到浦口,下午上车北去',
'父亲因为事忙,本已说定不送我,叫旅馆里一个熟识的茶房陪我同去',
'他再三嘱咐茶房,甚是仔细',
'但他终于不放心,怕茶房不妥帖,颇踌躇了一会',
'其实我那年已二十岁,北京已来往过两三次,是没有甚么要紧的了',
'他踌躇了一会,终于决定还是自己送我去',
'我两三回劝他不必去;他只说,不要紧,他们去不好',
'我们过了江,进了车站',
'我买票,他忙着照看行李',
'行李太多了,得向脚夫行些小费,才可过去',
'他便又忙着和他们讲价钱',
'我那时真是聪明过分,总觉他说话不大漂亮,非自己插嘴不可',
'但他终于讲定了价钱;就送我上车',
'他给我拣定了靠车门的一张椅子;我将他给我做的紫毛大衣铺好坐位',
'他嘱我路上小心,夜里警醒些,不要受凉',
'又嘱托茶房好好照应我',
'我心里暗笑他的迂;他们只认得钱,托他们直是白托',
'而且我这样大年纪的人,难道还不能料理自己么?唉,我现在想想,那时真是太聪明了',
'我说道,爸爸,你走吧',
'他望车外看了看,说,我买几个橘子去',
'你就在此地,不要走动',
'我看那边月台的栅栏外有几个卖东西的等着顾客',
'走到那边月台,须穿过铁道,须跳下去又爬上去',
'父亲是一个胖子,走过去自然要费事些',
'我本来要去的,他不肯,只好让他去',
'我看见他戴着黑布小帽,穿着黑布大马褂,深青布棉袍,蹒跚地走到铁道边,慢慢探身下去,尚不大难',
'可是他穿过铁道,要爬上那边月台,就不容易了',
'他用两手攀着上面,两脚再向上缩;他肥胖的身子向左微倾,显出努力的样子',
'这时我看见他的背影,我的泪很快地流下来了',
'我赶紧拭干了泪,怕他看见,也怕别人看见',
'我再向外看时,他已抱了朱红的橘子望回走了',
'过铁道时,他先将橘子散放在地上,自己慢慢爬下,再抱起橘子走',
'到这边时,我赶紧去搀他',
'他和我走到车上,将橘子一股脑儿放在我的皮大衣上',
'于是扑扑衣上的泥土,心里很轻松似的,过一会说,我走了;到那边来信',
'我望着他走出去',
'他走了几步,回过头看见我,说,进去吧,里边没人',
'等他的背影混入来来往往的人里,再找不着了,我便进来坐下,我的眼泪又来了',
'近几年来,父亲和我都是东奔西走,家中光景是一日不如一日',
'他少年出外谋生,独力支持,做了许多大事',
'那知老境却如此颓唐',
'他触目伤怀,自然情不能自已',
'情郁于中,自然要发之于外;家庭琐屑便往往触他之怒',
'他待我渐渐不同往日',
'但最近两年的不见,他终于忘却我的不好,只是惦记着我,惦记着我的儿子',
'我北来后,他写了一信给我,信中说道,我身体平安,惟膀子疼痛利害,举箸提笔,诸多不便,大约大去之期不远矣',
'我读到此处,在晶莹的泪光中,又看见那肥胖的,青布棉袍,黑布马褂的背影',
'我不知何时再能与他相见',
'']
这么分割文章就显得好得多了,我们再来从这些断句中提取文章的摘要。
可以看出《背影》这篇文章一共有56句话,接下来我们对文章进行分词,并提取文章中所有的词:
words = []
frequency = {}
for i in range(len(sentence_list)):
for word in jieba.lcut(sentence_list[i]):
words.append(word)
print(words)
可以得到结果:
当然这些词中有很多没有意义的词,我们称为停用词,我们可以用停用词表来过滤掉这些停用词:
stopwords = [line.strip() for line in open('./dataset/stopKeyWords.txt',encoding='UTF-8').readlines()]
new_words = []
for word in words:
if word not in stopwords:
new_words.append(word)
print(new_words)
可以得到去掉停用词之后的词表:
这些都是相对有意义的词语,我们接下来可以统计每个词的词频:
dic = {}
for word in new_words:
if word not in dic:
dic[word] = 1
else:
dic[word] = dic[word] + 1
可以得到《背影》这篇文章的词频表:
{'父亲': 11,
'相见': 2,
'二年': 1,
'余': 1,
'忘记': 1,
'背影': 4,
'那年': 2,
'冬天': 1,
'祖母': 2,
'死': 1,
'差使': 1,
'交卸': 1,
'祸不单行': 1,
'日子': 2,
'北京': 3,
'徐州': 2,
'打算': 1,
'跟着': 1,
'奔丧': 1,
'回家': 2,
'满院': 1,
'狼藉': 1,
'东西': 1,
'想起': 1,
'不禁': 1,
'簌簌': 1,
'流下': 1,
'眼泪': 2,
'说': 5,
'事已如此': 1,
'难过': 1,
'天无绝人之路': 1,
'变卖': 1,
'典质': 1,
'亏空': 1,
'借钱': 1,
'办': 1,
'丧事': 3,
'家中': 2,
'光景': 2,
'惨淡': 1,
'一半': 2,
'赋闲': 1,
'完毕': 1,
'南京': 2,
'谋事': 1,
'回': 2,
'念书': 1,
'同行': 1,
'时': 4,
'朋友': 1,
'约': 1,
'游逛': 1,
'勾留': 1,
'一日': 3,
'第二日': 1,
'上午': 1,
'便须': 1,
'渡江': 1,
'浦口': 1,
'下午': 1,
'上车': 2,
'北': 1,
'事忙': 1,
'本已': 1,
'说定': 1,
'送': 3,
'旅馆': 1,
'里': 1,
'熟识': 1,
'茶房': 4,
'陪': 1,
'同去': 1,
'再三': 1,
'嘱咐': 1,
'甚': 1,
'仔细': 1,
'终于': 4,
'放心': 1,
'妥帖': 1,
'踌躇': 2,
'一会': 3,
'二十岁': 1,
'来往': 1,
'两三次': 1,
'要紧': 1,
'两三回': 1,
'劝': 1,
'不要紧': 1,
'不好': 2,
'江': 1,
'进': 1,
'车站': 1,
'买票': 1,
'忙': 2,
'照看': 1,
'行李': 2,
'太多': 1,
'脚夫': 1,
'行些': 1,
'小费': 1,
'讲价钱': 1,
'聪明': 2,
'过分': 1,
'总觉': 1,
'说话': 1,
'漂亮': 1,
'非': 1,
'插嘴': 1,
'讲定': 1,
'价钱': 1,
'拣': 1,
'车门': 1,
'一张': 1,
'椅子': 1,
'做': 2,
'紫毛': 1,
'大衣': 1,
'铺': 1,
'坐位': 1,
'他嘱': 1,
'路上': 1,
'小心': 1,
'夜里': 1,
'警醒': 1,
'受凉': 1,
'嘱托': 1,
'好好': 1,
'照应': 1,
'暗笑': 1,
'迂': 1,
'认得': 1,
'钱': 1,
'托': 1,
'直是': 1,
'白托': 1,
'年纪': 1,
'料理': 1,
'想想': 1,
'真是太': 1,
'说道': 2,
'爸爸': 1,
'走': 10,
'他望': 1,
'车外': 1,
'买': 1,
'几个': 2,
'橘子': 5,
'走动': 1,
'月台': 3,
'栅栏': 1,
'外有': 1,
'卖东西': 1,
'顾客': 1,
'须': 2,
'穿过': 2,
'铁道': 4,
'跳下去': 1,
'爬上去': 1,
'胖子': 1,
'自然': 3,
'费事': 1,
'本来': 1,
'不肯': 1,
'只好': 1,
'戴': 1,
'黑布': 3,
'小帽': 1,
'穿着': 1,
'马褂': 2,
'深': 1,
'青布': 2,
'棉袍': 2,
'蹒跚': 1,
'慢慢': 2,
'探身': 1,
'尚': 1,
'大难': 1,
'爬': 2,
'两手': 1,
'攀着': 1,
'两脚': 1,
'向上': 1,
'缩': 1,
'肥胖': 2,
'身子': 1,
'左微': 1,
'倾': 1,
'显出': 1,
'努力': 1,
'样子': 1,
'泪': 2,
'很快': 1,
'流下来': 1,
'赶紧': 2,
'拭干': 1,
'向外看': 1,
'抱': 2,
'朱红': 1,
'先': 1,
'散': 1,
'放在': 2,
'地上': 1,
'搀': 1,
'车上': 1,
'一股脑儿': 1,
'皮大衣': 1,
'扑': 2,
'衣上': 1,
'泥土': 1,
'轻松': 1,
'来信': 1,
'我望': 1,
'几步': 1,
'回过': 1,
'头': 1,
'里边': 1,
'混入': 1,
'来来往往': 1,
'人里': 1,
'找不着': 1,
'坐下': 1,
'东奔西走': 1,
'少年': 1,
'出外': 1,
'谋生': 1,
'独力': 1,
'支持': 1,
'那知': 1,
'老境': 1,
'颓唐': 1,
'触目伤怀': 1,
'情': 1,
'不能自已': 1,
'情郁于': 1,
'中': 2,
'发之于': 1,
'外': 1,
'家庭': 1,
'琐屑': 1,
'触': 1,
'怒': 1,
'渐渐': 1,
'往日': 1,
'两年': 1,
'不见': 1,
'忘却': 1,
'惦记着': 2,
'儿子': 1,
'我北来': 1,
'写': 1,
'一信': 1,
'信中': 1,
'身体': 1,
'平安': 1,
'惟': 1,
'膀子': 1,
'疼痛': 1,
'利害': 1,
'举箸': 1,
'提笔': 1,
'诸多不便': 1,
'大去': 1,
'之期': 1,
'不远': 1,
'我读': 1,
'晶莹': 1,
'泪光': 1,
'不知': 1,
'再能': 1}
接下来我们可以统计每一句话含有词频统计得分,就是将每句话中的每个词词频进行相加的得分,可以侧面反映每句话的重要程度:
res = []
for i in range(len(sentence_list)):
score = 0
for word in jieba.lcut(sentence_list[i]):
score += dic.get(word, 0)
res.append(score)
print(res)
可以得到56句话的词频得分如下所示:
我们可以将这些词频得分前面加上句子的序列号:
score_dic = {}
for i in range(len(sentence_list)):
score_dic[i] = res[i]
print(score_dic)
可以得到每个句子词频得分的字典:
最后我们可以将这些词频得分进行排序:
result = sorted(score_dic.items(), key = lambda kv:(kv[1], kv[0]), reverse=True)
def extrct(num):
res = []
for i in range(num):
if len(sentence_list[result[i][0]]) > 5:
res.append(sentence_list[result[i][0]])
return res
extrct(10)
可以得到《背影》的10句摘要结果:
这样感觉是不是要好一些?这种方法对于句子相对较长的占优势,那么我们可以将词频得分除以句子长度做一个平均,这样就可以得到每个词的相对重要性:
score_dic = {}
for i in range(len(sentence_list)):
score_dic[i] = res[i]/ (len(jieba.lcut(sentence_list[i])) + 1)
print(score_dic)
可以得到的最终结果:
可以看出这次的结果都是《背影》中广为流传的金句,效果还可以,其实两种方法各有所长。本文仅仅是对抽取式文本摘要做一个讨论,做一个抛砖引玉的作用,同时也希望对大家有所帮助,文中如有纰漏也请大家不吝指教;如有转载,也请标明出处,谢谢。