1 语法特征

chap-data-intensive中,我们描述了如何建立基于检测文本特征的分类器。那些特征可能非常简单,如提取一个单词的最后一个字母,或者更复杂一点儿,如分类器自己预测的词性标签。在本章中,我们将探讨特征在建立基于规则的语法中的作用。对比特征提取,记录已经自动检测到的特征,我们现在要 声明 词和短语的特征。我们以一个很简单的例子开始,使用字典存储特征和它们的值。

  1. >>> kim = {'CAT': 'NP', 'ORTH': 'Kim', 'REF': 'k'}
  2. >>> chase = {'CAT': 'V', 'ORTH': 'chased', 'REL': 'chase'}

对象kimchase有几个共同的特征,CAT(语法类别)和ORTH(正字法,即拼写)。此外,每一个还有更面向语义的特征:kim['REF']意在给出kim的指示物,而chase['REL']给出chase表示的关系。在基于规则的语法上下文中,这样的特征和特征值对被称为特征结构,我们将很快看到它们的替代符号。

特征结构包含各种有关语法实体的信息。这些信息不需要详尽无遗,我们可能要进一步增加属性。例如,对于一个动词,根据动词的参数知道它扮演的“语义角色”往往很有用。对于 chase,主语扮演“施事”的角色,而宾语扮演“受事”角色。让我们添加这些信息,使用'sbj''obj'作为占位符,它会被填充,当动词和它的语法参数结合时:

  1. >>> chase['AGT'] = 'sbj'
  2. >>> chase['PAT'] = 'obj'

如果我们现在处理句子<cite>Kim chased Lee</cite>,我们要“绑定”动词的施事角色和主语,受事角色和宾语。我们可以通过链接到相关的NPREF特征做到这个。在下面的例子中,我们做一个简单的假设:在动词直接左侧和右侧的NP分别是主语和宾语。我们还在例子结尾为 Lee 添加了一个特征结构。

  1. >>> sent = "Kim chased Lee"
  2. >>> tokens = sent.split()
  3. >>> lee = {'CAT': 'NP', 'ORTH': 'Lee', 'REF': 'l'}
  4. >>> def lex2fs(word):
  5. ... for fs in [kim, lee, chase]:
  6. ... if fs['ORTH'] == word:
  7. ... return fs
  8. >>> subj, verb, obj = lex2fs(tokens[0]), lex2fs(tokens[1]), lex2fs(tokens[2])
  9. >>> verb['AGT'] = subj['REF']
  10. >>> verb['PAT'] = obj['REF']
  11. >>> for k in ['ORTH', 'REL', 'AGT', 'PAT']:
  12. ... print("%-5s => %s" % (k, verb[k]))
  13. ORTH => chased
  14. REL => chase
  15. AGT => k
  16. PAT => l

同样的方法可以适用不同的动词,例如 surprise,虽然在这种情况下,主语将扮演“源事”(SRC)的角色,宾语扮演“体验者”(EXP)的角色:

  1. >>> surprise = {'CAT': 'V', 'ORTH': 'surprised', 'REL': 'surprise',
  2. ... 'SRC': 'sbj', 'EXP': 'obj'}

特征结构是非常强大的,但我们操纵它们的方式是极其 ad hoc。我们本章接下来的任务是,显示上下文无关语法和分析如何能扩展到合适的特征结构,使我们可以一种更通用的和有原则的方式建立像这样的分析。我们将通过查看句法协议的现象作为开始;我们将展示如何使用特征典雅的表示协议约束,并在一个简单的语法中说明它们的用法。

由于特征结构是表示任何形式的信息的通用的数据结构,我们将从更形式化的视点简要地看着它们,并演示 NLTK 提供的特征结构的支持。在本章的最后一部分,我们将表明,特征的额外表现力开辟了一个用于描述语言结构的复杂性的广泛的可能性。