CLOVER🍀

That was when it all began.

Kuromoji(Atilika)0.9-SNAPSHOTに、NEologd(ipadic、unidic)を適用してみた話

Lucene Kuromojiとの組み合わせで、時々エントリを書いていますmecab-ipadic-NEologdですが、以前Kuromoji(Atilika)との組み合わせでは失敗したことがあります。

Kuromoji(Atilika)に、mecab-ipadic-neologdの辞書を適用できない?という話
http://d.hatena.ne.jp/Kazuhira/20150318/1426690374

Kuromoji
http://www.atilika.org/

Kuromoji(GitHub)
https://github.com/atilika/kuromoji

で、このAtilikaのKuromojiなのですが、最近更新が活発なようで、masterブランチは0.9系になっています。

AtilikaのMavenリポジトリには、相変わらず0.7.7しかないのですが。

http://www.atilika.org/nexus/content/repositories/atilika/org/atilika/kuromoji/kuromoji/

0.8系はどうしましたか?

で、0.9系だとKuromojiのモジュール構成も変わっていて更新も頻繁に行われているので、今NEologdを適用すると結果も変わるのかな?と思い、再度チャレンジしてみました。

結果からいくと、適用できました。ちょっと辞書を修正すれば。

対象は、IPA辞書とUniDicとします。

最初の準備として、KuromojiのソースコードをGitHubからCloneしておきましょう。

$ git clone https://github.com/atilika/kuromoji.git

あとは、それぞれに対してNEologdを組み込んでビルドしてみます。

Kuromoji 0.9 × mecab-ipadic-NEologdでビルドする

まずは、Kuromojiとmecab-ipadic-NEologdの組み合わせから。

mecab-ipadic-NEologdをCloneしてきて、CSV辞書を作成します。

$ git clone https://github.com/neologd/mecab-ipadic-neologd.git
$ cd mecab-ipadic-neologd
$ libexec/make-mecab-ipadic-neologd.sh

今回、mecab-ipadic-NEologdの辞書の日付は「20150810」でした。

先ほどCloneしたKuromojiのリポジトリへ移動し

$ cd /path/to/kuromoji

ディレクトリを作成して、CSV辞書を放り込みます。

$ mkdir kuromoji-ipadic/dictionary
$ cp -R /path/to/mecab-ipadic-neologd/build/mecab-ipadic-2.7.0-20070801-neologd-20150810 kuromoji-ipadic/dictionary

対象のプロジェクトは、「kuromoji-ipadic」です。

ビルドを行う前に、コンパイル時のヒープを広げておきます。

$ export MAVEN_OPTS='-Xmx4g'

前は2Gでよかったのですが、今は3Gでも足りませんでした。NEologdが成長していますね。

で、ビルド。

$ mvn -pl kuromoji-ipadic -am package \
 -DskipTests=true \
 -DskipDownloadDictionary=true \
 -Dkuromoji.dict.dir=kuromoji-ipadic/dictionary/mecab-ipadic-2.7.0-20070801-neologd-20150810 \
 -Dkuromoji.dict.encoding=utf-8

追記)
20150810日付以前のNEologdでは、Kuromojiに組み込む際に失敗しますが、20150817以降であれば修正いただいたので大丈夫です。

対応いただいた、@overlastさん、ありがとうございました!

追記、ここまで)

失敗…。

java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:297)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.RuntimeException: Unmatched quote in entry: ANIME FES.“VS”,1288,1288,5229,名詞,固有名詞,一般,*,*,*,ANIME FES.“VS",アニメフェスバーサス,アニメフェスバーサス
	at com.atilika.kuromoji.util.DictionaryEntryLineParser.parseLine(DictionaryEntryLineParser.java:64)
	at com.atilika.kuromoji.ipadic.compile.TokenInfoDictionaryCompiler.parse(TokenInfoDictionaryCompiler.java:35)
	at com.atilika.kuromoji.ipadic.compile.TokenInfoDictionaryCompiler.parse(TokenInfoDictionaryCompiler.java:27)
	at com.atilika.kuromoji.compile.AbstractTokenInfoDictionaryCompiler.analyzeTokenInfo(AbstractTokenInfoDictionaryCompiler.java:64)
	at com.atilika.kuromoji.compile.AbstractDictionaryCompiler.buildTokenInfoDictionary(AbstractDictionaryCompiler.java:48)
	at com.atilika.kuromoji.compile.AbstractDictionaryCompiler.build(AbstractDictionaryCompiler.java:38)
	at com.atilika.kuromoji.compile.AbstractDictionaryCompiler.build(AbstractDictionaryCompiler.java:163)
	at com.atilika.kuromoji.ipadic.compile.DictionaryCompiler.main(DictionaryCompiler.java:33)
	... 6 more

