赞
踩
互联网时代信息爆炸式增长,人们面对越来越多的信息无法一一阅读,而文本自动摘要技术可以一定程度上缓解这个问题。摘要就是一篇文章的核心部分信息,文本自动摘要技术分抽取式摘要和生成式摘要,前者是在原文中挑选一定比例的句子拼凑成一个摘要,后者更接近人为的总结式简写一篇文章。目前越来越多的研究者使用深度神经网络来研究生成式摘要技术,但是难度也挺大,效果有限。
本文的方法是使用基于启发式规则的算法实现了一个抽取式摘要算法,大体思路参考了这篇论文,但是也做了一些改进和修改。
我们知道一篇文章如果要用里面几个句子来代表,那么肯定选择那些拥有更多个与文章信息相关的关键词的那些句子;另外根据从小语文课上讲的中心句概念,文章首位和每个段落首位的句子基本也是中心句;更进一步,我们通过分析,如果文章中某个句子和文章中大部分句子表达的意思都相近,那么这个句子也能很好的作为摘要句子。因此本文使用关键词信息量、句子位置、句子相似度三个参数来构建一个句子权重的函数,计算所有句子的权重之后按照降序排序,去前面固定比例的句子,然后依据它们在原文中的先后顺序再次进行排序输出,这样就得到我们要的摘要了。
为了抽取一些重要句子,首先我们需要将所有句子划分开来;另外使用计算机来做计算就得把句子用向量表示出来,这里选用tfidf权重矩阵来表示一篇文章,tfidf矩阵每一行就是文章中的一个句子。本文使用机器学习库sklearn里的函数来实现tfidf矩阵。
切分句子函数:
- def split_sentence(text, punctuation_list='!?。!?'):
- """
- 将文本段安装标点符号列表里的符号切分成句子,将所有句子保存在列表里。
- """
- sentence_set = []
- inx_position = 0 #索引标点符号的位置
- char_position = 0 #移动字符指针位置
- for char in text:
- char_position += 1
- if char in punctuation_list:
- next_char = list(text[inx_position:char_position+1]).pop()
- if next_char not in punctuation_list:
- sentence_set.append(text[inx_position:char_position])
- inx_position = char_position
- if inx_position < len(text):
- sentence_set.append(text[inx_position:])
-
- sentence_with_index = {i:sent for i,sent in enumerate(sentence_set)}
- return sentence_set,sentence_with_index
使用“。”,“?” 和“!”来做切分句子的标点符号。
构建TFIDF矩阵函数:
- def get_tfidf_matrix(sentence_set,stop_word):
- corpus = []
- for sent in sentence_set:
- sent_cut = jieba.cut(sent)
- sent_list = [word for word in sent_cut if word not in stop_word]
- sent_str = ' '.join(sent_list)
- corpus.append(sent_str)
-
- vectorizer=CountVectorizer()
- transformer=TfidfTransformer()
- tfidf=transformer.fit_transform(vectorizer.fit_transform(corpus))
- # word=vectorizer.get_feature_names()
- tfidf_matrix=tfidf.toarray()
- return tfidf_matrix
有三个权重指数需要计算,分别是关键词信息量、句子位置和句子相似度信息量;下面分别使用三个函数实现
- def get_sentence_with_words_weight(tfidf_matrix):
- sentence_with_words_weight = {}
- for i in range(len(tfidf_matrix)):
- sentence_with_words_weight[i] = np.sum(tfidf_matrix[i])
-
- max_weight = max(sentence_with_words_weight.values()) #归一化
- min_weight = min(sentence_with_words_weight.values())
- for key in sentence_with_words_weight.keys():
- x = sentence_with_words_weight[key]
- sentence_with_words_weight[key] = (x-min_weight)/(max_weight-min_weight)
-
- return sentence_with_words_weight
计算位置权重:
- def get_sentence_with_position_weight(sentence_set):
- sentence_with_position_weight = {}
- total_sent = len(sentence_set)
- for i in range(total_sent):
- sentence_with_position_weight[i] = (total_sent - i) / total_sent
- return sentence_with_position_weight
计算相似度权重:
- def similarity(sent1,sent2):
- """
- 计算余弦相似度
- """
- return np.sum(sent1 * sent2) / (1e-6+(np.sqrt(np.sum(sent1 * sent1)) *\
- np.sqrt(np.sum(sent2 * sent2))))
-
- def get_similarity_weight(tfidf_matrix):
- sentence_score = collections.defaultdict(lambda :0.)
- for i in range(len(tfidf_matrix)):
- score_i = 0.
- for j in range(len(tfidf_matrix)):
- score_i += similarity(tfidf_matrix[i],tfidf_matrix[j])
- sentence_score[i] = score_i
-
- max_score = max(sentence_score.values()) #归一化
- min_score = min(sentence_score.values())
- for key in sentence_score.keys():
- x = sentence_score[key]
- sentence_score[key] = (x-min_score)/(max_score-min_score)
-
- return sentence_score
句子之间的相似度使用余弦相似度计算,这里的句子相似度得分sentence_score以该句子和所有句子的相似度的累加和作为权重,最后将所有权重归一化。
首先将三个权重指数按照一定的系数相加,对所有句子按照权重值进行降序排序:
- def ranking_base_on_weigth(sentence_with_words_weight,
- sentence_with_position_weight,
- sentence_score, feature_weight = [1,1,1]):
- sentence_weight = collections.defaultdict(lambda :0.)
- for sent in sentence_score.keys():
- sentence_weight[sent] = feature_weight[0]*sentence_with_words_weight[sent] +\
- feature_weight[1]*sentence_with_position_weight[sent] +\
- feature_weight[2]*sentence_score[sent]
-
- sort_sent_weight = sorted(sentence_weight.items(),key=lambda d: d[1], reverse=True)
- return sort_sent_weight
feature_weight是可调整的权重指数参数,我这里默认都是1,也就是将上述三个指标同等对待。如果有评测数据集,我们可以根据ROUGE得分的高低来调整这个feature_weight的取值。
最后将抽取的句子重新排列作为摘要输出:
- def get_summarization(sentence_with_index,sort_sent_weight,topK_ratio =0.3):
- topK = int(len(sort_sent_weight)*topK_ratio)
- summarization_sent = sorted([sent[0] for sent in sort_sent_weight[:topK]])
-
- summarization = []
- for i in summarization_sent:
- summarization.append(sentence_with_index[i])
-
- summary = ''.join(summarization)
- return summary
我们试一下将下面这篇新闻做一个自动摘要:
男子母亲和妻子同时坠河 先救妻子再救母亲
肥东小伙先救了老婆后救妈,“当时没想那么多”;妈妈不怪儿子反而心疼他
妈妈和老婆一起掉河里,你先救谁?这是一个考验男人的经典问题。虽然民间流传着千奇百怪的答案,却没有人真正回答得了。但就在7月22日下午,肥东县店埠河边圩埂村28岁的小伙郭某,遇到了活生生的现实。他的母亲和妻子一同落入深六米的水中,两人随时都可能丧命!先救谁?问题来了!
三口打鱼一齐跌落河中
7月22日,大暑,天气炎热。
当天下午6时许,太阳已不那么毒了,肥东县撮镇镇店埠河边圩埂村的郭某和妻子小青(小名),一起去村子边的店埠河下网捕鱼。“小青以前没有打过鱼,她想和我一起去看看,觉得新鲜。”郭某今年28岁,之前他一直和小青在浙江打工,郭某是水电工,小青是油漆工,两人在打工过程中相识,2008年在肥东结婚。
郭某父母农闲时会到河中下网打鱼,家中有两条小渔船。郭某好些年没打过鱼了,父母在河里下的网他不知道在哪,郭某的母亲孙某就提出,陪小夫妻俩一起到河里看看。
傍晚6时10分左右,三人来到河边,坐上一条小渔船,郭某向河中划去。郭某的母亲孙某在船的一角下网。“小青比较好奇,就走到船尾去看我妈下网,结果船就发生倾斜了。”危险就在此时发生,郭某回忆,小青在船尾站着不稳,惊吓地大喊起来,身体向河一侧倒去,而郭某的母亲孙某准备上前抱住小青,一旁撑竿的郭某也急了,想过来拉两人。小船顷刻失衡,三人同时落水。
儿子先救老婆再救妈妈
“店埠河中间水深六米,平时许多轮船都是从撮镇码头开往巢湖的。”圩埂村的村民告诉记者,他们平时都不给小孩子到店埠河游泳,“水深,河里以前就淹死过人。”
而落水的三人中,只有郭某会游泳,其他两人都不识水性。“当时也没有多想,只觉得小青离我近一点,所以我就向她游过去,她一把抱住我的腿,我拉住她的头发,掰开她的手,幸亏船就在旁边,我当时就拖着她游到了船边,然后让她拉住船边的环子。她还是害怕得大喊大叫,我就大声对她说,你要镇定,否则我们大家都会死!她就不再喊叫了。”
随后郭某转身去救妈妈。“她在水里呛着了,一会儿浮上来一会儿沉下去,我就扎个猛子过去了,当时妈妈已经开始下沉了,我从水里托起了她。”郭某说,当他游到妈妈跟前时,妈妈几乎已经没有了气力,身体也没有反应了。“当时吓死了,以为妈妈出事了,我一边拉着船,一边拖着妈妈,大概游了五六分钟,然后到了岸边。”
此时,郭某的妻子小青从水里爬上了岸,而郭某则在一旁拍着妈妈的背,希望将水控出来。“拍了几下后,妈妈咳嗽了一声,吐了许多水出来。不过一会妈妈又晕过去了。”
医生:再迟两分钟就危险了
看到妈妈晕厥后,郭某急忙背着妈妈回到家中,也没来得及换衣服,坐着邻居的车就将妈妈送到了肥东县人民医院。郭某的妻子小青在家换了衣服后,当天晚上也赶到了医院照顾婆婆。
昨天,记者在肥东县人民医院见到了郭某一家人,媳妇和儿子俩正坐在病床前。据介绍,孙某今年52岁,“她的血压一直比较高,而且之前做过胆囊切除手术,身体一直不大好,这样掉水里受到惊吓,当然就生病了。”郭某的父亲说。老郭告诉记者,平时孩子不在家时都是他打鱼,有时候孩子母亲会陪着他一起下网,“船只要坐稳了,哪会掉到河里。年轻人没有坐船经验,结果事情就发生了。”
医生告诉记者,由于病人有高血压,落水后又受到惊吓,刚开始有点昏迷,到了医院后呕吐、头昏,“不过挂了水后,目前并没有大碍,也可以回家休养了。”
不过,医生也告诉记者,溺水者大概只有不到十分钟的安全救援时间,若是过了这个宝贵的时间段,生命就非常危险了。“从病人的病情来看,应该是呛水过多受惊吓。若是再迟一两分钟,就非常危险了。”
妈妈不生气,爸爸挺不满
在医院里,老郭有些责备孩子:“打鱼有什么好看的,看热闹好险把命都搭上了!年轻人就图个新鲜,也不顾你妈妈安全。”
在老郭问明了事情前后,对儿子先救媳妇的事也有些不满,“他妈都要没气了,他还拖着船,就怕媳妇危险,媳妇都拉住船的铁环了,还有什么危险?!这时候当然应该先救他妈,等他妈安全了再去将船拉上来。”不过,这话老郭并没有当着儿子儿媳的面说。在病房里,老郭守在老婆身边,始终板着脸,儿子、儿媳也不敢和他多说话。
郭某的母亲孙某醒来后,却没有责怪儿子:“要不是儿子,我这老命就丢在河里了,现在挂了水,身体舒服多了。”孙某告诉记者,她的记忆只停留在儿子游过来救她的时候,“后来怎么上了岸,怎么到了医院,都不太记得了!”
毕竟她们都在我身边
昨天,在医院里,郭某忙前忙后,一会儿拿药,一会儿照顾母亲。一旁的小青对郭某说道:“要不你先回家换洗下,妈妈这边我来照顾。”在走廊里,记者和小郭对上了话。
记者:为什么先救老婆,后救妈妈?
郭某:当时没想那么多,只是看到老婆离我稍微近一点,我就游过去先救她了,当安顿好老婆后,发现妈妈在水中非常危险,又去救妈妈。
记者:知道那个让男人头疼的题目吗,“妈妈和老婆一起掉河里,你先救谁?”你老婆可问过你?
郭某:我老婆没有问过我这个问题。
但我听过这个题目,以前没有谈恋爱的时候也曾想过,觉得肯定是先救妈妈。
记者:为什么妈妈都昏迷了,你还拖着船,是否心里仍惦记着老婆的安全?
郭某:拉着船也是因为我没有力气游上岸了,所以是借着船一起游上了岸,也可以救老婆。
记者:你觉得你先救老婆,会不会受到父母的责怪,会不会让亲友觉得你不孝顺?
郭某:救人的时候没想过,(沉默一会儿)现在……(沉默)觉得可能会是个问题。
记者:请不要见怪,问一个让你更不舒服的问题,如果这事情再发生一次,你还会不会那样救援?
郭某:其实昨晚我想了一下,后来觉得,无论如何,这样的救人安排都是最合理的。所以我还是会这样选择,毕竟她们现在都还在我身边。
他是我儿子,我不怪他
昨天,在病床前,记者也和郭某的母亲孙某聊了起来。
记者:你知道你儿子先救媳妇、后救你的过程了吧?
孙某:知道呀。
记者:你会不会在心里怪儿子,没有先救你?
孙某:不怪,他是我儿子呀,他到现在忙得都没有回家换衣服,湿透的衣服就这样焐干了,我挺心疼他的。你们也不要再追问他了,这样让他感到难过。
记者:好像你丈夫有点不高兴,觉得儿子做得不对。
孙某:他就那脾气,回头我来劝劝他。
输出结果如下,我只抽取了其中30%的句子作为摘要,这个比例可以根据具体情况做调整:
男子母亲和妻子同时坠河 先救妻子再救母亲
肥东小伙先救了老婆后救妈,“当时没想那么多”;妈妈不怪儿子反而心疼他
妈妈和老婆一起掉河里,你先救谁?但就在7月22日下午,肥东县店埠河边圩埂村28岁的小伙郭某,遇到了活生生的现实。他的母亲和妻子一同落入深六米的水中,两人随时都可能丧命!先救谁?
当天下午6时许,太阳已不那么毒了,肥东县撮镇镇店埠河边圩埂村的郭某和妻子小青(小名),一起去村子边的店埠河下网捕鱼。”郭某今年28岁,之前他一直和小青在浙江打工,郭某是水电工,小青是油漆工,两人在打工过程中相识,2008年在肥东结婚。
郭某父母农闲时会到河中下网打鱼,家中有两条小渔船。郭某好些年没打过鱼了,父母在河里下的网他不知道在哪,郭某的母亲孙某就提出,陪小夫妻俩一起到河里看看。
傍晚6时10分左右,三人来到河边,坐上一条小渔船,郭某向河中划去。郭某的母亲孙某在船的一角下网。”危险就在此时发生,郭某回忆,小青在船尾站着不稳,惊吓地大喊起来,身体向河一侧倒去,而郭某的母亲孙某准备上前抱住小青,一旁撑竿的郭某也急了,想过来拉两人。
儿子先救老婆再救妈妈
“店埠河中间水深六米,平时许多轮船都是从撮镇码头开往巢湖的。”圩埂村的村民告诉记者,他们平时都不给小孩子到店埠河游泳,“水深,河里以前就淹死过人。”郭某说,当他游到妈妈跟前时,妈妈几乎已经没有了气力,身体也没有反应了。”
此时,郭某的妻子小青从水里爬上了岸,而郭某则在一旁拍着妈妈的背,希望将水控出来。”
医生:再迟两分钟就危险了
看到妈妈晕厥后,郭某急忙背着妈妈回到家中,也没来得及换衣服,坐着邻居的车就将妈妈送到了肥东县人民医院。郭某的妻子小青在家换了衣服后,当天晚上也赶到了医院照顾婆婆。
昨天,记者在肥东县人民医院见到了郭某一家人,媳妇和儿子俩正坐在病床前。”
医生告诉记者,由于病人有高血压,落水后又受到惊吓,刚开始有点昏迷,到了医院后呕吐、头昏,“不过挂了水后,目前并没有大碍,也可以回家休养了。
郭某的母亲孙某醒来后,却没有责怪儿子:“要不是儿子,我这老命就丢在河里了,现在挂了水,身体舒服多了。
记者:为什么先救老婆,后救妈妈?
郭某:当时没想那么多,只是看到老婆离我稍微近一点,我就游过去先救她了,当安顿好老婆后,发现妈妈在水中非常危险,又去救妈妈。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。