将标记器与NLTK组合成语法和解析器
nlp
nltk
python
28
0

我正在学习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")

现在,我想将此语法扩展为处理带有其他名词和动词的句子。如何在语法中不手动定义这些名词和动词的情况下将其添加到语法中?

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

参考资料:
Stack Overflow
收藏
评论
共 3 个回答
高赞 时间 活跃

您可以在文本上运行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的建议进行操作,从元组中拉出标签(单词,标签),并将这些标签用作“终端”(当我们创建一个完全标记的句子时,树的底部节点)。

最终,这并不能满足我在名词短语中标记头部名词的愿望,因为我不能将头部名词“单词”引入语法中,因为语法只有标签。

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

我用来打印的表格与通过加载语法文件(parser = nltk.load_parser('grammar.cfg'))来设置解析器的效果很好。生成的行之一如下所示:VBG->“ fencing” | “绑定” |“适量” |“生活” ...超过30个单词...

因此,现在我的语法将实际的单词作为终端,并分配了与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等其他程序包中的其他资源?

收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题