Javaの比較的新しい形態素解析器、Kuromoji。
lucene-gosenやGomokuのように辞書内包で、jarを落とせばその場で利用でき、Unidicに対応していて、ソースがLuceneのtrunkにコミットされているという、何かと気になる特徴の持ち主。
複数のモードを持っているようで、Searchモードを使うと「日本経済新聞」を「日本 | 経済 | 新聞」のように検索で利用しやすい形にばらして解析してくれたり、Extendedモードを使うと未知語をuni-gramにしてくれたりもするらしい。
今日はそんなKuromojiさんの導入から簡易な使い方までをさらっと追いかけてみた。
まずは下記ページからダウンロード。今回はkuromoji-0.7.5.tar.gzを利用。
Downloads - atilika/kuromoji
https://github.com/atilika/kuromoji/downloads
落としたファイルを解凍して中のjarをクラスパスに入れれば導入完了。とても簡単。
Mavenも利用できる。
<repository>
<id>ATILIKA dependencies</id>
<url>http://www.atilika.org/nexus/content/repositories/atilika</url>
</repository>
<dependency>
<groupId>org.atilika.kuromoji</groupId>
<artifactId>kuromoji</artifactId>
<version>0.7.6</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
Luceneと同じくApacheライセンス。ソースはgithubから落とせる。
$ git clone https://github.com/atilika/kuromoji.git
以下のようなコードで形態素解析できる。
// この2行で解析できる
Tokenizer tokenizer = Tokenizer.builder().build();
List<Token> tokens = tokenizer.tokenize("もう眠い");
// 結果を出力してみる
for (Token token : tokens) {
System.out.println("==================================================");
System.out.println("allFeatures : " + token.getAllFeatures());
System.out.println("partOfSpeech : " + token.getPartOfSpeech());
System.out.println("position : " + token.getPosition());
System.out.println("reading : " + token.getReading());
System.out.println("surfaceFrom : " + token.getSurfaceForm());
System.out.println("allFeaturesArray : " + Arrays.asList(token.getAllFeaturesArray()));
System.out.println("辞書にある言葉? : " + token.isKnown());
System.out.println("未知語? : " + token.isUnknown());
System.out.println("ユーザ定義? : " + token.isUser());
}
上記のコードは「もう眠い」という言葉を解析しています。結果は以下。
==================================================
allFeatures : 副詞,一般,*,*,*,*,もう,モウ,モー
partOfSpeech : 副詞,一般,*,*
position : 0
reading : モウ
surfaceFrom : もう
allFeaturesArray : [副詞, 一般, *, *, *, *, もう, モウ, モー]
辞書にある言葉? : true
未知語? : false
ユーザ定義? : false
==================================================
allFeatures : 形容詞,自立,*,*,形容詞・アウオ段,基本形,眠い,ネムイ,ネムイ
partOfSpeech : 形容詞,自立,*,*
position : 2
reading : ネムイ
surfaceFrom : 眠い
allFeaturesArray : [形容詞, 自立, *, *, 形容詞・アウオ段, 基本形, 眠い, ネムイ, ネムイ]
辞書にある言葉? : true
未知語? : false
ユーザ定義? : false
Builderにmodeを設定することで、解析結果が変化する。
Searchモードは「日本経済新聞」のような複数の言葉が組み合わさった単語を「日本 | 経済 | 新聞」のように分けて解析してくれる。全文検索エンジンと組み合わせて利用する場合に、日本経済新聞が「経済」や「新聞」でも検索可能になるので便利。
Extendedモードはそれに加えて、未知語をuni-gramとして扱う。たとえば「モバゲー」は「モ | バ | ゲ | ー」と1文字ごとに分割される。未知語を検索し損ねることを減らしてくれそうな機能。
String parseWord = "日本経済新聞でモバゲーの記事を読んだ";
Builder builder = Tokenizer.builder();
// ノーマルモード
Tokenizer normal = builder.build();
List<Token> tokensNormal = normal.tokenize(parseWord);
//=> 日本経済新聞 | で | モバゲー | の | 記事 | を | 読ん | だ
// Searchモード
builder.mode(Mode.SEARCH);
Tokenizer search = builder.build();
List<Token> tokensSearch = search.tokenize(parseWord);
//=> 日本 | 経済 | 新聞 | で | モバゲー | の | 記事 | を | 読ん | だ
// Extendsモード
builder.mode(Mode.EXTENDED);
Tokenizer extended = builder.build();
List<Token> tokensExtended = extended.tokenize(parseWord);
//=> 日本 | 経済 | 新聞 | で | モ | バ | ゲ | ー | の | 記事 | を | 読ん | だ
ソースを見る限りSearchモードは、漢字のみで構成される4文字以上の単語、もしくは7文字以上の単語に対してコストを重くすることで、分割を促しているようだ。
例えばカタカナ6文字の「ボトムアップ」は「ボトム | アップ」に分割されないが、9文字の「ボトムアップテスト」(IPA辞書にいる)はSearchモードにすると「ボトムアップ | テスト」に分割される。
Builderにユーザ辞書を渡して、その場で辞書をカスタマイズすることもできるらしい。
ユーザ辞書の書き方は、githubからソースを落としてくると「src/example/resources/userdict.txt」にサンプルが置いてある。
例えば「稀勢の里寛」を「稀勢の里 | 寛」に分割したい場合は、以下のように書くらしい。
稀勢の里寛,稀勢の里 寛,キセノサト ユタカ,カスタム人名
ユーザ辞書は以下のようにBuilderのuserDictionaryで指定する。
// ユーザ辞書を指定して解析
Builder builder = Tokenizer.builder();
builder.userDictionary("userdic.txt");
Tokenizer userTokenizer = builder.build();
List<Token> userTokens = userTokenizer.tokenize("稀勢の里寛");
//=> 稀勢の里 | 寛
ユーザ辞書はコストは設定しないらしい。ソースのUserDictionary.javaを見たら-100000と書いてあったので、ユーザ辞書に入れた単語は高確率で優先されることになりそう。
例えば「日,日,ヒ,カスタム名詞」をユーザ辞書に登録すると、「今日」が「今 | 日」にばらされたり、「日本」が「日 | 本」にばらされたりした。
KuromojiはUniDicにも対応しているらしい。
まずは下記サイトから個別ファイルの方のunidic-mecabのsrcをダウンロード(要登録)して解凍する。パッケージ版の方には辞書生成に必要な生ファイルがいないので注意。
形態素解析辞書 UniDic
http://www.tokuteicorpus.jp/dist/
次にKuromojiのソースをチェックアウト。
$ git clone https://github.com/atilika/kuromoji.git
チェックアウトしたソースのpom.xmlのpropertiesを以下のように変更する。
<properties>
<!-- unidicを指定 -->
<kuromoji.dict.format>unidic</kuromoji.dict.format>
<!-- 特に変更しない -->
<kuromoji.dict.url>http://atilika.com/releases/mecab-ipadic/mecab-ipadic-2.7.0-20070801.tar.gz</kuromoji.dict.url>
<!-- 解凍したUniDicのディレクトリのパスを指定する -->
<kuromoji.dict.dir>dictionary/unidic-mecab1312src</kuromoji.dict.dir>
<!-- utf-8を指定 -->
<kuromoji.dict.encoding>utf-8</kuromoji.dict.encoding>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
Mavenが必要なので入ってない場合はインストールして、以下のコマンドを実行する。
$ mvn compile
$ mvn jar:jar
これでtargetディレクトリ配下にjarが生成される。出来上がったjarにはUniDicが適用されている。
下記URLからkuromoji-solrのjarを落としてくる。
http://atilika.org/confluence/pages/viewpage.action?pageId=131141
Mavenの場合は以下のような感じ。
<repository>
<id>ATILIKA dependencies</id>
<url>http://www.atilika.org/nexus/content/repositories/atilika</url>
</repository>
<dependency>
<groupId>org.atilika.kuromoji</groupId>
<artifactId>kuromoji-solr</artifactId>
<version>0.5.3</version>
<type>jar</type>
</dependency>
下記サイトからlucene-coreのjarも落としてくる。あと、CommonsIOも必要らしい。
http://lucene.apache.org/java/docs/
Kuromojiでは以下のようにTokenizerを引数で渡すことで初期化する。
// せっかくなのでSearchモードに設定してみる
Builder builder = Tokenizer.builder();
builder.mode(Mode.SEARCH);
Tokenizer tokenizer = builder.build();
// AnalyzerにTokenizerを渡して初期化
Analyzer analyzer = new KuromojiAnalyzer(tokenizer);
lucene-gosenでやっているような、動詞の揺れを吸収する(「思った」を「思う」で検索してもヒットする)ような処理は見た限りではやってないっぽかった。でも、luceneのtrunkの中にはKuromojiBaseFormFilterというそれらしきクラスがいたりした。
Searchモードはインデックスのサイズもそれほど肥大化せず、そこそこに救える文字列が増えるので良いと思った。
Solr用のTokenizerもちゃんと用意されている。
とりあえずSolrを落としてきて解凍する。
http://lucene.apache.org/solr/
$ tar xzvf apache-solr-3.5.0.tgz
解凍したフォルダのexample/solrディレクトリに、kuromoji-x.x.x.jarと、kuromoji-solr-x.x.x.jarを入れる。
schema.xmlには以下のように記述する。
<fieldType name="text_kuromoji" class="solr.TextField">
<analyzer>
<tokenizer class="org.atilika.kuromoji.solr.KuromojiTokenizerFactory" mode="search" user-dictionary=""/>
</analyzer>
</fieldType>
modeのところはnormalやextendedが選べるっぽい。またuser-dictionaryにユーザ辞書のパスを指定することもできるようだ。
lucene-gosenも一緒に入れて、JapaneseWidthFilterFactoryとかJapaneseKatakanaStemFilterFactoryとかを併用してみたところ、軽く動かした感じでは想定通りの動きをしてくれていた。
Kuromoji関連のLuceneのIssues
https://issues.apache.org/jira/browse/LUCENE-3305
Kuromojiのgithub
https://github.com/atilika/
Kuromojiを調べてみた | johtaniの日記
http://johtani.jugem.jp/?eid=15