私は最近、非常によく似た問題を解決していました-件名、アクション、オブジェクトを抽出する必要がありました。そして、このライブラリを確認できるように、私の作品をオープン ソース化しました:
https://github.com/krzysiekfonal/textpipeliner
これはスペイシー(nltkの反対)に基づいていますが、文ツリーにも基づいています。
たとえば、このドキュメントを例として spacy に埋め込んでみましょう:
import spacy
nlp = spacy.load("en")
doc = nlp(u"The Empire of Japan aimed to dominate Asia and the " \
"Pacific and was already at war with the Republic of China " \
"in 1937, but the world war is generally said to have begun on " \
"1 September 1939 with the invasion of Poland by Germany and " \
"subsequent declarations of war on Germany by France and the United Kingdom. " \
"From late 1939 to early 1941, in a series of campaigns and treaties, Germany conquered " \
"or controlled much of continental Europe, and formed the Axis alliance with Italy and Japan. " \
"Under the Molotov-Ribbentrop Pact of August 1939, Germany and the Soviet Union partitioned and " \
"annexed territories of their European neighbours, Poland, Finland, Romania and the Baltic states. " \
"The war continued primarily between the European Axis powers and the coalition of the United Kingdom " \
"and the British Commonwealth, with campaigns including the North Africa and East Africa campaigns, " \
"the aerial Battle of Britain, the Blitz bombing campaign, the Balkan Campaign as well as the " \
"long-running Battle of the Atlantic. In June 1941, the European Axis powers launched an invasion " \
"of the Soviet Union, opening the largest land theatre of war in history, which trapped the major part " \
"of the Axis' military forces into a war of attrition. In December 1941, Japan attacked " \
"the United States and European territories in the Pacific Ocean, and quickly conquered much of " \
"the Western Pacific.")
シンプルなパイプ構造を作成できるようになりました (パイプについては、このプロジェクトの readme を参照してください)。
pipes_structure = [SequencePipe([FindTokensPipe("VERB/nsubj/*"),
NamedEntityFilterPipe(),
NamedEntityExtractorPipe()]),
FindTokensPipe("VERB"),
AnyPipe([SequencePipe([FindTokensPipe("VBD/dobj/NNP"),
AggregatePipe([NamedEntityFilterPipe("GPE"),
NamedEntityFilterPipe("PERSON")]),
NamedEntityExtractorPipe()]),
SequencePipe([FindTokensPipe("VBD/**/*/pobj/NNP"),
AggregatePipe([NamedEntityFilterPipe("LOC"),
NamedEntityFilterPipe("PERSON")]),
NamedEntityExtractorPipe()])])]
engine = PipelineEngine(pipes_structure, Context(doc), [0,1,2])
engine.process()
その結果、次のようになります。
>>>[([Germany], [conquered], [Europe]),
([Japan], [attacked], [the, United, States])]
実際には、それは別のライブラリであるgrammaregexに強く基づいています(検索パイプ)。投稿からそれについて読むことができます:
https://medium.com/@krzysiek89dev/grammaregex-library-regex-like-for-text-mining-49e5706c9c6d#.zgx7odhsc
編集済み
実際、readme で示した例では adj を破棄していますが、必要なのはエンジンに渡されるパイプ構造を必要に応じて調整することだけです。たとえば、サンプル文の場合、すべての文ごとに3つの要素(subj、verb、adj)のタプルを提供するような構造/ソリューションを提案できます。
import spacy
from textpipeliner import PipelineEngine, Context
from textpipeliner.pipes import *
pipes_structure = [SequencePipe([FindTokensPipe("VERB/nsubj/NNP"),
NamedEntityFilterPipe(),
NamedEntityExtractorPipe()]),
AggregatePipe([FindTokensPipe("VERB"),
FindTokensPipe("VERB/xcomp/VERB/aux/*"),
FindTokensPipe("VERB/xcomp/VERB")]),
AnyPipe([FindTokensPipe("VERB/[acomp,amod]/ADJ"),
AggregatePipe([FindTokensPipe("VERB/[dobj,attr]/NOUN/det/DET"),
FindTokensPipe("VERB/[dobj,attr]/NOUN/[acomp,amod]/ADJ")])])
]
engine = PipelineEngine(pipes_structure, Context(doc), [0,1,2])
engine.process()
それはあなたに結果を与えるでしょう:
[([Donald, Trump], [is], [the, worst])]
少し複雑なのは、複合文があり、lib が文ごとに 1 つのタプルを生成するという事実にあります。パイプ構造のリストをエンジンに渡して、より多くのタプルを生成できるようにする可能性をすぐに追加します (私のプロジェクトにも必要です)。文ごと。しかし、今のところ、構造が VERB ではなく VERB/conj/VERB のみ異なる複合送信用の 2 番目のエンジンを作成するだけで解決できます (これらの正規表現は常に ROOT から始まるため、VERB/conj/VERB は 2 番目の動詞につながります)。重文):
pipes_structure_comp = [SequencePipe([FindTokensPipe("VERB/conj/VERB/nsubj/NNP"),
NamedEntityFilterPipe(),
NamedEntityExtractorPipe()]),
AggregatePipe([FindTokensPipe("VERB/conj/VERB"),
FindTokensPipe("VERB/conj/VERB/xcomp/VERB/aux/*"),
FindTokensPipe("VERB/conj/VERB/xcomp/VERB")]),
AnyPipe([FindTokensPipe("VERB/conj/VERB/[acomp,amod]/ADJ"),
AggregatePipe([FindTokensPipe("VERB/conj/VERB/[dobj,attr]/NOUN/det/DET"),
FindTokensPipe("VERB/conj/VERB/[dobj,attr]/NOUN/[acomp,amod]/ADJ")])])
]
engine2 = PipelineEngine(pipes_structure_comp, Context(doc), [0,1,2])
そして、両方のエンジンを実行すると、期待どおりの結果が得られます:)
engine.process()
engine2.process()
[([Donald, Trump], [is], [the, worst])]
[([Hillary], [is], [better])]
これが必要だと思います。もちろん、特定の例文のパイプ構造をすばやく作成しただけで、すべてのケースで機能するとは限りませんが、多くの文章構造を見たので、すでにかなりの割合を満たしていますが、FindTokensPipe などを追加するだけで済みます。現在うまくいかないケースと、いくつかの調整の後、非常に多くの可能な文をカバーできると確信しています(英語はそれほど複雑ではないので... :)