NLP4J - 構文解析した結果からキーワードを抽出する

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

# 構文解析した結果からキーワードを抽出する例

「車が高速道路で急に停止した。エンジンから煙がもくもくと出た。」
という文から
「なにがどうした（名詞 ... 動詞）」
の関係を抽出することにしてみます。

日本語話者であれば
「車が ... 停止する」
「煙が ... 出る」
という関係を見つけ出すことができると思います。これを自然言語処理で行ってみたいと思います。

ここでいう「キーワード」は「切り出したい意味のある文字列」です。


# 構文解析をする

構文解析のエンジンとして、構文解析器として著名な「Cabocha」を利用します。
NLP4JでCabochaを利用する方法については以下の記事で紹介しております。
[NLP4J - Java で構文解析（Cabochaを利用）](https://qiita.com/drafts/b6e68e1f4e3925dc6d6a)

Cabochaで処理を行った結果、以下のような係り受け解析の結果（＝構文解析の結果）を取得することができます。

```
-。
  -た
    -する
      -停止
        -が
          -車
        -で
          -道路
            -高速
        -に
          -急
-。
  -た
    -出る
      -から
        -エンジン
      -が
        -煙
      -もくもくと
```

# 構文解析をした結果からキーワードを抽出する

「構文解析を行う」までは各種書籍やネット記事で紹介されているのですが、キーワード抽出の例は多くは紹介されていません。

NLP４Jでは構文解析結果からのキーワード抽出処理の機能を用意してあります。
UserPatternAnnotator という名称のクラスを用意しており、XML形式の設定ファイルを指定することでユーザーの定義による抽出を可能にしています。

XMLを用意することで、構文解析の結果から任意のパターン抽出ができるようになっています。（ライブラリにバグが無ければ！）

```xml
<?xml version="1.0" encoding="UTF-8"?>
<patterns lang="ja">
	<!-- facet : 抽出したキーワードをどのファセットに割り当てるか -->
	<!-- value : 抽出したパターンからキーワードの正規形を定義する -->
	<!-- {0.lex} : id=0 のキーワードの正規形 -->
	<!-- {2.lex} : id=2 のキーワードの正規形 -->
	<!-- <w /> : 単語を表す -->
	<!-- <w lex="/(が|は)/" /> : 正規形が「が」または「は」である単語 -->
	<!-- <w upos="ADP" /> : Universal POSが ADP(助詞) である単語 -->
	<!-- <w upos="NOUN" /> : Universal POSが NOUN(名詞) である単語 -->
	<pattern facet="pattern.sv" value="{0.lex} ... {2.lex}">
		<w id="2" upos="VERB">
			<w id="1" lex="/(が|は)/" upos="ADP">
				<w id="0" upos="NOUN" />
			</w>
		</w>
	</pattern>
	<pattern facet="pattern.sv" value="{0.lex} ... {2.lex}">
		<w id="3" lex="する">
			<w id="2" upos="NOUN">
				<w id="1" lex="/(が|は)/" upos="ADP">
					<w id="0" upos="NOUN" />
				</w>
			</w>
		</w>
	</pattern>
</patterns>
```

以下が実際のファイルになります。
https://github.com/oyahiroki/nlp4j/blob/master/nlp4j/nlp4j-examples/src/main/java/nlp4j/pattern/examples/UserPatternAnnotatorExample.java

内部的な処理としては
1. 構文解析の結果を取得してツリー構造とする
2. パターン抽出の設定を読み込んでツリー構造とする
3. 構文解析の結果のツリー構造と、パターン抽出設定のツリー構造を比較して、マッチしたところをキーワードに変換
という仕組みになっています。

# Maven Dependency

Maven での設定は以下になります。

```xml
<dependency>
	<groupId>org.nlp4j</groupId>
	<artifactId>nlp4j-core</artifactId>
	<version>[1.3.1.0,)</version>
</dependency>
```

以下がMavenレポジトリになります。
https://mvnrepository.com/artifact/org.nlp4j/nlp4j-core


# Java Code

以上で紹介したCabochaとキーワード抽出処理のコードは以下のようになります。

```java
package nlp4j.pattern.examples;
import nlp4j.Document;
import nlp4j.Keyword;
import nlp4j.KeywordWithDependency;
import nlp4j.cabocha.CabochaAnnotator;
import nlp4j.impl.DefaultDocument;
import nlp4j.pattern.UserPatternAnnotator;
public class UserPatternAnnotatorExample {
	public static void main(String[] args) throws Exception {
		String text = "車が高速道路で急に停止した。エンジンから煙がもくもくと出た。";
		Document doc = new DefaultDocument();
		doc.putAttribute("text", text);
		{ // 係り受け解析
			CabochaAnnotator ann = new CabochaAnnotator();
			ann.setProperty("encoding", "MS932");
			ann.setProperty("target", "text");
			ann.annotate(doc);
		}
		{ // 係り受けパターン抽出
			UserPatternAnnotator ann = new UserPatternAnnotator();
			ann.setProperty("file", "src/test/resources/nlp4j.pattern.examples/pattern-ja-sv.xml");
			ann.annotate(doc);
		}
		// Expected Result
		// 車 ... 停止
		// 煙 ... 出る
		for (Keyword kwd : doc.getKeywords("pattern")) {
			System.err.println(kwd.getLex());
		}
	}
}
```

# 結果

コードを実行すると以下の結果が出力されます。
構文解析の結果から、XMLの設定だけでキーワードの抽出ができました！

```
車 ... 停止
煙 ... 出る
```

# まとめ

NLP4J を利用すると構文解析の結果からキーワードの抽出ができます。


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