繁体   English   中英

使用 NLTK 将分词器组合成语法和解析器

[英]Combining a Tokenizer into a Grammar and Parser with NLTK

我正在阅读 NLTK 书,但我似乎无法做一些看起来是构建体面语法的自然第一步的事情。

我的目标是为特定的文本语料库构建语法。

(最初的问题:我应该尝试从头开始学习语法还是应该从预定义的语法开始?如果我应该从另一种语法开始,哪种语法适合英语开始?)

假设我有以下简单的语法:

simple_grammar = nltk.parse_cfg("""
S -> NP VP
PP -> P NP
NP -> Det N | Det N PP
VP -> V NP | VP PP
Det -> 'a' | 'A'
N -> 'car' | 'door'
V -> 'has'
P -> 'in' | 'for'
 """);

这个语法可以解析一个很简单的句子,比如:

parser = nltk.ChartParser(simple_grammar)
trees = parser.nbest_parse("A car has a door")

现在我想扩展这个语法来处理其他名词和动词的句子。 如何将这些名词和动词添加到我的语法中,而无需在语法中手动定义它们?

例如,假设我希望能够解析句子“A car has Wheels”。 我知道提供的分词器可以神奇地找出哪些词是动词/名词等。如何使用分词器的输出来告诉语法“轮子”是名词?

您可以在文本上运行 POS 标记器,然后调整语法以处理 POS 标记而不是单词。

> text = nltk.word_tokenize("A car has a door")
['A', 'car', 'has', 'a', 'door']

> tagged_text = nltk.pos_tag(text)
[('A', 'DT'), ('car', 'NN'), ('has', 'VBZ'), ('a', 'DT'), ('door', 'NN')]

> pos_tags = [pos for (token,pos) in nltk.pos_tag(text)]
['DT', 'NN', 'VBZ', 'DT', 'NN']

> simple_grammar = nltk.parse_cfg("""
  S -> NP VP
  PP -> P NP
  NP -> Det N | Det N PP
  VP -> V NP | VP PP
  Det -> 'DT'
  N -> 'NN'
  V -> 'VBZ'
  P -> 'PP'
  """)

> parser = nltk.ChartParser(simple_grammar)
> tree = parser.parse(pos_tags)

我知道这是一年后,但我想补充一些想法。

我选择了很多不同的句子,并用我正在从事的项目的词性标记它们。 从那里我按照 StompChicken 的建议做,从元组(词,标签)中提取标签并将这些标签用作“终端”(树的底部节点,因为我们创建了一个完全标记的句子)。

最终,这不符合我在名词短语中标记主要名词的愿望,因为我无法将主要名词“单词”拉入语法,因为语法只有标签。

所以我所做的是使用一组 (word, tag) 元组来创建一个标签字典,所有带有该标签的单词作为该标签的值。 然后我将这本词典打印到 screen/grammar.cfg(上下文无关语法)文件中。

我用来打印它的表单与通过加载语法文件( parser = nltk.load_parser('grammar.cfg') )设置解析器完美配合。 它生成的其中一行如下所示:

VBG -> "fencing" | "bonging" | "amounting" | "living" ... over 30 more words...

所以现在我的语法将实际单词作为终端并分配与nltk.tag_pos相同的标签。

希望这可以帮助其他任何想要自动标记大型语料库并仍然将实际单词作为语法中的终端的人。

import nltk
from collections import defaultdict

tag_dict = defaultdict(list)

...
    """ (Looping through sentences) """

        # Tag
        tagged_sent = nltk.pos_tag(tokens)

        # Put tags and words into the dictionary
        for word, tag in tagged_sent:
            if tag not in tag_dict:
                tag_dict[tag].append(word)
            elif word not in tag_dict.get(tag):
                tag_dict[tag].append(word)

# Printing to screen
for tag, words in tag_dict.items():
    print tag, "->",
    first_word = True
    for word in words:
        if first_word:
            print "\"" + word + "\"",
            first_word = False
        else:
            print "| \"" + word + "\"",
    print ''

解析是一个棘手的问题,很多事情都可能出错!

您需要(至少)三个组件,一个标记器,一个标记器,最后是解析器。

首先,您需要将正在运行的文本标记为标记列表。 这可以像围绕空格分割输入字符串一样简单,但是如果您正在解析更一般的文本,您还需要处理数字和标点符号,这很重要。 例如,句末句号通常不被视为它所附单词的一部分,但标记缩写的句号通常是。

当您有输入标记列表时,您可以使用标记器来尝试确定每个单词的 POS,并使用它来消除输入标记序列的歧义。 这有两个主要优点:首先,它加快了解析速度,因为我们不再需要考虑由歧义词许可的替代假设,因为 POS-tagger 已经做到了这一点。 其次,它改进了未知单词的处理,即。 不在你的语法中的单词,也为这些单词分配一个标签(希望是正确的)。 以这种方式组合解析器和标记器是司空见惯的。

然后 POS 标签将构成语法中的前置终结符,前置终结符是产生式的左侧,只有终结符作为它们的右侧。 即在 N -> "house", V -> "jump" 等中。 N 和 V 是前置词。 具有句法的文法,两边只有非终结符,产生式和词法产生式,一个非终结符通向一个终结符,这是相当常见的。 大多数时候这在语言上是有意义的,并且大多数 CFG 解析器要求语法采用这种形式。 然而,可以通过从 RHS 中具有非终端的任何终端创建“虚拟产品”以这种方式表示任何 CFG。

如果您想在语法中进行比标记器输出的更多(或更少)细粒度标记区分,则可能需要在 POS 标记和前置终端之间进行某种映射。 然后,您可以使用标注器的结果初始化图表,即。 跨越每个输入标记的适当类别的被动项目。 遗憾的是我不知道 NTLK,但我确信有一种简单的方法可以做到这一点。 当图表被播种时,解析可以正常进行,并且可以以常规方式提取任何解析树(也包括单词)。

然而,在大多数实际应用中,您会发现解析器可以返回几种不同的分析,因为自然语言是高度模糊的。 我不知道你想解析什么样的文本语料库,但如果它是像自然语言这样的东西,你可能必须构建某种解析选择模型,这将需要一个树库,一个解析树的集合一些大小从几百到几千个解析不等,这一切都取决于您的语法和您需要的准确结果。 给定这个树库,人们可以自动推断出与之对应的 PCFG。 然后可以将 PCFG 用作对解析树进行排序的简单模型。

所有这些都需要自己做很多工作。 你用解析结果做什么? 您是否查看过 NTLK 或其他包(例如 StanfordParser 或 BerkeleyParser)中的其他资源?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM