本部分包含更多用来理解文档的高级库。我们采用这种稍显随意的说法,来讨论计算机如何提取或处理文档的内容,而不是简单地操纵单词和字母。
接下来你将了解如何:
对于前面部分列出的算法,你能凭自己的努力建立一个库。但从现在起,这变得更难了:因为这往往需要大量带注释标记的数据(即一个带有词性的词汇表),或者依赖于复杂的机器学习算法。因此,我们一般都推荐使用库。
在这样一个充满公开问题和活跃研究的领域,你能找到大多数基于 Python 的库。Python 是学界广泛采用的一种语言,不过你偶尔也可以找到基于其他语言的现成的库。
最后一个介绍性的说明是统计学和机器学习目前是自然语言处理的统治者。所以,可能有人试图使用 TensorFlow 来完成这些任务(即深度新闻摘要)。你当然也可以尝试,如果你考虑花相当长的时间来研究的话。
生成一个能正确代表文档含义的摘要或标题可以由多种算法实现。一些依赖于信息检索技术,而另一些则更为高级。其原理也分为两种策略:从原文中提取句子或其中的部分,生成摘要。
另一种策略尚属待解决的研究领域,所以我们只关注第一种。
SumBasic 算法是一种通过句子中各个单词出现的的概率来确定最具代表性的句子的方法:
找到分值最高的句子,之后再排除这个句子,重新计算文档中每个单词的概率。之所以这样做是因为所选句子已经包含了文档总体意义的一部分,即这一部分变得不那么重要 - 有助于避免过度重复。你需要重复这个过程,直到达到所需的摘要长度。
这项技术很简单。它不需要通过数据库来建立每个单词出现在所有文档中出现的一般概率。您只需要单词在计算每个输入文档中的概率。不过,你必须排除所谓的非索引词以保证有效性,这些常见的词语在大多数文献中都存在,诸如 the 或 is;否则可能会引入包含许多这样词语的无意义的句子。你也可以通过词干分解来改善结果。
《The Impact of Frequency on Summarization(频率对摘要的影响)》(PDF)中最早论述了这一点; 这里有一个可用的 Python 库的实现。
基于频率的算法是悠久而热门,因为总体来说,它实现起来有效而简单。SumBasic 是很不错的,常被用作文献中的基线。但是,还有更简单的算法。例如,Open Text Summarizer 是一个 2003 库,运用了更为简单的方法。基本上你仅需计算每个单词的词频,然后排除常见的英文单词(比如 the, is),最后根据一个句子所包含的单词的词频来计算句子的分值。
我们有更为复杂的方法计算单个句子间的相关性。其中一些从 PageRank 中获得灵感 - 它们被称为 LexRank 和 TextRank。它们都通过不同句子之间的关系得出更为复杂的句子重要性的度量,但计算句子相似性的方式有所不同。
PageRank 中当前文档重要性的衡量依据是其中链接到的文档的重要性,每个文档以及每个链接的重要性都被循环计算直到达到平衡。
TextRank 的工作原理相同:元素之间的关系可以用来推断每个元素的重要性。TextRank 实际上采用了比起初的 PageRank 算法更为复杂的公式,因为一个链接只能存在或不存在,而文本联系可能部分地存在。例如,你可能会推算两个句子含有具有相同词干的不同词汇(即 cat 和 cats 都以 cat 为词干)仅仅部分相关。
原始论文论述的是一个通用的而不是具体的算法。不过,它也论述了两种应用:关键字提取和摘要。主要区别是:
例如,你可以选择将单词或者短语的 N 元模型(n-gram)作为单元。单词的 N 元模型是 n 个单词的序列,按处理字符的 k-gram 算法同样的计算方法。也就是说,对于“狗比猫更好(dogs are better than cats)”这个 短语,其 3 元模型(3-grams)为:
短语往往根据其相似程度产生加权链接,或仅根据自己所在的位置产生链接(即一个短语可能与前一个和后一个链接),其方法工作原理相同。
用于提取短语的 TextRank 以整个句子为单位,以它们之间的相同单词数来衡量相似度。因此,如果两个短语包含 tornado, data 和 center 这三个单词,那么它们相似度就比只包含两个相同单词的情况更大。通过短语的长度对相似度进行标准化,以避免较长短语的相似度总是高于较短短语的问题。
用于衡量相似度的单词可以进行词干化;非索引词通常不在计算之列;也可以进一步地排除动词,不过如果你还没法确定词性,那这会很复杂。
LexRank 的不同之处主要在于它使用了标准的 TF-IDF (词频-逆向文件词频)算法。大概就是,在 TF-IDF 算法中,首先根据它们在所有文档和每个特定文档中出现的频率来衡量每个单词的值。例如,你要概括汽车杂志中的文章,那么在每个文档中都会出现很多“汽车”这个词。所以,“汽车” 这个词与每个文档的相关性很弱。相反,“爆炸”这个词只会出现在少部分文档中(希望如此),所以在它在其出现的每个文档中更为重要。
这篇论文《TextRank: Bring Order into Texts(使文本有序)》(PDF)论述了这个算法。ExplainToMe 中有一个 TextRank 的 Python 实现。
我们此前看到的算法都有一点不足:不考虑语义。考虑到有些词有相似的含义(即同义词),或者大多数词在不同语境下会有不同的含义(即多义词)时,这种弱点就显而易见了。潜在语义分析试图克服这些问题。
“潜在语义分析”这种表述强调这是一项技术而非某个特定的算法 - 当你需要表示单词含义时就可以使用的技术。它不仅可以用于生成摘要,还可以用来查找用户查询的词。例如,如果用户搜索“快乐(happiness)”,基于潜在语义分析(LSA)的搜索库也会返回关于“开心(joy)”的结果。
LSA 算法的具体数学公式有点复杂,涉及到矩阵及其运算。不过其理念很简单:含义相似的词语在文本中的相似部分出现。所以你首先先建立一个标准 TF-IDF 矩阵,这个矩阵只需包含在各个特定文档中和所有文档中每个单词的词频。
现在我们的问题是要找出非必然的同时出现的单词之间的关联。例如,假设不同的文档都含有“快乐(happiness)”和“开心(joy)”的短语,还有其他“饼干(cookie)”或“巧克力(chocolate)”之类的单词。这些词不在同一个句子中出现,但都出现在同一份文档中。在某一个文件中包含若干诸如“一只小狗创造快乐(a dog create happiness)”、“许多狗给孩子们带来欢乐(dogs bring joy to children)”的短语,通过这份文件,LSA 算法就能够借助“狗”同这些单词的相互联系来找到“快乐”与“开心”的关联。
这种关联的建立基于同时出现的单词或所有文档中相关单词的频率,这些相关单词甚至能够同句子或者文档建立关联。所以,如果“快乐”和“开心”经常与“狗”同时出现,LSA 算法会把这份特定文档与这些相关单词(“快乐”,“开心”)和“狗”关联。
大体来讲,这项技术将把初始的矩阵从每个词语与其词频的关系变形为一个与每个文档相链接的词语(加权)关系组合。
问题在于单词有很多,因而它们的组合也很多,需要大量的计算和简化,而这就是复杂的数学的用武之地。
可谓矩阵在手,天下我有。也就是说,你可以随心所欲地使用词义的度量了;例如,你可以使用基于图的算法找到最切题的短语,然后运用 LSA 找到与其最相近的那些短语。
文本摘要和奇异值分解论述了一种找到最合适句子的算法。Python 库 sumy
是一个实现。
摘要生成是一片已经有许多设计好的有效算法的富饶领域,这些算法实际上要远比我们在这里列举的多。它们的方法和设计目标各不相同;例如,有些是专门用以回答用户提出的问题,有些则是为了概括多个文档,等等。
您可以在《自动文本摘要(Automatic Text Summarization)》中找到其他算法的简要分类。我们前面提到的 Python 库 sumy
实现了几种算法,但这篇论文并未全部提及。
Classifier4J(Java)、NClassifier(C#)和 Summarize(Python)用如下所述的算法实现了贝叶斯分类器:
为了概括文档,该算法首先确定文档中单词的词频;然后它将文档划分为一系列句子,之后通过组织包含各个高频单词的首个句子,生成摘要;最后重排这些句子以反映原始文档中的顺序。- Summarize.py
尽管这些贝叶斯分类器的项目现已废弃,但是它们依然能帮助你理解算法是如何实现的。
DataTeaser 和 PyTeaser(它们都基于 Python ,不过一开始 DataTeaser 是基于 Scala 的)使用一种自定义方法,结合多种简单的度量来生成一篇文章的摘要。
这就是是第三部分!下一次,我们将讨论潜在语义分析的其他用法、文档句法分析等等。