なんか、CSVに「"」(二重引用符)が半端に入っているような…。

ANIME FES.“VS"

仕方ないので、ここは「"」を「”」に変換しておきます。
※繰り返しになりますが、この手順はNEologdが20150817以降であれば不要です

$ perl -wpi -e 's!"!”!g' kuromoji-ipadic/dictionary/mecab-ipadic-2.7.0-20070801-neologd-20150810/*.csv

再度、ビルド。

$ mvn -pl kuromoji-ipadic -am package \
 -DskipTests=true \
 -DskipDownloadDictionary=true \
 -Dkuromoji.dict.dir=kuromoji-ipadic/dictionary/mecab-ipadic-2.7.0-20070801-neologd-20150810 \
 -Dkuromoji.dict.encoding=utf-8

今度は成功しました。

[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ kuromoji-ipadic ---
[INFO] Building jar: /path/to/kuromoji/kuromoji-ipadic/target/kuromoji-ipadic-0.9-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] Kuromoji ........................................... SUCCESS [  0.003 s]
[INFO] Kuromoji Common .................................... SUCCESS [  1.241 s]
[INFO] Kuromoji IPADIC .................................... SUCCESS [07:20 min]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 07:21 min
[INFO] Finished at: 2015-08-14T00:01:09+09:00
[INFO] Final Memory: 36M/3309M
[INFO] ------------------------------------------------------------------------

やりましたね!前回はKuromojiのソースコードに細工をしないと、ビルドできなかったのに。

ビルド所要時間、7分ちょっと…。うちの環境でビルドしている時の比較ですが、Lucene Kuromojiの3倍以上かかってる感じです。

動作確認(Kuromoji × mecab-ipadic-NEologd)

それでは、動作確認してみましょう。動作確認用のプログラムは、このようなものを用意。
sample_ipadic_neologd.groovy

import com.atilika.kuromoji.ipadic.Tokenizer

def texts = ['すもももももももものうち',
             'きゃりーぱみゅぱみゅは、2012年に「つけまつける」でデビュー!',
             '日本経済新聞でモバゲーの記事を読んだ',
             'くりぃむしちゅーは、上田晋也と有田哲平の2人からなる日本のお笑いコンビ',
             '艦隊これくしょんは、角川ゲームスが開発し、DMM.comが配信しているブラウザゲーム']

def tokenizer = new Tokenizer()

texts.each { text ->
  println("$text")
  println('   [' + tokenizer.tokenize(text).collect { it.surfaceForm }.join(', ') + ']')
}

Tokenizerのパッケージ名、辞書の名前が入るようになったんですね…。

import com.atilika.kuromoji.ipadic.Tokenizer

で、動かした結果。まずは、通常のIPA辞書のもので確認(別途ビルドしました)。

$ groovy -cp kuromoji/kuromoji-common/target/kuromoji-common-0.9-SNAPSHOT.jar:kuromoji/kuromoji-ipadic/target/kuromoji-ipadic-0.9-SNAPSHOT.jar sample_ipadic_neologd.groovy
すもももももももものうち
   [すもも, も, もも, も, もも, の, うち]
きゃりーぱみゅぱみゅは、2012年に「つけまつける」でデビュー!
   [きゃ, り, ー, ぱみゅぱみゅは, 、, 2012, 年, に, 「, つけ, ま, つける, 」, で, デビュー, !]
日本経済新聞でモバゲーの記事を読んだ
   [日本経済新聞, で, モバゲー, の, 記事, を, 読ん, だ]
くりぃむしちゅーは、上田晋也と有田哲平の2人からなる日本のお笑いコンビ
   [くり, ぃむしちゅ, ー, は, 、, 上田, 晋, 也, と, 有田, 哲, 平, の, 2, 人, から, なる, 日本, の, お笑い, コンビ]
艦隊これくしょんは、角川ゲームスが開発し、DMM.comが配信しているブラウザゲーム
   [艦隊, これ, くし, ょんは, 、, 角川, ゲームス, が, 開発, し, 、, DMM, ., com, が, 配信, し, て, いる, ブラウザゲーム]

続いて、mecab-ipadic-NEologd版。
※起動がちょっと重め

$ groovy -cp kuromoji/kuromoji-common/target/kuromoji-common-0.9-SNAPSHOT.jar:kuromoji/kuromoji-ipadic/target/kuromoji-ipadic-0.9-SNAPSHOT.jar sample_ipadic_neologd.groovy
すもももももももものうち
   [すもももももももものうち]
きゃりーぱみゅぱみゅは、2012年に「つけまつける」でデビュー!
   [きゃりーぱみゅぱみゅ, は, 、, 2012年, に, 「, つけまつける, 」, で, デビュー, !]
日本経済新聞でモバゲーの記事を読んだ
   [日本経済新聞, で, モバゲー, の, 記事, を, 読ん, だ]
くりぃむしちゅーは、上田晋也と有田哲平の2人からなる日本のお笑いコンビ
   [くりぃむしちゅー, は, 、, 上田, 晋也, と, 有田哲平, の, 2, 人, から, なる, 日本, の, お笑いコンビ]
艦隊これくしょんは、角川ゲームスが開発し、DMM.comが配信しているブラウザゲーム
   [艦隊これくしょん, は, 、, 角川ゲームス, が, 開発, し, 、, DMM.com, が, 配信, し, て, いる, ブラウザゲーム]

形態素解析結果が、大きく変わりましたね。OKそうです。

Kuromoji 0.9 × mecab-unidic-NEologdでビルドする

続いて、mecab-unidic-NEologdを使ってビルドしてみます。

なお、UniDicですが、Lucene版のKuromojiでは実はサポートしていません。こちらが使えるのは、Atilika Kuromojiのアドバンテージな気もします。

mecab-ipadic-NEologdの時と同じように、mecab-unidic-NEologdをCloneしてきてビルドします。

$ git clone https://github.com/neologd/mecab-unidic-neologd.git
$ cd mecab-unidic-neologd
$ libexec/make-mecab-unidic-neologd.sh

mecab-ipadic-NEologdとほぼ同じ手順です。

Kuromojiのディレクトリへ移り、

$ cd /path/to/kuromoji

ヒープを広げて

$ export MAVEN_OPTS='-Xmx4g'

ディレクトリを作成し、作成したCSV辞書を放り込みます。

$ mkdir kuromoji-unidic/dictionary
$ cp -R /path/to/mecab-unidic-neologd/build/unidic-mecab-2.1.2_src-neologd-20150810 kuromoji-unidic/dictionary

今回の対象は、「kuromoji-unidic」です。

で、ビルド。

$ mvn -pl kuromoji-unidic -am package \
 -DskipTests=true \
 -DskipDownloadDictionary=true \
 -Dkuromoji.dict.dir=kuromoji-unidic/dictionary/unidic-mecab-2.1.2_src-neologd-20150810

こちらも、コケてしまいました…。mecab-ipadic-NElogdの時と、同じ理由で。

java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:297)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.RuntimeException: Unmatched quote in entry: ANIME FES.“VS”,1288,1288,3861,名詞,固有名詞,一般,*,*,*,アニメフェスバーサス,ANIME FES.“VS",ANIME FES.“VS",アニメフェスバーサス,ANIME FES.“VS",アニメフェスバーサス,固,*,*,*,*
	at com.atilika.kuromoji.util.DictionaryEntryLineParser.parseLine(DictionaryEntryLineParser.java:64)
	at com.atilika.kuromoji.unidic.compile.TokenInfoDictionaryCompiler.parse(TokenInfoDictionaryCompiler.java:36)
	at com.atilika.kuromoji.unidic.compile.TokenInfoDictionaryCompiler.parse(TokenInfoDictionaryCompiler.java:28)
	at com.atilika.kuromoji.compile.AbstractTokenInfoDictionaryCompiler.analyzeTokenInfo(AbstractTokenInfoDictionaryCompiler.java:64)
	at com.atilika.kuromoji.compile.AbstractDictionaryCompiler.buildTokenInfoDictionary(AbstractDictionaryCompiler.java:48)
	at com.atilika.kuromoji.compile.AbstractDictionaryCompiler.build(AbstractDictionaryCompiler.java:38)
	at com.atilika.kuromoji.compile.AbstractDictionaryCompiler.build(AbstractDictionaryCompiler.java:163)
	at com.atilika.kuromoji.unidic.compile.DictionaryCompiler.main(DictionaryCompiler.java:34)
	... 6 more

とりあえず、こちらも修正。

$ perl -wpi -e 's!"!”!g' kuromoji-unidic/dictionary/unidic-mecab-2.1.2_src-neologd-20150810/*.csv

リトライ。

$ mvn -pl kuromoji-unidic -am package \
 -DskipTests=true \
 -DskipDownloadDictionary=true \
 -Dkuromoji.dict.dir=kuromoji-unidic/dictionary/unidic-mecab-2.1.2_src-neologd-20150810

で、またコケます…。

[WARNING] 
java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at org.codehaus.mojo.exec.ExecJavaMojo$1.run(ExecJavaMojo.java:297)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NumberFormatException: Value out of range. Value:"-38377" Radix:10
	at java.lang.Short.parseShort(Short.java:120)
	at java.lang.Short.parseShort(Short.java:144)
	at com.atilika.kuromoji.unidic.dict.DictionaryEntry.<init>(DictionaryEntry.java:54)
	at com.atilika.kuromoji.unidic.compile.TokenInfoDictionaryCompiler.parse(TokenInfoDictionaryCompiler.java:37)
	at com.atilika.kuromoji.unidic.compile.TokenInfoDictionaryCompiler.parse(TokenInfoDictionaryCompiler.java:28)
	at com.atilika.kuromoji.compile.AbstractTokenInfoDictionaryCompiler.analyzeTokenInfo(AbstractTokenInfoDictionaryCompiler.java:64)
	at com.atilika.kuromoji.compile.AbstractDictionaryCompiler.buildTokenInfoDictionary(AbstractDictionaryCompiler.java:48)
	at com.atilika.kuromoji.compile.AbstractDictionaryCompiler.build(AbstractDictionaryCompiler.java:38)
	at com.atilika.kuromoji.compile.AbstractDictionaryCompiler.build(AbstractDictionaryCompiler.java:163)
	at com.atilika.kuromoji.unidic.compile.DictionaryCompiler.main(DictionaryCompiler.java:34)
	... 6 more

今回は、事情が違うようです。

Kuromojiはコストをshortの範囲で表現しなくてはならないようで、今回はshortの下限を下回っているエントリが存在しています。

$ grep -E ',\-38377' kuromoji-unidic/dictionary/unidic-mecab-2.1.2_src-neologd-20150810/*.csv
kuromoji-unidic/dictionary/unidic-mecab-2.1.2_src-neologd-20150810/mecab-unidic-user-dict-seed.20150810.csv:この歌を・・・・・・・・♪,1288,1288,-38377,名詞,固有名詞,一般,*,*,*,コノウタヲ,この歌を・・・・・・・・♪,この歌を・・・・・・・・♪,コノウタヲ,この歌を・・・・・・・・♪,コノウタヲ,固,*,*,*,*

Lucene Kuromojiでmecab-ipadic-NEologdと組み合わせた時も、絵文字のコストがshortの範囲を越えてビルドに失敗したことがあったなぁ…。

仕方がないので、今回はコストをshortの最小値に設定。

-32768

気を取り直して、再度ビルド。

$ mvn -pl kuromoji-unidic -am package \
 -DskipTests=true \
 -DskipDownloadDictionary=true \
 -Dkuromoji.dict.dir=kuromoji-unidic/dictionary/unidic-mecab-2.1.2_src-neologd-20150810

今度は、OKでした!

[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ kuromoji-unidic ---
[INFO] Building jar: /path/to/kuromoji/kuromoji-unidic/target/kuromoji-unidic-0.9-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] Kuromoji ........................................... SUCCESS [  0.002 s]
[INFO] Kuromoji Common .................................... SUCCESS [  1.072 s]
[INFO] Kuromoji UniDic .................................... SUCCESS [10:14 min]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10:15 min
[INFO] Finished at: 2015-08-14T00:31:27+09:00
[INFO] Final Memory: 32M/3399M
[INFO] ------------------------------------------------------------------------

ビルドに、約10分…。

動作確認(Kuromoji × mecab-unidic-NEologd)

ビルドしたKuromoji+mecab-unidic-NEologdで、動作確認。使ったプログラムは、こちら。
sample_unidic_neologd.groovy

import com.atilika.kuromoji.unidic.Tokenizer

def texts = ['すもももももももものうち',
             'きゃりーぱみゅぱみゅは、2012年に「つけまつける」でデビュー!',
             '日本経済新聞でモバゲーの記事を読んだ',
             'くりぃむしちゅーは、上田晋也と有田哲平の2人からなる日本のお笑いコンビ',
             '艦隊これくしょんは、角川ゲームスが開発し、DMM.comが配信しているブラウザゲーム']

def tokenizer = new Tokenizer()

texts.each { text ->
  println("$text")
  println('   [' + tokenizer.tokenize(text).collect { it.surfaceForm }.join(', ') + ']')
}

UniDicの場合のTokenizerのパッケージは、こちら。

import com.atilika.kuromoji.unidic.Tokenizer

で、動かした結果。こちらも、まずは通常のUniDicのもので確認(やっぱり、別途ビルドしました)。

$ groovy -cp kuromoji/kuromoji-common/target/kuromoji-common-0.9-SNAPSHOT.jar:kuromoji/kuromoji-unidic/target/kuromoji-unidic-0.9-SNAPSHOT.jar sample_unidic_neologd.groovy 
すもももももももものうち
   [すもも, も, もも, も, もも, の, うち]
きゃりーぱみゅぱみゅは、2012年に「つけまつける」でデビュー!
   [きゃ, り, ー, ぱ, み, ゅ, ぱ, み, ゅ, は, 、, 2, 0, 1, 2, 年, に, 「, つけ, まつ, ける, 」, で, デビュー, !]
日本経済新聞でモバゲーの記事を読んだ
   [日本, 経済, 新聞, で, モバゲー, の, 記事, を, 読ん, だ]
くりぃむしちゅーは、上田晋也と有田哲平の2人からなる日本のお笑いコンビ
   [くりぃむ, しちゅー, は, 、, 上田, 晋也, と, 有田, 哲平, の, 2, 人, から, なる, 日本, の, お, 笑い, コンビ]
艦隊これくしょんは、角川ゲームスが開発し、DMM.comが配信しているブラウザゲーム
   [艦隊, これ, く, しょ, ん, は, 、, 角川, ゲーム, ス, が, 開発, し, 、, D, M, M, ., c, o, m, が, 配信, し, て, いる, ブラウザ, ゲーム]

続いて、mecab-unidic-NEologd版。
※起動がちょっと重め

$ groovy -cp kuromoji/kuromoji-common/target/kuromoji-common-0.9-SNAPSHOT.jar:kuromoji/kuromoji-unidic/target/kuromoji-unidic-0.9-SNAPSHOT.jar sample_unidic_neologd.groovy
すもももももももものうち
   [すもももももももものうち]
きゃりーぱみゅぱみゅは、2012年に「つけまつける」でデビュー!
   [きゃりーぱみゅぱみゅ, は, 、, 2, 0, 1, 2, 年, に, 「, つけまつける, 」, で, デビュー, !]
日本経済新聞でモバゲーの記事を読んだ
   [日本経済新聞, で, モバゲー, の, 記事, を, 読ん, だ]
くりぃむしちゅーは、上田晋也と有田哲平の2人からなる日本のお笑いコンビ
   [くりぃむしちゅー, は, 、, 上田晋也, と, 有田哲平, の, 2, 人, から, なる, 日本, の, お笑いコンビ]
艦隊これくしょんは、角川ゲームスが開発し、DMM.comが配信しているブラウザゲーム
   [艦隊これくしょん, は, 、, 角川ゲームス, が, 開発, し, 、, DMM.com, が, 配信, し, て, いる, ブラウザゲーム]

こちらもOKそうです。

NEologdとは関係なさそうですが、UniDicだと2012年がバラバラにされるんですね。

2, 0, 1, 2, å¹´

まとめ

Atilika Kuromojiに対して、mecab-ipadic-NEologdとmecab-unidic-NEologdのそれぞれを組み込む手順を示しました。

一部、辞書を修正しちゃいましたが…。UniDicのコストの話(shortの下限を下回る)はさておき、二重引用符の件はなんでしょう?これでハマった人、見たことないような。後でちょっと追ってみようかな…。

通常、自分はLucene Kuromoji+mecab-ipadic-NEologdを使って試していますが(Lucene版KuromojiはUniDicに対応してないし)、だいたいの手順はできたのでKuromoji(Atilika)が0.9のリリースタグでもつけられる状態になってくれれば、おおよそビルドの自動化ができる気がします。

Lucene版に比べると、Kuromoji(Atilika)のアドバンテージはこういうところでしょうか。

  • モジュールのバリエーションが多い(kuromoji/kuromoji-ipadic、kuromoji-jumandic、kuromoji-naist-jdic、kuromoji-unidic、kuromoji-unidic-kanaaccent) ※NEologd視点では、現時点ではipadicとunidicですが
  • 原型の15文字制限がない
  • LuceneのCoreがくっついてこない

Lucene Kuromojiの場合、原型が15文字を超えることを許さないんですよね…。

気になるところは、今のところビルドが遅くてメモリも大量に消費する、というところですかね。辞書を修正してしまった点は、また別の話だと思います。

とりあえず、こんなところで。