[英]Efficient Context-Free Grammar parser, preferably Python-friendly
我需要为我的一个项目解析一小部分英语,描述为具有(1级)特征结构的无上下文语法( 示例 ),我需要有效地完成它。
现在我正在使用NLTK的解析器,它产生正确的输出,但速度非常慢。 对于我的约450个相当模糊的非词典规则和50万个词条的语法,解析简单的句子可能需要2到30秒,这取决于所得到的树的数量。 词条对性能几乎没有影响。
另一个问题是在开始时加载(25MB)语法+词典可能需要一分钟。
从我在文献中可以找到的,用于解析这种语法(Earley或CKY)的算法的运行时间应该与语法的大小呈线性关系,并且应该与输入令牌列表的大小相关。 我对NLTK的体验表明,歧义是最能伤害表现的,而不是语法的绝对大小。
所以现在我正在寻找一个CFG解析器来取代NLTK。 我一直在考虑PLY,但我不知道它是否支持CFG中的特征结构,这在我的情况下是必需的,我看到的例子似乎是在进行大量的过程解析,而不仅仅是指定语法。 有人能告诉我一个PLY的例子,它既支持功能结构又使用声明性语法?
对于能够有效地完成我需要的任何其他解析器,我也没问题。 Python接口是首选,但不是绝对必要的。
除了工具......
您可能从理论上记得有无限语法定义相同的语言。 对于给定语言,存在对语法进行分类和确定哪个是“规范”或“最小”语法的标准,但最终,“最佳”语法是对于手头的任务和工具更方便的语法(记住将CFG转换为LL和LR语法?)。
然后,你可能不需要一个巨大的词汇来用英语解析一个句子。 关于德语或拉丁语(甚至西班牙语)等语言中的单词有很多值得一提的地方,但在很多时候没有任意的和ambiguos英语。 你应该能够使用一个小词典,它只包含到达句子结构所需的关键词。 无论如何,您选择的语法,无论其大小,都可以以工具可以直接使用它的方式进行缓存(即,您可以跳过解析语法)。
鉴于此,看一看其他人已经使用的更简单的解析器可能是一个好主意。 文献中必须有成千上万的人。 学习不同的方法可以让你评估自己的方法,并可能导致你采用别人的方法。
最后,正如我已经提到的,解释自然语言更多是关于人工智能而不是解析。 因为结构决定意义和意义决定结构,你必须同时玩两者。 自80年代以来,我在文献中看到的一种方法是让不同的专业代理人针对“ 黑板 ”解决问题。 通过这种方法,合成和语义分析同时发生。
我建议使用bitpar,一种用C ++编写的非常高效的PCFG解析器。 我为它编写了一个基于shell的Python包装器,请参阅https://github.com/andreasvc/eodop/blob/master/bitpar.py
我已经使用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")
打印:
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']
随着您扩展词汇量,这可能会变慢。 五十万条记录? 我认为合理的功能词汇量大约为5-6千字。 你可以处理的句子结构非常有限 - 自然语言是NLTK的用途。
我认为ANTLR是我所知道的最适合Java的解析器生成器。 我不知道Jython是否会为Python和Java提供一个很好的交互方式。
尝试在PyPy上运行它,它可能会快得多。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.