python nltk 8 分析句子结构

英文文档 http://www.nltk.org/book/
中文文档 https://www.bookstack.cn/read/nlp-py-2e-zh/0.md
以下编号按个人习惯

Analyzing Sentence Structure(分析句子结构)

1 Some Grammatical Dilemmas(一些语法困境)

歧义在语言中是普遍存在的,重要的目的是能够理解自然语言。

2 What’s the Use of Syntax?(语法有什么用)

2.1 Beyond n-grams(超越n-grams)

下图中,我们系统地将较长的序列替换为较短的序列,并且保留了语法性。每个组成单元的序列实际上都可以被一个单词替换,最后我们只得到两个元素。
在这里插入图片描述
替换单词序列:从第一行开始,我们可以将特定的单词序列(例如the brook)替换为单个的单词(例如it);重复这个过程,我们得到一个合乎语法的两个词的句子。

3 Context Free Grammar(上下文无关语法)

无上下文语法定义在nltk.grammar模块中。下面例子定义了一个简单的语法,并用其解析句子。
上下文无关语法不考虑它所处的上下文。

# 简单的无上下文语法
def asimple_grammar():
    grammar1 = CFG.fromstring("""
        S -> NP VP
        VP -> V NP | V NP PP
        PP -> P NP
        V -> "saw" | "ate" | "walked"
        NP -> "John" | "Mary" | "Bob" | Det N | Det N PP
        Det -> "a" | "an" | "the" | "my"
        N -> "man" | "dog" | "cat" | "telescope" | "park"
        P -> "in" | "on" | "by" | "with"
        """)

    sent1 = "Mary saw Bob".split()
    rd_parser = nltk.RecursiveDescentParser(grammar1)
    # 解析sent1。只有一种结果
    for tree in rd_parser.parse(sent1):
        print(tree)

    sent2 = "the dog saw a man in the park".split()
    # 解析sent2,得到两棵树,则在结构上有歧义——介词短语附件歧义
    for tree in rd_parser.parse(sent2):
        print(tree)

可以在一个文件中自己编写语法,并加载。

# 在文件mygrammar.cfg中,编写自己的语法。
# 加载自定义的语法文件
grammar1 = nltk.data.load('file:mygrammar.cfg')

mygrammar.cfg文件中内容如下:

S -> NP VP
VP -> V NP | V NP PP
PP -> P NP
V -> "saw" | "ate" | "walked"
NP -> "John" | "Mary" | "Bob" | Det N | Det N PP
Det -> "a" | "an" | "the" | "my"
N -> "man" | "dog" | "cat" | "telescope" | "park"
P -> "in" | "on" | "by" | "with"

一个语法被认为是递归的:如果语法类型出现在产生式左侧也出现在右侧。
下面例子中Nom -> Adj Nom 此为一个递归。而S -> NP VP 和 VP -> V S构成间接递归 。

grammar2 = nltk.CFG.fromstring("""
      S  -> NP VP
      NP -> Det Nom | PropN
      Nom -> Adj Nom | N                  #递归
      VP -> V Adj | V NP | V S | V NP PP
      PP -> P NP
      PropN -> 'Buster' | 'Chatterer' | 'Joe'
      Det -> 'the' | 'a'
      N -> 'bear' | 'squirrel' | 'tree' | 'fish' | 'log'
      Adj  -> 'angry' | 'frightened' |  'little' | 'tall'
      V ->  'chased'  | 'saw' | 'said' | 'thought' | 'was' | 'put'
      P -> 'on'
      """)

4 Parsing With Context Free Grammar(使用上下文无关语法进行解析)

解析器:根据文法产生式处理输入的句子,并建立符合语法的一个或多个组分的结构。语法是格式正确的说明性规范,仅是一个字符串,不是程序。例如问答系统对提交的问题进行语法分析。
简单的解析算法有①递归下降解析的自上而下的方法②移位减少解析的自下而上的方法
复杂的解析算法有①带有自下向上过滤的自顶向下方法(左角解析)② 图表解析的动态编程技术

4.1 Recursive Descent Parsing(递归向下解析)

# nltk 提供了递归下降解析器
rd_parser = nltk.RecursiveDescentParser(grammar1)

本方法中,解析树向下扩展的过程可通过图形演示,看到实际效果

