高效的上下文无关语法分析器,最好是Python友好的

高效的上下文无关语法分析器,最好是Python友好的,python,parsing,nlp,grammar,nltk,Python,Parsing,Nlp,Grammar,Nltk,我需要为我的一个项目解析一小部分英语,描述为具有(1级)特征结构的上下文无关语法(),我需要高效地完成这项工作 现在我使用的是的解析器,它产生正确的输出,但速度非常慢。对于我大约450条相当模糊的非词汇规则和50万条词汇条目的语法,解析简单句子可能需要2到30秒,这取决于生成的树的数量。词汇条目对表现几乎没有影响 另一个问题是,在开始加载(25MB)语法+词汇可能需要一分钟的时间 从文献中可以发现,用于解析此类语法(Earley或CKY)的算法的运行时间应该与语法的大小成线性关系,与输入标记列表

我需要为我的一个项目解析一小部分英语,描述为具有(1级)特征结构的上下文无关语法(),我需要高效地完成这项工作

现在我使用的是的解析器,它产生正确的输出,但速度非常慢。对于我大约450条相当模糊的非词汇规则和50万条词汇条目的语法,解析简单句子可能需要2到30秒,这取决于生成的树的数量。词汇条目对表现几乎没有影响

另一个问题是,在开始加载(25MB)语法+词汇可能需要一分钟的时间

从文献中可以发现,用于解析此类语法(Earley或CKY)的算法的运行时间应该与语法的大小成线性关系,与输入标记列表的大小成立方关系。我使用NLTK的经验表明,歧义是影响性能的最大因素,而不是语法的绝对大小

所以现在我正在寻找一个CFG解析器来代替NLTK。我一直在考虑,但我无法判断它是否支持CFGs中的功能结构,这在我的案例中是必需的,我看到的示例似乎在进行大量的过程分析,而不仅仅是指定语法。有谁能给我举一个既支持特性结构又使用声明式语法的例子吗


我还可以使用任何其他能够高效完成我需要的工作的解析器。Python接口更可取,但并非绝对必要。

我认为ANTLR是我所知道的最好的Java解析器生成器。我不知道Jython是否能为Python和Java提供一种很好的交互方式。

当然可以看看。这是我遇到的最具python风格的解析实现,从纯学术的角度来看,这是一个伟大的设计

我曾在当地一所大学教授翻译和编译理论。它们既好又成熟,但我不会在Python项目中使用它们


也就是说,与编程语言不同,自然语言更多的是关于语义而不是语法,因此您最好跳过现有解析工具的学习曲线,使用自制(自顶向下、回溯、无限前瞻)的词法分析器和解析器,然后花大量时间编写代码,找出经过解析但不明确的自然语言句子的含义。

如果它可以表示为一种语言(我不认为所有CFG都可以,但应该有很多CFG可以),那么您可以使用,在使用packrat解析实现时,这应该是线性时间(尽管可能会禁止内存使用)


我对它没有任何经验,因为我在远离它很长一段时间后才开始重新研究解析和编译,但我正在阅读一些关于这种相对最新技术的好消息。YMMV。

试着在PyPy上运行它,它可能会快得多。

我已经使用pyparsing进行了有限词汇的命令解析,但这里是pyparsing之上的一个小框架,用于处理您发布的示例:

from pyparsing import *

transVerb, transVerbPlural, transVerbPast, transVerbProg = (Forward() for i in range(4))
intransVerb, intransVerbPlural, intransVerbPast, intransVerbProg = (Forward() for i in range(4))
singNoun,pluralNoun,properNoun = (Forward() for i in range(3))
singArticle,pluralArticle = (Forward() for i in range(2))
verbProg = transVerbProg | intransVerbProg
verbPlural = transVerbPlural | intransVerbPlural

for expr in (transVerb, transVerbPlural, transVerbPast, transVerbProg,
            intransVerb, intransVerbPlural, intransVerbPast, intransVerbProg,
            singNoun, pluralNoun, properNoun, singArticle, pluralArticle):
    expr << MatchFirst([])

def appendExpr(e1, s):
    c1 = s[0]
    e2 = Regex(r"[%s%s]%s\b" % (c1.upper(), c1.lower(), s[1:]))
    e1.expr.exprs.append(e2)

def makeVerb(s, transitive):
    v_pl, v_sg, v_past, v_prog = s.split()
    if transitive:
        appendExpr(transVerb, v_sg)
        appendExpr(transVerbPlural, v_pl)
        appendExpr(transVerbPast, v_past)
        appendExpr(transVerbProg, v_prog)
    else:
        appendExpr(intransVerb, v_sg)
        appendExpr(intransVerbPlural, v_pl)
        appendExpr(intransVerbPast, v_past)
        appendExpr(intransVerbProg, v_prog)

