自然语言处理实战
上QQ阅读APP看书,第一时间看更新

1.6 词序和语法

词的顺序很重要。那些在词序列(如句子)中控制词序的规则被称为语言的语法(grammar,也称文法)。这是之前的词袋或词向量例子中所丢弃的信息。幸运的是,在大多数简短的短语甚至许多完整的句子中,上述词向量近似方法都可以奏效。如果只是想对一个短句的一般意义和情感进行编码的话,那么词序并不十分重要。看一下“Good morning Rosa”这个例子中的所有词序结果:

>>> from itertools import permutations

>>> [" ".join(combo) for combo in\
...     permutations("Good morning Rosa!".split(), 3)]
['Good morning Rosa!',
 'Good Rosa! morning',
 'morning Good Rosa!',
 'morning Rosa! Good',
 'Rosa! Good morning',
 'Rosa! morning Good']

现在,如果试图孤立地解释这些字符串中的每一个(不看其他字符串),那么可能会得出结论,即这些字符串可能都有相似的意图或含义。我们甚至可能注意到Good这个词的大写形式,并把它放在脑海中短语的最前面。但是我们也可能认为Good Rosa是某种专有名词,如餐馆或花店的名字。尽管如此,一个聪明的聊天机器人或者布莱切利公园20世纪40年代的聪明女士可能会用同样无伤大雅的问候语来回应这6种情况中的任何一种:“Good morning my dear General.”[17]

我们(在脑海中)再用一个更长、更复杂的短语来尝试一下,这是一条逻辑语句,其中词的顺序非常重要:

>>> s = """Find textbooks with titles containing 'NLP',
...     or 'natural' and 'language', or
...     'computational' and 'linguistics'."""
>>> len(set(s.split()))
12
>>> import numpy as np
>>> np.arange(1, 12 + 1).prod()  # factorial(12) = arange(1, 13).prod()
479001600

词排列的数量从简单的问候语factorial(3) == 6激增到更长的语句factorial(12) == 479001600!很明显,词序所包含的逻辑对任何希望正确回复的机器而言都很重要。尽管普通的问候语通常不会因为词袋处理而造成混淆,但如果把更复杂的语句放入词袋中,就会丢失大部分意思。就像前面示例中的自然语言查询一样,词袋并不是处理数据库查询的最佳方式。

无论语句是用形式化的编程语言(如SQL)编写的,还是用非形式化的自然语言(如英语)编写的,当语句要表达事物之间的逻辑关系时,词序和语法都非常重要。这就是计算机语言依赖严格的语法和句法规则分析器的原因。幸运的是,自然语言句法树分析器取得了一些最新进展,使得从自然语言中提取出语法和逻辑关系变得可能,并且可以达到显著的精确率(超过90%)[18]。在后面的章节中,我们将介绍如何使用SyntaxNet(Parsey McParseface)和SpaCy这样的包来识别这些关系。

就像上面有关布莱切利公园问候语的例子一样,即使一条语句的逻辑解释并不依赖词序,有时关注词序也可以得到一些十分微妙的相关意义的暗示,这些意义可以辅助更深层次的回复。有关这些更深层的自然语言处理环节将在下一节讨论。此外,第2章会介绍一种技巧,它能够将一些由词序表达的信息融合到词向量表示当中。同时,第2章还会介绍如何改进前面例子中使用的分词器(str.split()),以便更准确地将词向量中的词放到更合适的槽内。这样,“good”和“Good”这样的词会放到同一个栈里,而“rosa”和“Rosa”(不是“Rosa!”)这样的词条将会分配到不同的栈。