MeCabのN-Best解を出力する

今回もMeCabの話です。MeCabにはN-Best解という機能がありますので、その紹介と、コマンドラインおよびPythonライブラリで利用する方法をまとめます。

公式ドキュメントではこちらですね。
参考: N-Best 解の出力

この間の最小コスト法の記事で、MeCabの仕組みを紹介しましたがざっくりいうと、生起コストと連接コストの総和が最小になるような分解方法を見つけてそれを返すというものでした。
そして、コストの総和が最小になるものがあれば、2番目に小さいもの、3番目に小さいもの、4番目に小さいもの、がそれぞれ存在します。それらをN個出力してくれるというシンプルな機能です。

まずコマンドラインの場合、使い方は非常に簡単で 引数に -N {数値} を追加するだけです。
いつもの「すもももももももものうち」で 3つ出してみましょう。
以下の通り、3パターンの解析結果が出力されました。

$ mecab -N 3
すもももももももものうち
すもも	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
うち	名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS
すもも	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
うち	名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS
すもも	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
の	助詞,連体化,*,*,*,*,の,ノ,ノ
うち	名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS

ドキュメントにありますが、 -N の最大値は 512だそうです。513以上を設定すると「invalid N value」というメッセージが返ってきます。

$ mecab -N 513
invalid N value

-N 512 とすると必ず512通り返ってくるというわけではなく、分解のパターンが指定した数より少なければ見つかった分しか返ってきません。文章が短いとそうなりがちですね。
たとえば、次のように1単語の場合はNがなんであっても1つしか結果は得られません。

$ mecab -N 512
岐阜
岐阜	名詞,固有名詞,地域,一般,*,*,岐阜,ギフ,ギフ
EOS

さて、コマンドラインで使う方法がわかったので、次はPythonでやってみましょう。

僕はてっきり、Tagger オブジェクトを生成するときに引数の文字列の中に -N {数値} を含めておけばいいんだと勘違いしていました。しかしそれでは動作しません。
「すももももも」でやってみます。

import MeCab


tagger = MeCab.Tagger("-N 512")
print(tagger.parse("すももももも"))
"""
すもも	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
EOS
"""

エラー等は出ませんが、解は1個しか得られませんでしたね。

よくよく調べてみると、Taggerオブジェクトが、parseNBest というメソッドを持っており、これを使わないといけないようです。 また、 Taggerオブジェクトを生成する時点では、 -N の指定は不要でした。(指定しても動くが、出力に全く影響しません。)

parseNBest メソッドには、N-Best会を表示したい個数の数値と、解析したいテキストを渡します。

tagger = MeCab.Tagger()
print(tagger.parseNBest(3, "すももももも"))
"""
すもも	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
EOS
すもも	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
もも	名詞,一般,*,*,*,*,もも,モモ,モモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
EOS
すもも	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も	助詞,係助詞,*,*,*,*,も,モ,モ
もも	動詞,自立,*,*,五段・マ行,未然ウ接続,もむ,モモ,モモ
EOS
"""

さて、基本的な使い方の説明は以上になります。

最後に一点、MeCabのN-Best解の出力には一つバグがあるらしいのでその情報を共有しておきます。通常のフォーマットで出力する場合は何も問題ないのですが、出力フォーマットをカスタマイズして生起コスト、連接コストとその総和を表示するとどうやらN個の出力結果の総コストが全部同じ値になってしまいます。
コストの総和が小さい順に出力されているので、2番目の出力のコストは何なのかな?とか1番目とどのくらい差分があるんだろうか?とか気になるのですが、正確な値が出力されないということでちょっと不便ですね。一応見ておきます。
参考: MeCabの出力形式を変更する

%pc が文頭からの 連接コスト + 単語生起コスト の累積で、EOSと共に出力されたやつが、その解析結果全体のコストの総和です。

tagger = MeCab.Tagger("-F %M\\t%pc\\t%H\\n -E EOS\\t%pc\\n")
print(tagger.parseNBest(3, "すももももも"))
"""
すもも	7263	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も	7774	助詞,係助詞,*,*,*,*,も,モ,モ
もも	15010	名詞,一般,*,*,*,*,もも,モモ,モモ
EOS	14437
すもも	7263	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
もも	14544	名詞,一般,*,*,*,*,もも,モモ,モモ
も	15055	助詞,係助詞,*,*,*,*,も,モ,モ
EOS	14437
すもも	7263	名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も	7774	助詞,係助詞,*,*,*,*,も,モ,モ
もも	16749	動詞,自立,*,*,五段・マ行,未然ウ接続,もむ,モモ,モモ
EOS	14437
"""

ご覧の通り、3パターンとも全部 14437 になってしまっていますね。
利用する場合は注意が必要です。

どうやら開発者の方(@taku910)も認識されているバグのようですが、事情があって修正されてていないようです。(ツイートされています)

N-best解でコストが変更されないのは、今のところ仕様でTODOになっています。修正は厄介で、前方からの最適コストはA*探索で使うため、書き換えられないのです。

https://twitter.com/taku910/status/383126956893413376

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です