nltk.app.rdparser()

4.2 Shift-Reduce Parsing(移进-归约分析)

简单的自下而上的解析器,移进-归约分析器试图查找与语法产生的右侧相对应的单词和短语序列,并用左侧代替他们,直到减少为S。

# nltk提供移进-归约分析器。此分析器最多只能找到一个解析
sr_parser = nltk.ShiftReduceParser(grammar1)

查看分析的过程

nltk.app.srparser()

5 Dependencies and Dependency Grammar(依存关系和依存文法)

短语结构文法是关于词和词序列如何结合起来形成句子成分的。
依存语法集中关注的是词和其他词之间的关系。依存关系是一个中心词(通常是动词)与它的依赖之间的二元对称关系。其他词语中心词要么依赖,要么依赖路径与它联通。

# nltk为依存语法编码的一种方式。只能捕捉依存关系信息,不能指定关系类型。
groucho_dep_grammar = nltk.DependencyGrammar.fromstring("""
     'shot' -> 'I' | 'elephant' | 'in'
     'elephant' -> 'an' | 'in'
     'in' -> 'pajamas'
     'pajamas' -> 'my'
     """)
print(groucho_dep_grammar)

在依存语法的传统中,下表中的动词具有不同的配价,配价限制不仅适用于动词,也适用于其他类的中心词。
在这里插入图片描述
到目前为止,语法能否扩大到覆盖自然语言的大型语料库,还是非常困难的。将语法模块化是很难的,每部分语法可以独立开发。

6 Grammar Development(语法开发)

访问树库,开发广泛覆盖的语法

# treebank中包含许多人工标注句法树的句法
t = treebank.parsed_sents('wsj_0001.mrg')[0]
print(t)
# 中央研究院树库语料
nltk.corpus.sinica_treebank.parsed_sents()[3450].draw()

随着语法覆盖范围的增加和输入句子长度的增长,分析树的数量也在增长。这就会存在歧义。例如:

grammar = nltk.CFG.fromstring("""
    S -> NP V NP
    NP -> NP Sbar
    Sbar -> NP V
    NP -> 'fish'
    V -> 'fish'
    """)
tokens = ["fish"] * 5
cp = nltk.ChartParser(grammar)
for tree in cp.parse(tokens):
    print(tree)

上面代码的结果,输出了两个树:
在这里插入图片描述
因此,处理歧义是开发广泛覆盖的解析器的关键。图表分析器岁提高了计算同一句子的多个分析的效率,但仍然会被大量的分析淹没。另一种有效解决方案:加权语法和概率解析算法

def give(t):
    return t.label() == 'VP' and len(t) > 2 and t[1].label() == 'NP' and (
            t[2].label() == 'PP-DTV' or t[2].label() == 'NP') and (
                   'give' in t[0].leaves() or 'gave' in t[0].leaves())
                   
def sent(t):
    return ' '.join(token for token in t.leaves() if token[0] not in '*-0')

def print_node(t, width):
    output = "%s %s: %s / %s: %s" % (sent(t[0]), t[1].label(), sent(t[1]), t[2].label(), sent(t[2]))
    if len(output) > width:
        output = output[:width] + "..."
    print(output)

# 加权语法
def weighted_grammar():
    # 检查所有涉及give的介词和双宾语结构的实例
    for tree in nltk.corpus.treebank.parsed_sents():
        for t in tree.subtrees(give):
            print_node(t, 72)

概率上下文无关文法(PCFG),要求产生式所有给定的左侧的概率之和为1。分析树被分配了概率,结果返回概率最大最有可能的解析。

# 概率上下文无关文法
grammar = nltk.PCFG.fromstring("""
    S    -> NP VP              [1.0]
    VP   -> TV NP              [0.4]
    VP   -> IV                 [0.3]
    VP   -> DatV NP NP         [0.3]
    TV   -> 'saw'              [1.0]
    IV   -> 'ate'              [1.0]
    DatV -> 'gave'             [1.0]
    NP   -> 'telescopes'       [0.8]
    NP   -> 'Jack'             [0.2]
    """)
viterbi_parser = nltk.ViterbiParser(grammar)
for tree in viterbi_parser.parse(['Jack', 'saw', 'telescopes']):
    print(tree)

在这里插入图片描述

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值