構文解析した結果からキーワードを抽出する例
「車が高速道路で急に停止した。エンジンから煙がもくもくと出た。」
という文から
「なにがどうした(名詞 ... 動詞)」
の関係を抽出することにしてみます。
日本語話者であれば
「車が ... 停止する」
「煙が ... 出る」
という関係を見つけ出すことができると思います。これを自然言語処理で行ってみたいと思います。
ここでいう「キーワード」は「切り出したい意味のある文字列」です。
構文解析をする
構文解析のエンジンとして、構文解析器として著名な「Cabocha」を利用します。
NLP4JでCabochaを利用する方法については以下の記事で紹介しております。
NLP4J - Java で構文解析(Cabochaを利用)
Cabochaで処理を行った結果、以下のような係り受け解析の結果(=構文解析の結果)を取得することができます。
-。
-た
-する
-停止
-が
-車
-で
-道路
-高速
-に
-急
-。
-た
-出る
-から
-エンジン
-が
-煙
-もくもくと
構文解析をした結果からキーワードを抽出する
「構文解析を行う」までは各種書籍やネット記事で紹介されているのですが、キーワード抽出の例は多くは紹介されていません。
NLP4Jでは構文解析結果からのキーワード抽出処理の機能を用意してあります。
UserPatternAnnotator という名称のクラスを用意しており、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
内部的な処理としては
- 構文解析の結果を取得してツリー構造とする
- パターン抽出の設定を読み込んでツリー構造とする
- 構文解析の結果のツリー構造と、パターン抽出設定のツリー構造を比較して、マッチしたところをキーワードに変換
という仕組みになっています。
Maven Dependency
Maven での設定は以下になります。
<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とキーワード抽出処理のコードは以下のようになります。
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 を利用すると構文解析の結果からキーワードの抽出ができます。