MeCab互換の形態素解析器 Igo でグロンギ語変換(+ そこから、始まる Igo のデバッグ)
この前のLTで @jewel_x12 さんがDonauってサービスを発表してくれました。内容も面白かったんだけど、Igo-Rubyってのを使ってて「Pure Rubyでこんなことができるんだ!」と関心した。よくよく調べてみると Igo と言うのは元は Java で書かれたもので、構造が比較的簡単なのでいろんな言語にも移植されているようです。最近すっかり Pythonista になってしまったので、Python移植が無いものかと調べてみたらちゃんとありました(igo-python)。
GAEなどPure Pythonでないと動かない環境でも使えるので、これは便利そうです。
この Igo さん、どうやら MeCab と互換性があるらしく、MeCabで使っている辞書がそのまま使えます。
・・・ということは、この前作ったグロンギ語変換辞書もそのまま使える・・・!
ということでやってみた。MeCabとインターフェースはほぼ一緒。違うのは、Taggerの引数が辞書の場所、parseがノードのリストを返す、strにエンコードする必要なくunicodeをそのまま入れられるってとこくらい(元のMeCabのソースを流用したためにエンコード後の文字列を突っ込んでしばらく「動かねー!」って言ってたのはないしょ)。
で、動かしてみたところ
[原文] 殺してやる! [グロンギ語訳] ボソギデジャス! [再翻訳] のろってやる! [原文] 命拾いしたな [グロンギ語訳] ギボヂヂソギギダバ [再翻訳]命拾い下か [原文] これはクウガのベルト [グロンギ語訳] ボセパクウガンデスド [再翻訳] これ羽クウガのてると
やったーできたー!
・・・あれ・・・?なんか・・・再翻訳結果が・・・ヘン・・・
「のろってやる!」はいいとして(同音異義語がたくさんあるから解析器が違えば結果が違うのは仕方がない)、他は文法的にも明らかにおかしい。自分がつまらないミスをしたんじゃないかとソースを見渡したけど、どこにもおかしいところはない。そもそもIgo単体で使っても動作が怪しい。
そういうわけで、Igo の trunk に上がっているコードを読んでみました。すると・・・
28 public short linkCost(int leftId, int rightId) { 29 return matrix[rightId*rightSize + leftId]; 30 }
118 int minCost = f.cost + mtx.linkCost(f.rightId, vn.leftId);
お気づきいただけるだろうか・・・。メソッドの定義は(leftId, rightId)になっているのに、呼び出し側は(rightId, leftId)で呼び出していることに・・・。
キャアァアアァァ!
どうやら作者の中でrightIdとleftIdの区別が曖昧になっているようです。[rightId*rightSize + leftId]ってところもなんだかおかしいし。
そしてさらに恐ろしいのは、IgoのJava以外への移植版も同じ間違いをしているということ。全部を確認したわけではないけどね。少なくともPython版とRuby版は間違えている。皆さん特に意味を考えずに移植いちゃったんですね。今の今まで修正されてなかったということは、他の言語への移植でも間違えている可能性大なのではないかと。
JavaとPythonについてはPatchを上げておきました。(https://gist.github.com/1908441)
いろいろと逆になっていたので、MeCabの辞書と同じ順番にしておいた。辞書の互換性はなくなるけど、そもそも今までの辞書は何かがおかしいから別にいいよね。
さあ、修正してテスト
[原文] 殺してやる! [グロンギ語訳] ボソギデジャス! [再翻訳] 殺してやる! [原文] 命拾いしたな [グロンギ語訳] ギボヂヂソギギダバ [再翻訳] 命拾いしたか [原文] これはクウガのベルト [グロンギ語訳] ボセパクウガンデスド [再翻訳] これはクウガのベルト
MeCabでやった時の結果と一致。やったー!
これをGAEにでも上げて、変換APIでもつくろうかと思ったけど、Igo のデバッグに疲れたので今日はここまで。
それにしてもなぜ今まで誰もIgoのバグに気が付かなったんだ・・・なぜ正常に解析できていた・・・漢字混じりだとパスがそこまで多くないからな。
グロンギ語変換は同音異義語が多いからパスが大量に生成されるからね。
今回の件は致命的な気がするので、作者にも報告しておきたいと思います。