web-dev-qa-db-ja.com

効率的な文脈自由文法パーサー、できればPython対応

私は自分のプロジェクトの1つで英語の小さなサブセットを解析する必要があります。これは、(1レベルの)素性構造( )を持つ文脈自由文法として記述されており、効率的に行う必要があります。

現在、私は [〜#〜] nltk [〜#〜] のパーサーを使用しています。これは正しい出力を生成しますが、非常に低速です。約450のかなりあいまいな非レキシコンルールと50万のレキシカルエントリの私の文法では、結果のツリーの数に応じて、単純な文の解析に2〜30秒かかる場合があります。字句エントリは、パフォーマンスにほとんどまたはまったく影響を与えません。

もう1つの問題は、最初に(25MB)文法+レキシコンをロードするのに最大1分かかる可能性があることです。

私が文献で見つけることができることから、そのような文法(EarleyまたはCKY)を解析するために使用されるアルゴリズムの実行時間は、文法のサイズに対して線形であり、入力トークンリストのサイズに対して立方である必要があります。 NLTKでの私の経験は、文法の絶対的なサイズではなく、あいまいさがパフォーマンスを最も損なうものであることを示しています。

だから今私はNLTKに代わるCFGパーサーを探しています。 [〜#〜] ply [〜#〜] を検討してきましたが、私の場合に必要なCFGの素性構造をサポートしているかどうか、および私が行った例がわかりません。見たところ、文法を指定するだけでなく、多くの手続き型解析を行っているようです。機能構造体をサポートし、宣言型文法を使用するPLYの例を誰かに見せてもらえますか?

また、必要なことを効率的に実行できる他のパーサーでも問題ありません。 Pythonインターフェースが望ましいですが、絶対に必要というわけではありません。

18
Max Shawabkeh

ぜひ Pyparsing をご覧ください。これは、私が遭遇した構文解析の最もPythonicな実装であり、純粋に学術的な観点からは優れた設計です。

[〜#〜] antlr [〜#〜]JavaCC の両方を使用して、地元の大学で翻訳者とコンパイラの理論を教えました。それらは両方とも優れていて成熟していますが、Pythonプロジェクトでは使用しません。

とは言うものの、プログラミング言語とは異なり、自然言語は構文よりもセマンティクスに重点を置いているため、既存の解析ツールの学習曲線をスキップして、自家製(トップダウン、バックトラック、無制限)を使用する方がはるかに良いでしょう。先読み)字句アナライザーとパーサー、および構文解析されたがあいまいな自然言語の文が何を意味するかを理解するコードの作成に多くの時間を費やしています。

12
Apalala

ツーリングはさておき...

理論から、同じ言語を定義する無限の文法があることを覚えているかもしれません。文法を分類し、特定の言語の「標準」または「最小」のどちらであるかを判断するための基準がありますが、最終的には、「最良の」文法が、手元のタスクとツールにとってより便利なものです( CFGのLLお​​よびLR文法への変換?)。

そうすれば、英語の文を解析するのに巨大なレキシコンはおそらく必要ありません。ドイツ語やラテン語(またはスペイン語)のような言語の単語について知られることはたくさんありますが、多くの場合、恣意的で曖昧な英語ではありません。文の構造に到達するために必要なキーワードのみを含む小さな辞書で逃げることができるはずです。いずれにせよ、選択した文法は、そのサイズに関係なく、ツールが直接使用できる方法でキャッシュできます(つまり、文法の解析をスキップできます)。

それを考えると、他の誰かがすでに取り組んでいるより単純なパーサーを見てみるのは良い考えかもしれません。文献には何千ものものがあるに違いありません。さまざまなアプローチを研究することで、自分自身を評価することができ、他の人のアプローチを採用するようになる可能性があります。

最後に、すでに述べたように、自然言語の解釈は、構文解析よりも人工知能に関するものです。構造が意味を決定し、意味が構造を決定するので、両方を同時にプレイする必要があります。 80年代以降、私が文献で見たアプローチは、さまざまな専門エージェントに「 黒板 "」に対する問題の解決を試みさせることです。このアプローチでは、構文分析と意味分析が同時に行われます。

2
Apalala

C++で記述された非常に効率的なPCFGパーサーであるbitparの使用をお勧めします。シェルベースのPythonラッパーを作成しました。 https://github.com/andreasvc/eodop/blob/master/bitpar.py を参照してください。

2
Andreas

これには少し遅れますが、次の2つのオプションがあります。

Spark はPythonで書かれたアーリーパーサーです。

Elkhound はC++で記述されたGLRパーサーですElkhoundはBisonのような構文を使用します

1
Jerry

限られた語彙のコマンド解析に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']

語彙を増やすと、これは遅くなる可能性があります。 50万エントリ?妥当な機能語彙は5000〜6000語程度だと思いました。また、処理できる文の構造はかなり制限されます。NLTKの目的は自然言語です。

1
PaulMcG

ANTLRは、私がJavaで知っている最高のパーサジェネレーターだと思います。 JythonがPythonとJava)が相互作用するための良い方法を提供するかどうかはわかりません。

0
duffymo

[〜#〜] peg [〜#〜] 言語(すべてのCFGができるとは思いませんが、おそらく多くのCFGができると思います)として表現できる場合は、 pyPEG 、これはPackrat解析実装を使用する場合は線形時間であると想定されます(ただし、メモリ使用量が禁止される可能性があります)。

久しぶりに構文解析とコンパイルの研究を始めたばかりなので、経験はありませんが、この比較的最新の手法についての良い話題を読んでいます。 YMMV。

0
Binary Phile

PyPyで実行してみてください。はるかに高速になる可能性があります。

0
TryPyPy