5.2 段落处理

我们解释一句话时会使用丰富的上下文知识,一部分取决于前面的内容,一部分取决于我们的背景假设。DRT 提供了一个句子的含义如何集成到前面段落表示中的理论,但是在前面的讨论中明显缺少这两个部分。首先,一直没有尝试纳入任何一种推理;第二,我们只处理了个别句子。这些遗漏由模块nltk.inference.discourse纠正。

段落是一个句子的序列 s<sub>1</sub>, … s<sub>n</sub>,段落线是读法的序列 s<sub>1</sub>-r<sub>i</sub>, … s<sub>n</sub>-r<sub>j</sub> ,每个序列对应段落中的一个句子。该模块按增量处理句子,当有歧义时保持追踪所有可能的线。为简单起见,下面的例子中忽略了范围歧义。

  1. >>> dt = nltk.DiscourseTester(['A student dances', 'Every student is a person'])
  2. >>> dt.readings()
  3. s0 readings:
  4. s0-r0: exists x.(student(x) & dance(x))
  5. s1 readings:
  6. s1-r0: all x.(student(x) -> person(x))

一个新句子添加到当前的段落时,设置参数consistchk=True会通过每条线,即每个可接受的读法的序列的检查模块来检查一致性。在这种情况下,用户可以选择收回有问题的句子。

  1. >>> dt.add_sentence('No person dances', consistchk=True)
  2. Inconsistent discourse: d0 ['s0-r0', 's1-r0', 's2-r0']:
  3. s0-r0: exists x.(student(x) & dance(x))
  4. s1-r0: all x.(student(x) -> person(x))
  5. s2-r0: -exists x.(person(x) & dance(x))
  1. >>> dt.retract_sentence('No person dances', verbose=True)
  2. Current sentences are
  3. s0: A student dances
  4. s1: Every student is a person

以类似的方式,我们使用informchk=True检查新的句子φ是否对当前的段落有信息量。定理证明器将段落线中现有的句子当做假设,尝试证明φ;如果没有发现这样的证据,那么它是有信息量的。

  1. >>> dt.add_sentence('A person dances', informchk=True)
  2. Sentence 'A person dances' under reading 'exists x.(person(x) & dance(x))':
  3. Not informative relative to thread 'd0'

也可以传递另一套假设作为背景知识,并使用这些筛选出不一致的读法;详情请参阅http://nltk.org/howto上的段落 HOWTO。

discourse模块可适应语义歧义,筛选出不可接受的读法。下面的例子调用 Glue 语义和 DRT。由于 Glue 语义模块被配置为使用的覆盖面广的 Malt 依存关系分析器,输入(Every dog chases a boy. He runs.)需要分词和标注。

  1. >>> from nltk.tag import RegexpTagger
  2. >>> tagger = RegexpTagger(
  3. ... [('^(chases|runs)$', 'VB'),
  4. ... ('^(a)$', 'ex_quant'),
  5. ... ('^(every)$', 'univ_quant'),
  6. ... ('^(dog|boy)$', 'NN'),
  7. ... ('^(He)$', 'PRP')
  8. ... ])
  9. >>> rc = nltk.DrtGlueReadingCommand(depparser=nltk.MaltParser(tagger=tagger))
  10. >>> dt = nltk.DiscourseTester(['Every dog chases a boy', 'He runs'], rc)
  11. >>> dt.readings()
  12. s0 readings:
  13. s0-r0: ([],[(([x],[dog(x)]) -> ([z3],[boy(z3), chases(x,z3)]))])
  14. s0-r1: ([z4],[boy(z4), (([x],[dog(x)]) -> ([],[chases(x,z4)]))])
  15. s1 readings:
  16. s1-r0: ([x],[PRO(x), runs(x)])

段落的第一句有两种可能的读法,取决于量词的作用域。第二句的唯一的读法通过条件<cite>PRO(x)`</cite>表示代词 He。现在让我们看看段落线的结果:

  1. >>> dt.readings(show_thread_readings=True)
  2. d0: ['s0-r0', 's1-r0'] : INVALID: AnaphoraResolutionException
  3. d1: ['s0-r1', 's1-r0'] : ([z6,z10],[boy(z6), (([x],[dog(x)]) ->
  4. ([],[chases(x,z6)])), (z10 = z6), runs(z10)])

当我们检查段落线d0d1时,我们看到读法s0-r0,其中 every dog 超出了a boy的范围,被认为是不可接受的,因为第二句的代词不能得到解释。相比之下,段落线d1中的代词(重写为z24 通过 等式(z24 = z20)绑定。

不可接受的读法可以通过传递参数filter=True过滤掉。

  1. >>> dt.readings(show_thread_readings=True, filter=True)
  2. d1: ['s0-r1', 's1-r0'] : ([z12,z15],[boy(z12), (([x],[dog(x)]) ->
  3. ([],[chases(x,z12)])), (z17 = z12), runs(z15)])

虽然这一小段是极其有限的,它应该能让你对于我们在超越单个句子后产生的语义处理问题,以及部署用来解决它们的技术有所了解。