有时候,很简单的数学方法,就可以完成很复杂的任务。
这个系列的前两部分就是很好的例子。仅仅依靠统计词频,就能找出关键词和相似文章。虽然它们算不上效果最好的方法,但肯定是最简便易行的方法。
今天,依然继续这个主题。讨论如何通过词频,对文章进行自动摘要(Automatic summarization)。
如果能从3000字的文章,提炼出150字的摘要,就可以为读者节省大量阅读时间。由人完成的摘要叫"人工摘要",由机器完成的就叫"自动摘要"。许多网站都需要它,比如论文网站、新闻网站、搜索引擎等等。2007年,美国学者的论文《A Survey on Automatic Text Summarization》(Dipanjan Das, Andre F.T. Martins, 2007)总结了目前的自动摘要算法。其中,很重要的一种就是词频统计。
这种方法最早出自1958年的IBM公司科学家H.P. Luhn的论文《The Automatic Creation of Literature Abstracts》。
Luhn博士认为,文章的信息都包含在句子中,有些句子包含的信息多,有些句子包含的信息少。"自动摘要"就是要找出那些包含信息最多的句子。
句子的信息量用"关键词"来衡量。如果包含的关键词越多,就说明这个句子越重要。Luhn提出用"簇"(cluster)表示关键词的聚集。所谓"簇"就是包含多个关键词的句子片段。
上图就是Luhn原始论文的插图,被框起来的部分就是一个"簇"。只要关键词之间的距离小于"门槛值",它们就被认为处于同一个簇之中。Luhn建议的门槛值是4或5。也就是说,如果两个关键词之间有5个以上的其他词,就可以把这两个关键词分在两个簇。
下一步,对于每个簇,都计算它的重要性分值。
以前图为例,其中的簇一共有7个词,其中4个是关键词。因此,它的重要性分值等于 ( 4 x 4 ) / 7 = 2.3。
然后,找出包含分值最高的簇的句子(比如5句),把它们合在一起,就构成了这篇文章的自动摘要。具体实现可以参见《Mining the Social Web: Analyzing Data from Facebook, Twitter, LinkedIn, and Other Social Media Sites》(O'Reilly, 2011)一书的第8章,python代码见github。
Luhn的这种算法后来被简化,不再区分"簇",只考虑句子包含的关键词。下面就是一个例子(采用伪码表示),只考虑关键词首先出现的句子。
Summarizer(originalText, maxSummarySize):
// 计算原始文本的词频,生成一个数组,比如[(10,'the'), (3,'language'), (8,'code')...]
wordFrequences = getWordCounts(originalText)// 过滤掉停用词,数组变成[(3, 'language'), (8, 'code')...]
contentWordFrequences = filtStopWords(wordFrequences)// 按照词频进行排序,数组变成['code', 'language'...]
contentWordsSortbyFreq = sortByFreqThenDropFreq(contentWordFrequences)// 将文章分成句子
sentences = getSentences(originalText)// 选择关键词首先出现的句子
setSummarySentences = {}
foreach word in contentWordsSortbyFreq:
firstMatchingSentence = search(sentences, word)
setSummarySentences.add(firstMatchingSentence)
if setSummarySentences.size() = maxSummarySize:
break// 将选中的句子按照出现顺序,组成摘要
summary = ""
foreach sentence in sentences:
if sentence in setSummarySentences:
summary = summary + " " + sentencereturn summary
类似的算法已经被写成了工具,比如基于Java的Classifier4J库的SimpleSummariser模块、基于C语言的OTS库、以及基于classifier4J的C#实现和python实现。
(完)
两会 说:
好文,但比前两篇难懂些,有个实例就好了。
2013年3月26日 22:17 | # | 引用
jimu 说:
哎,这个网站居然还在!
我大学时代就在这个网站上读卡尔维诺了!
都是很好的书!
还在真是太激动了!
站主,你又微博了嘛?
2013年3月26日 22:46 | # | 引用
SomeOne 说:
“门槛值”是指“阈值”么?
2013年3月27日 07:46 | # | 引用
loyld 说:
基于Java的Classifier4J库的SimpleSummariser模块
-- 这个载了源码,看了很山寨,没有真正实现博客中的算法。。。。
2013年3月27日 16:18 | # | 引用
学生甲 说:
Summly 程序用的是这个算法吗?
http://dapenti.org/blog/more.asp?name=xilei&id=75132
2013年3月27日 23:26 | # | 引用
M16 说:
classifier4J的C#实现,C#的连接无法访问,可否更新下载连接,或者发下代码到我邮箱,十分感谢
2013年3月28日 10:22 | # | 引用
王子非 说:
貌似吴军在数学之美的黑板报中都描述了有关算法,还有起源。
2013年3月28日 14:53 | # | 引用
冰上游鱼 说:
嗯 思想很好。就像《数学之美》中所说,很多有效的算法思想都很简单。
2013年4月 8日 10:44 | # | 引用
hoot 说:
统计应用貌似很广 语言翻译也依赖于词频统计 楼主可以来个这方面的文章。嘿嘿
2013年5月 1日 00:51 | # | 引用
李旭 说:
阮推荐点书,或是网站吧。
2013年7月29日 13:27 | # | 引用
Roottan 说:
阮哥,有没什么比较好的自动打Tag 的工具,Tag 跟关键词相似但还是有些不同的,一直没找到比较好的相关工具和paper。
2014年2月25日 16:34 | # | 引用
kerygao 说:
复杂的数学算法,标书得如此简单易懂。大牛,多出美文!期待。。。
2015年2月26日 23:41 | # | 引用
HOU_JUN 说:
最近想根据上面的算法,实现一个自动摘要,做搜索引擎项目需要。但是不知道用什么比较好的方法实现把中文文章分成句子。
我的想法是这样:把文章中的所有“句号”,“感叹号”,“问好”替换成空格,然后再使用字符串流读取每一句(遇到空格算一句),添加到vector的sentences容器中,然后再对每一个关键字遍历sentences数组做string.find("keyword"),若找到则把该句子压到一个装摘要句子的容器vector的summary;当summary的大小到达摘要的上限的时候,跳出循环。将summary中的句子进行拼接,形成摘要,返回。
我使用if(originalTxt[i]=='。'||originalTxt[i]=='!'||originalTxt[i]=='?')
originalTxt[i] = ' ';
但是这样会后警告,所以这一部分想请教阮先生。
2015年9月27日 22:53 | # | 引用
高小林 说:
伪代码中貌似没有考虑句子重复的问题。
2020年4月17日 11:59 | # | 引用