def makeNoun(s, proper=False):
    if proper:
        appendExpr(properNoun, s)
    else:
        n_sg,n_pl = (s.split() + [s+"s"])[:2]
        appendExpr(singNoun, n_sg)
        appendExpr(pluralNoun, n_pl)

def makeArticle(s, plural=False):
    for ss in s.split():
        if not plural:
            appendExpr(singArticle, ss)
        else:
            appendExpr(pluralArticle, ss)

makeVerb("disappear disappears disappeared disappearing", transitive=False)
makeVerb("walk walks walked walking", transitive=False)
makeVerb("see sees saw seeing", transitive=True)
makeVerb("like likes liked liking", transitive=True)

makeNoun("dog")
makeNoun("girl")
makeNoun("car")
makeNoun("child children")
makeNoun("Kim", proper=True)
makeNoun("Jody", proper=True)

makeArticle("a the")
makeArticle("this every")
makeArticle("the these all some several", plural=True)

transObject = (singArticle + singNoun | properNoun | Optional(pluralArticle) + pluralNoun | verbProg | "to" + verbPlural)
sgSentence = (singArticle + singNoun | properNoun) + (intransVerb | intransVerbPast | (transVerb | transVerbPast) + transObject)
plSentence = (Optional(pluralArticle) + pluralNoun) + (intransVerbPlural | intransVerbPast | (transVerbPlural |transVerbPast) + transObject)

sentence = sgSentence | plSentence


def test(s):
    print s
    try:
        print sentence.parseString(s).asList()
    except ParseException, pe:
        print pe

test("Kim likes cars")
test("The girl saw the dog")
test("The dog saw Jody")
test("Kim likes walking")
test("Every girl likes dogs")
test("All dogs like children")
test("Jody likes to walk")
test("Dogs like walking")
test("All dogs like walking")
test("Every child likes Jody")
当你扩大词汇量时,这可能会变慢。50万个词条?我认为合理的功能性词汇大约有5000-6000个单词。而且你可以处理的句子结构会非常有限-自然语言就是NLTK的用途。

除此之外

从理论上你可能还记得,定义同一种语言的语法是无限的。对于给定的语言,有一些标准可以对语法进行分类,并确定哪一种是“规范的”或“最小的”,但最终,“最好的”语法是对手头的任务和工具更方便的语法(还记得CFG到LL和LR语法的转换吗?)

然后,你可能不需要庞大的词库来解析英语句子。德语、拉丁语(甚至西班牙语)等语言中的一个单词有很多值得了解的地方,但不是在很多时候武断和含糊不清的英语中。你应该能够使用一个只包含句子结构所需的关键词的小词典。无论如何,你选择的语法,无论大小,都可以以一种电子工具可以直接使用的方式缓存(即,您可以跳过语法分析)

有鉴于此,不妨看看其他人已经开发的更简单的解析器。文献中肯定有数千种。研究不同的方法可以让你评估自己的方法,并可能导致你采用其他人的方法


最后,正如我已经提到的,解释自然语言更多的是关于人工智能,而不是解析。因为结构决定意义,而意义决定结构,你必须同时处理两者。我在80年代以来的文献中看到的一种方法是让不同的专业代理进行拍摄在解决“”的问题时,使用这种方法,语法分析和语义分析同时进行。

这方面有点晚,但这里还有两个选项供您选择:

是用Python编写的早期解析器


是一个用C++的ELKHOND编写的GLR解析器,使用BISON类似的语法

< P>我推荐使用BITPAR,一个非常高效的PCFG解析器,用C++编写。我已经编写了一个基于shell的Python包装器,见

ANTLR实际上支持Python,在其他几种语言中:不知道,我只是用它来做java。谢谢更新。谢谢。但是,从我所看到的ANTLR(及其所有亲属,Lex/Yacc,Bison等)来看主要用于解析确定性编程语言,而不是不明确的自然语言。在我的例子中,一个中等长度的句子可能会导致20个解析树,我需要生成它们。注意:下一票不是我的。自然语言
Kim likes cars
['Kim', 'likes', 'cars']
The girl saw the dog
['The', 'girl', 'saw', 'the', 'dog']
The dog saw Jody
['The', 'dog', 'saw', 'Jody']
Kim likes walking
['Kim', 'likes', 'walking']
Every girl likes dogs
['Every', 'girl', 'likes', 'dogs']
All dogs like children
['All', 'dogs', 'like', 'children']
Jody likes to walk
['Jody', 'likes', 'to', 'walk']
Dogs like walking
['Dogs', 'like', 'walking']
All dogs like walking
['All', 'dogs', 'like', 'walking']
Every child likes Jody
['Every', 'child', 'likes', 'Jody']