NLP4J - Java で 英語の構文解析結果のルールのパターンマッチを適用してキーワードを抽出する

[NLP4J Index](https://qiita.com/oyahiroki/items/ba79a759c599d3459d29)

#やりたいこと

英文の構文解析結果に対して「こういう構文（係り受け）があればキーワードとして抽出しなさい」というルールを定義しておき、ルールの適用結果を利用したい。
例えば「主語 ... 動詞 ... 目的語」の関係を抽出できれば、何がどうなっているのかの関係をきれいに抽出することができ、テキストマイニングなどに応用することができる。


#構文解析結果の確認

そもそも構文解析の結果がどういうものになっていないのかを知らないと抽出ルールを定義できないので、まずは確認してみます。

構文解析の表現方法は統一されたものが無い（はず）ですが、NLP4Jでは各実装の結果を統一的なフォーマットにしています。

日本語でも英語でも、Stanford NLPでも Caobocha でも GiNZAでも同じ形式で構文解析結果を表示します。各実装ごとにライブラリを用意しています。

```java
String text = "I eat sushi with chopsticks.";
// Document
Document doc = new DefaultDocument();
doc.putAttribute("text", text);
System.err.println("Document: " + doc.getAttributeAsString("text"));

{ // Annotation 1: nlp4j.stanford.StanfordPosDependencyAnnotator
	StanfordPosDependencyAnnotator ann = new StanfordPosDependencyAnnotator();
	ann.setProperty("target", "text");
	ann.annotate(doc);
}

System.err.println("Extracted Keyword in XML:");
for (Keyword kwd : doc.getKeywords()) {
	if (kwd instanceof KeywordWithDependency) {
		System.err.println(((KeywordWithDependency) kwd).toStringAsXml());
	}
}

```

#構文解析結果

構文解析結果のような「ツリー状」のデータを表現するには、JSONよりもXMLが読みやすいのでXMLにしてあります。
以下のような構文解析結果になりました。

```
Extracted Keyword in XML:
<?xml version="1.0" encoding="UTF-8"?>
<w begin="2" depth="0" end="5" facet="VBP" id="0" lex="eat" relation="root" sequence="0" str="eat">
    <w begin="0" depth="1" end="1" facet="PRP" id="1" lex="I" relation="nsubj" sequence="1" str="I"/>
    <w begin="6" depth="1" end="11" facet="NN" id="2" lex="sushi" relation="obj" sequence="2" str="sushi"/>
    <w begin="17" depth="1" end="27" facet="NNS" id="3" lex="chopstick" relation="obl" sequence="3" str="chopsticks">
        <w begin="12" depth="2" end="16" facet="IN" id="4" lex="with" relation="case" sequence="4" str="with"/>
    </w>
    <w begin="27" depth="1" end="28" facet="." id="5" lex="." relation="punct" sequence="5" str="."/>
</w>
```



#ルールの定義（XML形式）

例として「主語 ... 動詞 ... 目的語」の関係を抽出するようなルールを記述してみます。複数のルール定義も可能ですし、異なる構文パターンを抽出することも可能です。

構文解析結果の一部をコピペすればルールを定義することができます。
ルールの文法についてはまた別の機会に紹介したいと思います。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<patterns lang="en">
	<pattern facet="pattern.svo" value="{1.lex} ... {0.lex} ... {2.lex}">
		<w id="0">
			<w id="1" relation="nsubj" />
			<w id="2" relation="obj" />
		</w>
	</pattern>
</patterns>
```

#ルールの適用

「Annotator」というかたちで処理を定義しています。

```java
String text = "I eat sushi with chopsticks.";

// Document
Document doc = new DefaultDocument();
doc.putAttribute("text", text);
System.err.println("Document: " + doc.getAttributeAsString("text"));

{ // Annotation 1: nlp4j.stanford.StanfordPosDependencyAnnotator
	StanfordPosDependencyAnnotator ann = new StanfordPosDependencyAnnotator();
	ann.setProperty("target", "text");
	ann.annotate(doc);
}

{ // Annotation 2: nlp4j.pattern.UserPatternAnnotator
	UserPatternAnnotator ann = new UserPatternAnnotator();
	ann.setProperty("file", "src/test/resources/nlp4j.pattern.examples/" //
			+ "pattern-en-5syntax_svo_simple.xml");
	ann.annotate(doc);
}

// Expected Result
System.err.println("Extracted Pattern:");
for (Keyword kwd : doc.getKeywords("pattern")) {
	System.err.println(kwd.getFacet() + " : " + kwd.getLex());
}
```

#処理結果

「主語 ... 動詞 ... 目的語」の関係が抽出できました！

```
Extracted Pattern:
pattern.svo : I ... eat ... sushi
```



#Maven

使ってみたいという方はこちらでお試しください。

```xml
<dependencies>
	<dependency>
		<groupId>org.nlp4j</groupId>
		<artifactId>nlp4j-core</artifactId>
		<version>[1.3.1.0,)</version>
	</dependency>
	<dependency>
		<groupId>org.nlp4j</groupId>
		<artifactId>nlp4j-stanford</artifactId>
		<version>[1.0.0.0,)</version>
	</dependency>
	<!-- https://mvnrepository.com/artifact/edu.stanford.nlp/stanford-corenlp -->
	<dependency>
		<groupId>edu.stanford.nlp</groupId>
		<artifactId>stanford-corenlp</artifactId>
		<version>4.0.0</version>
		<scope>provided</scope>
	</dependency>
	<!-- https://mvnrepository.com/artifact/edu.stanford.nlp/stanford-corenlp -->
	<dependency>
		<groupId>edu.stanford.nlp</groupId>
		<artifactId>stanford-corenlp</artifactId>
		<version>4.0.0</version>
		<classifier>models</classifier>
		<scope>provided</scope>
	</dependency>
</dependencies>

```

[NLP4J Index](https://qiita.com/oyahiroki/items/ba79a759c599d3459d29)