注意)
このエントリでは、mecab-ipadic-neologdをLucene Kuromojiに適用するにあたり、2つほど問題が発生したのですが、作者の@overlastさんにそのうちのひとつを対応いただきました。
修正版mecab-ipadic-neologdを使ってLucene Kuromojiに適用するエントリは、以下を見るようにしてください。
修正されたmecab-ipadic-neologdの辞書を、Lucene Kuromojiに適用してみる
http://d.hatena.ne.jp/Kazuhira/20150316/1426520209
こちらのエントリは、備忘録的に残っているだけです。
以降は、それを踏まえた上で読まれますよう。とりあえずLucene Kuromojiに適用したい場合は、上記のエントリをご覧ください。
先日、ちょっと気になるエントリが世の中に出ていました。
MeCab 用の新語辞書 mecab-ipadic-neologd を公開しました
http://diary.overlasting.net/2015-03-13-1.html
更新が止まって久しいIPA辞書に対して、シードを加えて新辞書を作ったそうです。スゴイ…。
新しいIPA辞書が使えるということは、Luceneで遊んでいる人からみるとKuromojiに適用したくなるものです。
というわけで、やってみました!だいぶ苦労しましたけど…。
各種インストール
この作業を行うために、まず元のサイトからリンクされている手順を見て各種ソフトウェアをインストールしました。
mecab-ipadic-NEologd : Neologism dictionary for MeCab
https://github.com/neologd/mecab-ipadic-neologd/blob/master/README.ja.md
必要なものは、C++コンパイラ、iconv、MeCab、mecab-ipadic、xzだそうです。
うちの環境だと、MeCab以外だとC++コンパイラのみが入っていなかったので、g++をインストール。
$ sudo apt-get install g++
ここから先は、MeCabのインストールです。
MeCabをインストールする
まずは、MeCabをインストールします。
MeCab: Yet Another Part-of-Speech and Morphological Analyzer
http://mecab.googlecode.com/svn/trunk/mecab/doc/index.html
なのですが、このサイトでダウンロードできるmecab-0.996.tar.gzは、tarが壊れているようなので展開できませんでした…。
仕方がないので、ここは以下を参考に、少し前のMeCabをインストール。
Mecabのインストールメモ
http://qiita.com/ShingoOikawa/items/175be8a472ec8ed8a707
今回は、MeCabをシステムグローバルにインストールしたいわけではないので、インストール先を指定します。ここでは、「$MECAB_HOME」と記載します。
$ wget http://mecab.googlecode.com/files/mecab-0.994.tar.gz $ tar -zxvf mecab-0.994.tar.gz $ cd mecab-0.994 $ ./configure --prefix=$MECAB_HOME $ make $ sudo make install
で、インストールされたMeCabにパスを通します。
$ export PATH=$MECAB_HOME/bin:$PATH
確認。
$ mecab --version
mecab of 0.994
続いて、mecab-ipadicのインストール。
$ wget http://mecab.googlecode.com/files/mecab-ipadic-2.7.0-20070801.tar.gz $ tar -zxvf mecab-ipadic-2.7.0-20070801.tar.gz $ cd mecab-ipadic-2.7.0-20070801 $ ./configure --with-charset=utf-8 $ make $ sudo make install
これで、先ほどインストールしたMeCabのディレクトリに、IPA辞書を元にした辞書がインストールされます。
$ ls -l $MECAB_HOME/lib/mecab/dic 合計 4 drwxr-xr-x 2 root root 4096 3月 15 01:42 ipadic
ここまでで、MeCabのインストールは終了です。
mecab-ipadic-neologdのインストール
次は、mecab-ipadic-neologdをインストールします。
こちらは、以下に載っている手順に沿って進めていけばOKです。
mecab-ipadic-NEologd : Neologism dictionary for MeCab
https://github.com/neologd/mecab-ipadic-neologd/blob/master/README.ja.md
$ git clone https://github.com/neologd/mecab-ipadic-neologd.git
$ cd mecab-ipadic-neologd
$ git pull
$ ./bin/install-mecab-ipadic-neologd
スクリプトを実行すると、途中で現在のデフォルトのシステム辞書から、どのように変わるのかが表示されます。
default system dictonary | mecab-ipadic-neologd リアル スコープ | リアルスコープ 世界一 受け たい 授業 | 世界一受けたい授業 め ちゃ イケ | めちゃイケ 学校 の カイダン | 学校のカイダン 志村 動物 園 | 志村 動物園 志村 どう ぶつ 園 | 志村どうぶつ園 アド 街 | アド街 ドクター イエロー | ドクターイエロー 中村 明日美 子 | 中村明日美子 同級生 アニメ 化 | 同級生 アニメ化 ふしぎ 発見 | ふしぎ発見 め ちゃ ギントン | めちゃギントン 学校 の 階段 | 学校の階段
続行してよければ、「yes」で。
[install-mecab-ipadic-neologd] : Do you want to install mecab-ipadic-neologd? Type yes or no. yes
確認。
$ mecab -d $MECAB_HOME/lib/mecab/dic/mecab-ipadic-neologd きゃりーぱみゅぱみゅ きゃりーぱみゅぱみゅ 名詞,固有名詞,一般,*,*,*,きゃりーぱみゅぱみゅ,キャリーパミュパミュ,キャリーパミュパミュ EOS
無事、インストールされたようです。
なお、ここでインストールされたMeCabそのものは、以後は使いません。むしろ、mecab-ipadic-neologdのビルド時にカレントディレクトリに生成される、buildディレクトリの中身の方が必要です。
$ ls -l build 合計 11928 drwxrwxr-x 2 xxxxx xxxxx 4096 3月 15 01:47 mecab-ipadic-2.7.0-20070801-neologd-20150313 -rw-rw-r-- 1 xxxxx xxxxx 12208105 3月 15 01:47 mecab-ipadic-2.7.0-20070801.tar.gz
Luceneのビルド
今度は、話題をLuceneに。
まずはLuceneのソースコードをsvn exportして、ビルドを行います。
$ svn export http://svn.apache.org/repos/asf/lucene/dev/tags/lucene_solr_5_0_0 $ cd lucene_solr_5_0_0/lucene $ ant ivy-bootstrap $ ant compile
「ant ivy-bootstrap」は、すでにAntにIvyが導入済みであれば不要です。
なお、ここでLuceneをエクスポートしたディレクトリ(/path/to/lucene_solr_5_0_0)を、$LUCENE_SRC_HOMEと記載します。
続いて、Kuromojiの使う辞書のビルド。
とりあえず、何も考えずにデフォルトの辞書でビルドしてみます。
$ cd analysis/kuromoji
$ ant regenerate
この時、IPA辞書をダウンロードしてきます。
展開先は、こちらになります。
$ ls -l $LUCENE_SRC_HOME/lucene/build/analysis/kuromoji 合計 53332 drwxrwxr-x 4 xxxxx xxxxx 4096 3月 15 01:56 classes drwxrwxr-x 2 xxxxx xxxxx 4096 3月 15 01:56 mecab-ipadic-2.7.0-20070801 -rw-rw-r-- 1 xxxxx xxxxx 54599680 3月 15 01:56 mecab-ipadic-2.7.0-20070801.tar lrwxrwxrwx 1 xxxxx xxxxx 84 3月 15 01:56 mecab-ipadic-2.7.0-20070801.tar.gz -> /xxxxx/.ivy2/cache/mecab/mecab-ipadic/.tar.gzs/ipadic-2.7.0-20070801..tar.gz
この付近に、mecab-ipadic-neologdのビルド時に作成した辞書の元ネタを置いて、Kuromojiで使う辞書をビルドしてみましょう。
mecab-ipadic-neologdの辞書を使って、Kuromojiの辞書とKuromojiをビルドする
Lucene Kuromojiの辞書作成ツールは、指定されたディレクトリ配下にあるCSVファイル(拡張子が「.csv」)を処理対象とするようです。
ここで、先ほど作成したmecab-ipadic-neologdの中間生成物を、Kuromojiのビルド時のディレクトリにコピーします。
$ cp -Rp [mecab-ipadic-neologdをビルドしたディレクトリ]/build/mecab-ipadic-2.7.0-20070801-neologd-20150313 $LUCENE_SRC_HOME/lucene/build/analysis/kuromoji
そして、Kuromojiのbuild.xmlを修正します。
デフォルトのIPA辞書ではなく、コピーしたmecab-ipadic-neologdの中間生成物を使うように、build.xmlのipadic.versionを修正します(ここが、ディレクトリ名も指すようになっているので)。
<!-- <property name="ipadic.version" value="mecab-ipadic-2.7.0-20070801" /> --> <property name="ipadic.version" value="mecab-ipadic-2.7.0-20070801-neologd-20150313" />
今回使う辞書(というかCSVファイル)はUTF-8で書かれているので、デフォルトのEUC-JPから変更します。
<!-- <property name="dict.encoding" value="euc-jp"/> --> <property name="dict.encoding" value="utf-8"/>
build-dictタスクでは、辞書のダウンロードは不要になるので、dependsからdownload-dictタスクを切り離します。
<!-- <target name="build-dict" depends="compile-tools, download-dict"> --> <target name="build-dict" depends="compile-tools">
辞書作成ツールは、今回のCSVファイルを読ませるとデフォルトのヒープサイズ(1G)では足りなくなるので、拡張します。2Gにしましたが、今回はこれで十分に余裕がありました。
<!-- TODO: optimize the dictionary construction a bit so that you don't need 1G --> <!-- <java fork="true" failonerror="true" maxmemory="1g" classname="org.apache.lucene.analysis.ja.util.DictionaryBuilder"> --> <java fork="true" failonerror="true" maxmemory="2g" classname="org.apache.lucene.analysis.ja.util.DictionaryBuilder">
では、辞書をビルドしてみましょう!
$ ant regenerate
しばらく待っていると、コケます…。
[java] building tokeninfo dict... [java] parse... [java] sort... [java] encode... [java] Exception in thread "main" java.lang.AssertionError [java] at org.apache.lucene.analysis.ja.util.BinaryDictionaryWriter.put(BinaryDictionaryWriter.java:129) [java] at org.apache.lucene.analysis.ja.util.TokenInfoDictionaryBuilder.buildDictionary(TokenInfoDictionaryBuilder.java:143) [java] at org.apache.lucene.analysis.ja.util.TokenInfoDictionaryBuilder.build(TokenInfoDictionaryBuilder.java:78) [java] at org.apache.lucene.analysis.ja.util.DictionaryBuilder.build(DictionaryBuilder.java:37) [java] at org.apache.lucene.analysis.ja.util.DictionaryBuilder.main(DictionaryBuilder.java:82)
コケた場所を見てみます。
ここのassertに引っかかっているようです。
assert baseForm.length() < 16;
BaseFormは、15文字以内とする必要があるようです。これは、Kuromojiの仕様でしょうか?
ここで、MeCabの辞書エントリのフォーマットを見てみます。
単語の追加方法
http://mecab.googlecode.com/svn/trunk/mecab/doc/dic.html
こんなフォーマットです。
表層形,左文脈ID,右文脈ID,コスト,品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型,原形,読み,発音
例は、こんな感じ。
工藤,1223,1223,6058,名詞,固有名詞,人名,名,*,*,くどう,クドウ,クドウ
BaseFormは、10番目の要素なので、「原形」ですね。原形が、15文字を超えてはならない、と。
まあ、MeCabがこの辞書を取り込めていることを考えると、Kuromojiの制限なのでしょう。
この制限の理由や詳細はちゃんと見れていませんが、とりあえずこの制限を外して試してみましょう。
// assert baseForm.length() < 16;
実行。
$ ant regenerate
またコケました…。
[java] building tokeninfo dict... [java] parse... [java] sort... [java] encode... [java] Exception in thread "main" java.lang.AssertionError [java] at org.apache.lucene.analysis.ja.util.BinaryDictionaryWriter.put(BinaryDictionaryWriter.java:122) [java] at org.apache.lucene.analysis.ja.util.TokenInfoDictionaryBuilder.buildDictionary(TokenInfoDictionaryBuilder.java:143) [java] at org.apache.lucene.analysis.ja.util.TokenInfoDictionaryBuilder.build(TokenInfoDictionaryBuilder.java:78) [java] at org.apache.lucene.analysis.ja.util.DictionaryBuilder.build(DictionaryBuilder.java:37) [java] at org.apache.lucene.analysis.ja.util.DictionaryBuilder.main(DictionaryBuilder.java:82)
またコケた箇所を確認してみます。
String existing = posDict.get(leftId); assert existing == null || existing.equals(fullPOSData);
ここ、ソースを読むと、辞書の元ネタのCSVの以下を文字列連結したものと
品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型
以下のペアが異なる組み合わせがあると、発生するようです。
左文脈ID,右文脈ID
例えば、左文脈IDが「1288」である時に、すでに以下の組み合わせが出現しているのに
名詞-固有名詞-一般
同じ左文脈IDに
名詞-固有名詞-人名
が登場したりすると、発生します。
さすがに、これは良くない気がします…。
とりあえず変更した辞書作成ツールのソースコードは、15文字までの制限解除も含めて、元に戻しました。
mecab-ipadic-neologdのシードを補正する
となると、mecab-ipadic-neologd側のシードのCSVファイルを修正した方がよさそうですね。ISSUE書こうかとも思いましたが、Kuromojiの都合なところもある気がするので…。
まず、通常のIPA辞書の内容のCSVから、左文脈ID,右文脈ID,品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型を取り出します。
$ find $LUCENE_SRC_HOME/lucene/build/analysis/kuromoji/mecab-ipadic-2.7.0-20070801-neologd-20150313/*.csv | \ > grep -v 'mecab-user-dict-seed' | \ > xargs cat | \ > perl -wanl -F, -e 'print "$F[1],$F[2],$F[4],$F[5],$F[6],$F[7],$F[8]"' | \ > sort -n | \ > uniq > ipadic-id-with-part-of-speech.csv
mecab-ipadic-neologdのシードは抜いています。
このようなファイルができあがります。
$ head ipadic-id-with-part-of-speech.csv 1,1,その他,間投,*,*,* 2,2,フィラー,*,*,*,* 3,3,感動詞,*,*,*,* 4,4,記号,アルファベット,*,*,* 5,5,記号,一般,*,*,* 6,6,記号,括弧開,*,*,* 7,7,記号,括弧閉,*,*,* 8,8,記号,句点,*,*,* 9,9,記号,空白,*,*,* 10,10,記号,読点,*,*,*
これを基本の情報として、mecab-ipadic-neologdのシードのCSVの左文脈IDと右文脈IDを補正するスクリプトを書きます。なぜかスクリプトは、Groovy。
※そういえば、IDと品詞のどっちに寄せるべきかは考えてなかった…
transform.groovy
transform.groovy def partOfSpeechCsv = args[0] def inputCsv = args[1] def outputCsv = args[2] def maxContext = 1 def partOfSpeechMap = [:] new File(partOfSpeechCsv).eachLine { line -> def tokens = line.split(/,/) def contexts = [tokens[0], tokens[1]] def partOfSpeech = tokens.drop(2).join('-') partOfSpeechMap[partOfSpeech] = contexts if ((tokens[0] as int) > maxContext) { maxContext = tokens[0] as int } } new File(outputCsv).withWriter('UTF-8') { writer -> new File(inputCsv).eachLine('UTF-8') { line -> def tokens = line.split(/,/) if (tokens[10].length() >= 16) { println("[WARN] Discard, BaseForm length greather than 16. => [${tokens[10]}]") return } def contexts = [tokens[1], tokens[2]] def partOfSpeech = "${tokens[4]}-${tokens[5]}-${tokens[6]}-${tokens[7]}-${tokens[8]}" def leftContext def rightContext def ipadicContext = partOfSpeechMap[partOfSpeech] if (ipadicContext == null) { maxContext++ leftContext = maxContext rightContext = maxContext partOfSpeechMap[partOfSpeech] = [leftContext as String, rightContext as String] } else if (ipadicContext != contexts) { leftContext = ipadicContext[0] as int rightContext = ipadicContext[1] as int } else { leftContext = contexts[0] as int rightContext = contexts[1] as int } writer.write(tokens[0]) writer.write(',') writer.write(leftContext as String) writer.write(',') writer.write(rightContext as String) writer.write(',') writer.write(tokens.drop(3).join(',')) writer.newLine() } }
先ほど作成した、ipadic-id-with-part-of-speech.csvというファイルの中に、同じ品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型を持っていて、かつ左文脈ID,右文脈IDがずれていた場合にはIPA辞書と合わせるようにします。未登録の場合は、最大の文脈IDをひとつずつインクリメントした値を付与します(適当)。
また、原形が15文字を超えていた場合は、対象外として破棄します。原形が15文字を超えていた場合は、とりあえず警告するようにしてみましたが…。
あと、CSVを分解する際に気楽にsplitを使っていますが、本当ならKuromojiの中にあるCSVUtil#parseを使用するのが確実だったかもしれません。
では、作成。
$ groovy transform.groovy \ > ipadic-id-with-part-of-speech.csv \ > $LUCENE_SRC_HOME/lucene/build/analysis/kuromoji/mecab-ipadic-2.7.0-20070801-neologd-20150313/mecab-user-dict-seed.20150313.csv \ > $LUCENE_SRC_HOME/lucene/build/analysis/kuromoji/mecab-ipadic-2.7.0-20070801-neologd-20150313/mecab-user-dict-seed.20150313_revised.csv
なお、原形が15文字を超えるものは、66707個ありました…。
これで、$LUCENE_SRC_HOME/lucene/build/analysis/kuromoji/mecab-ipadic-2.7.0-20070801-neologd-20150313ディレクトリ配下に、mecab-user-dict-seed.20150313_revised.csvという名前のファイルができます。
元のシードのCSVは、いらなくなったので削除します。
$ rm $LUCENE_SRC_HOME/lucene/build/analysis/kuromoji/mecab-ipadic-2.7.0-20070801-neologd-20150313/mecab-user-dict-seed.20150313.csv
今渡こそ、気を取り直して実行!
$ ant regenerate
うちのPCで3分ほどかかりましたが、今度はうまくいったようです。
regenerate: BUILD SUCCESSFUL Total time: 3 minutes 17 seconds
最後に、Kuromojiをビルドします。
$ ant jar-core
今回の辞書を使った、Lucene KuromojiのJARファイルができあがります。
jar-core: [jar] Building jar: $LUCENE_SRC_HOME/lucene/build/analysis/kuromoji/lucene-analyzers-kuromoji-5.0.0-SNAPSHOT.jar BUILD SUCCESSFUL Total time: 5 seconds
こちらを使って、動作確認してみましょう。
Kuromojiを使ったプログラムを書く
それでは、まずは普通にKuromojiを使ったプログラムを書いてみましょう。
ビルドツールがsbtなのは、ご愛嬌。
build.sbt
name := "lucene-kuromoji-mecab-neologd" version := "0.0.1-SNAPSHOT" scalaVersion := "2.11.5" organization := "org.littlewings" updateOptions := updateOptions.value.withCachedResolution(true) scalacOptions ++= Seq("-Xlint", "-unchecked", "-deprecation", "-feature") libraryDependencies ++= Seq( "org.apache.lucene" % "lucene-core" % "5.0.0", "org.apache.lucene" % "lucene-analyzers-common" % "5.0.0", "org.apache.lucene" % "lucene-analyzers-kuromoji" % "5.0.0" )
Luceneは、5.0.0です。
ソースコードがScalaなのも、ご愛嬌。
src/main/scala/org/littlewings/lucene/kuromoji/KuromojiWithNeologd.scala
package org.littlewings.lucene.kuromoji import org.apache.lucene.analysis.ja.JapaneseAnalyzer import org.apache.lucene.analysis.tokenattributes.CharTermAttribute object KuromojiWithNeologd { def main(args: Array[String]): Unit = { val texts = Array( "すもももももももものうち", "きゃりーぱみゅぱみゅ", "日本経済新聞でモバゲーの記事を読んだ", "くりーむしちゅー", "艦隊これくしょん" ) val analyzer = new JapaneseAnalyzer for (text <- texts) { val tokenStream = analyzer.tokenStream("", text) val charTermAttr = tokenStream.addAttribute(classOf[CharTermAttribute]) tokenStream.reset() val tokens = Iterator .continually(tokenStream.incrementToken()) .takeWhile(identity) .map(_ => charTermAttr.toString) println(s"InputText = $text") println(s" Tokenized = ${tokens.mkString("[", ", ", "]")}") tokenStream.close() } } }
形態素解析する文章や単語は、適当に選んでいます。Kuromojiは、デフォルトのSEARCHモードです。
このプログラムを実行してみます。
> run [info] Running org.littlewings.lucene.kuromoji.KuromojiWithNeologd InputText = すもももももももものうち Tokenized = [すもも, もも, もも] InputText = きゃりーぱみゅぱみゅ Tokenized = [く, ー, ぱみゅぱみゅ] InputText = 日本経済新聞でモバゲーの記事を読んだ Tokenized = [日本, 日本経済新聞, 経済, 新聞, モバゲ, 記事, 読む] InputText = くりーむしちゅー Tokenized = [くり, ー, むし, ちる, ゅ, ー] InputText = 艦隊これくしょん Tokenized = [艦隊, くい] [success] Total time: 1 s, completed 2015/03/15 2:57:30
当然、新しい単語がわからないので、ものすごい結果になります。
では、ここで先ほど辞書を使ってビルドした、KuromojiのJARファイルを使ってみます。
1度sbtを終了。
> exit
libディレクトリを作成します。
$ mkdir lib
この中に、ビルドしたJARファイルを放り込みます。
$ cp $LUCENE_SRC_HOME/lucene/build/analysis/kuromoji/lucene-analyzers-kuromoji-5.0.0-SNAPSHOT.jar lib/
sbtの依存関係定義から、Kuromojiを外します。
libraryDependencies ++= Seq( "org.apache.lucene" % "lucene-core" % "5.0.0", "org.apache.lucene" % "lucene-analyzers-common" % "5.0.0" // "org.apache.lucene" % "lucene-analyzers-kuromoji" % "5.0.0" )
では、再度sbtを起動して、実行。
> run [info] Running org.littlewings.lucene.kuromoji.KuromojiWithNeologd InputText = すもももももももものうち Tokenized = [すもももももも, すもももももももものうち, もも] InputText = きゃりーぱみゅぱみゅ Tokenized = [きゃりーぱみゅぱみゅ] InputText = 日本経済新聞でモバゲーの記事を読んだ Tokenized = [日本, 日本経済新聞, 経済, 新聞, mobage, 記事, 読む] InputText = くりーむしちゅー Tokenized = [くりーむしちゅー] InputText = 艦隊これくしょん Tokenized = [艦隊これくしょん] [success] Total time: 1 s, completed 2015/03/15 3:02:09
結果が大きく変わりました!きゃりーぱみゅぱみゅとかも単語として認識していますね!モバゲーが、mobageに…。
ところで、「すもももももももものうち」の結果が、妙なことになりました。
InputText = すもももももももものうち Tokenized = [すもももももも, すもももももももものうち, もも]
これはどうしたことでしょう?と思い、シードのCSVを見てみると
$ view [mecab-ipadic-neologdをビルドしたディレクトリ]/build/mecab-ipadic-2.7.0-20070801-neologd-20150313/mecab-user-dict-seed.20150313.csv
以下の定義が…。
すももももも,1288,1288,5072,名詞,固有名詞,一般,*,*,*,すももももも,スモモモモモ,スモモモモモ すもももももも,1288,1288,4587,名詞,固有名詞,一般,*,*,*,すもももももも,スモモモモモモ,スモモモモモモ すもももももも〜地上最強のヨメ〜,1288,1288,3763,名詞,固有名詞,一般,*,*,*,すもももももも〜地上最強のヨメ〜,スモモモモモモチジョウサイキョウノヨメ,スモモモモモモチジョ>ウサイキョウノヨメ
これに引っかかったのか…。
さらに、こういうエントリまで…。
すもももももももものうち,1288,1288,4143,名詞,固有名詞,一般,*,*,*,すもももももももものうち,スモモモモモモモモノウチ,スモモモモモモモモノウチ
これ、名詞なの??
まあ、とりあえず動かせたのでよしとするかな…。
終わりに
なんとか動かせるところまではいきましたが、LuceneのKuromojiで使うにはそれなりに苦労しました。
左文脈IDと右文脈IDに対して品詞の組み合わせがずれているのはまだしも、原形が15文字以内という制限は知らなかったですね。あとでKuromojiのソースを確認してみるかも。
あと、今回は取り込み時に15文字よりも原形が長い場合は破棄しましたが、ちゃんと見た方がいいのかなと。splitの仕方も積み残し的な感じですね。
だいぶ歪んだ形になったかもですが、目標は達成できたのでこれでおしまい、と。
今回作成したソースコードとスクリプトは、こちらに置いています。
https://github.com/kazuhira-r/lucene-examples/tree/master/lucene-kuromoji-mecab-neologd
それにしても、これどうやって辞書を作ってるのかに興味がありますね。