2010年09月11日

MeCabの未知語(unk.def)と戯れた記録

今年の初めくらいにメモして後でまとめようと思っていた話なんだけど、さっぱりまとめる暇が取れないまま放置されていたので、メモをそのままブログに貼っておく。



MeCab(辞書はNAIST)で遊んでいたら、スクエニが以下のように解析された。

echo スクエニ | mecab
スクエニ 感動詞,*,*,*,*,*,*
EOS

感動詞になっている。出来れば名詞が良いなぁと思う。
スクエニは未知語として判定されていると思われる。
未知語判定されているかどうかは、-F%Sすれば分かる。
1が出れば未知語、0は辞書にある語。

echo スクエニ魔法 | mecab -F"%m\t%s\n"
スクエニ 1
魔法 0
EOS

未知語については「/usr/local/lib/mecab/dic/naist-jdic/unk.def」を見れば分かる。

KATAKANA,1358,1358,3869,名詞,一般,*,*,*,*,*
KATAKANA,1365,1365,4186,名詞,固有名詞,地域,一般,*,*,*
KATAKANA,1364,1364,4074,名詞,固有名詞,組織,*,*,*,*
KATAKANA,1361,1361,4181,名詞,固有名詞,人名,一般,*,*,*
KATAKANA,1360,1360,4077,名詞,固有名詞,一般,*,*,*,*
KATAKANA,3,3,2577,感動詞,*,*,*,*,*,*

確かに未知語でKATAKANAの場合は感動詞に割り振られるパターンがあるらしい。しかもスコア的には一番低くなっている。

試しにIPA辞書でmecabってみる。-dでシステム辞書が指定できる。

echo スクエニ | mecab -d /usr/local/lib/mecab/dic/ipadic
スクエニ 名詞,固有名詞,組織,*,*,*,*
EOS

品詞が名詞になった。IPAのunk.defはどう設定されているのか。
IPA辞書は「/usr/local/lib/mecab/dic/ipadic」にいるけど、defファイルは入ってない。
但し、ダウンロードしたtar.gzファイルを展開したところにはいるので、それを覗いてみる。

KATAKANA,1285,1285,9461,名詞,一般,*,*,*,*,*
KATAKANA,1293,1293,13661,名詞,固有名詞,地域,一般,*,*,*
KATAKANA,1292,1292,10922,名詞,固有名詞,組織,*,*,*,*
KATAKANA,1289,1289,13581,名詞,固有名詞,人名,一般,*,*,*
KATAKANA,1288,1288,10521,名詞,固有名詞,一般,*,*,*,*
KATAKANA,3,3,14138,感動詞,*,*,*,*,*,*

名詞,一般がもっともコストが低くなっている。
対するNAISTでは感動詞が一番低くなっている。
そのため、NAIST辞書では感動詞が選ばれている。たぶん。

じゃ、コストを下げれば良いのだろうけど、unk.dicの再生成の方法が分からない。
そんな時はヘルプ。

/usr/local/libexec/mecab/mecab-dict-index -h

Usage: /usr/local/libexec/mecab/mecab-dict-index [options] files
-d, --dicdir=DIR set DIR as dicdi (default ".")
-o, --outdir=DIR set DIR as output dir (default ".")
-U, --unknown build parameters for unknown words
<以下略>

-Uというのが怪しい

試しにunk.defの数字を書き換えてみる。

KATAKANA,3,3,2577,感動詞,*,*,*,*,*,*
これの2577を5577にしてみる。

その後、unk.dicのバックアップ。
$ cd /usr/local/lib/mecab/dic
$ cp unk.dic unk.dic.org

で、辞書の作り直し。

$ /usr/local/libexec/mecab/mecab-dict-index -d ../naist-jdic -U unk.dic -t utf-8 unk.def
reading ../naist-jdic/unk.def ... 40
emitting double-array: 100% |###########################################|

done!

なんか出来た。ちゃんと結果が名詞になった。

$ echo スクエニ | mecab
スクエニ 名詞,一般,*,*,*,*,*
EOS

うまくいったのか不安なので、試しに「名詞,一般」ではなく「名詞,固有名詞」として判定させられるか試してみる。

KATAKANA,1360,1360,4077,名詞,固有名詞,一般,*,*,*,*
こうなっている箇所のスコアを、4077から3077に変更。
これで名詞一般よりもスコアが低くなった。

再作成
$ /usr/local/libexec/mecab/mecab-dict-index -d ../naist-jdic -U unk.dic -t utf-8 unk.def

$ echo スクエニ | mecab
スクエニ 名詞,固有名詞,一般,*,*,*,*
EOS

どうやら出来たらしい。
ちなみにここのコストをものすごく低くすると、カタカナが全部1文字ずつ分割されてしまう。

KATAKANA,1360,1360,-10000,名詞,固有名詞,一般,*,*,*,*

上記みたくすると

$ echoスクエニ | mecab
ス 名詞,一般,*,*,*,*,*
ク 名詞,一般,*,*,*,*,*
エ 名詞,一般,*,*,*,*,*
ニ 名詞,一般,*,*,*,*,*
EOS

連結コストがマイナスだと、分けて連結した方がコスト的にマイナスになるので。

しかしこの辺りの数字をいい感じの値に収めると、微妙なカタカナの分割がなくなる。
たとえば、ユニクロ。
$ echo ユニクロ | meca
ユニ 名詞,固有名詞,人名,一般,*,*,ユニ,ユニ,ユニ,,
クロ 名詞,一般,*,*,*,*,クロ,クロ,クロ,,
EOS

こんな風に分割されてしまうのを、未知語カタカナのコストを1500にすると。

$ echo ユニクロ| mecab
ユニクロ 名詞,一般,*,*,*,*,*
EOS

こうなる。
カタカナが続く場合は、ひとまとめにして名詞,一般にしてくれるわけだ。

1500程度の数字なら、辞書にある文字を押しのけてでしゃばるほどではない。
例えばマイケルという文字はそのまま使われる。

$ echo マイケル | mecab
マイケル 名詞,固有名詞,人名,名,*,*,マイケル,マイケル,マイケル,,
EOS

文の繋がりによっては、変な特性が出てしまうかもしれないけど。

さて、カタカナの問題は解決した。そうなると英語の問題も気になってくる。例えば僕らの情報推進処理機構、IPAという文字をmecabると以下のように一語ごとにバラされてしまう。

$ echo IPA | mecab
I 記号,アルファベット,*,*,*,*,I,アイ,アイ,,
P 記号,アルファベット,*,*,*,*,P,ピー,ピー,,
A 記号,アルファベット,*,*,*,*,A,エイ,エイ,,
EOS

この辺も一語にまとまってくれると楽だ。アルファベットはALPHAの項を直せば良い。と思って見てみたが、数字は適正なように見える。

ALPHA,1358,1358,3832,名詞,一般,*,*,*,*,*
ALPHA,1365,1365,4165,名詞,固有名詞,地域,一般,*,*,*
ALPHA,1364,1364,4053,名詞,固有名詞,組織,*,*,*,*
ALPHA,1361,1361,4160,名詞,固有名詞,人名,一般,*,*,*
ALPHA,1360,1360,4054,名詞,固有名詞,一般,*,*,*,*
ALPHA,3,3,2579,感動詞,*,*,*,*,*,*

こんな時はchar.defを確認

CHARACTER CATEGORY DEFINITIONを見ると、こうなっている。

NUMERIC 1 1 0
ALPHA 1 1 0
HIRAGANA 0 1 2
KATAKANA 1 1 2

NUMERICとALPHAは最後の数字が0になってるとこが怪しい。試しにNUMERIC(数字)をmecabってみる。

$ echo 1990 | mecab
1 名詞,数,*,*,*,*,1,イチ,イチ,,
9 名詞,数,*,*,*,*,9,キュウ,キュー,,
9 名詞,数,*,*,*,*,9,キュウ,キュー,,
0 名詞,数,*,*,*,*,0,ゼロ,ゼロ,,
EOS

やはりALPHAと同じく分かれる。

じゃ、作り直してみようということで、とりあえず書き直し。

NUMERIC 1 1 2
ALPHA 1 1 2

作成方法は、ヘルプを見る限りではこれが怪しい。
-C, --charcategory build character category maps

まず、バックアップ。
$ cp char.bin char.bin.org

で、再作成
$ /usr/local/libexec/mecab/mecab-dict-index -d ../naist-jdic -C char.bin -t utf-8 char.def

でも、結果は変わらない。ハズレだったようだ。

あー、わかった。
英数が単体で辞書に登録されているんだ。こんな感じに。

I 記号,アルファベット,*,*,*,*,I,アイ,アイ,,
4,1367,1367,703,名詞,数,*,*,*,*,4,ヨン,ヨン,,
5,1367,1367,709,名詞,数,*,*,*,*,5,ゴ,ゴ,,

これ、邪魔ですね。

無駄なものを消して辞書を再作成すれば良いのだけど、バラバラに存在する72行(数字、アルファベット、それぞれ全角と半角)を消すのは面倒。というわけで、未知語のコストを高くしてしまえば良いわけだ。

まずは、unk.defのNUMERICとALPHAのスコアを低い数字に修正
NUMERIC,1367,1367,1400,名詞,数,*,*,*,*,*
ALPHA,1358,1358,1400,名詞,一般,*,*,*,*,*

他のアルファベットや数字を使う言葉を邪魔しないように、且つ、コストに700強が設定されている数字やアルファベット単体の子たちが2つ並んだ場合でも負けないようにということで、1400を設定。700強が2つ合わさると1400強になるから、1400が勝つという寸法。連接コストにもよるけど。

辞書再作成
/usr/local/libexec/mecab/mecab-dict-index -d ../naist-jdic -U unk.dic -t utf-8 unk.def

$ echo 1990 | mecab
1990 名詞,数,*,*,*,*,*
EOS

$ echo IPA | mecab
IPA 名詞,一般,*,*,*,*,*
EOS

アルファベット単品の場合

$ echo A列車 | mecab
A 記号,アルファベット,*,*,*,*,A,エイ,エイ,,
列車 名詞,一般,*,*,*,*,列車,レッシャ,レッシャ,,
EOS

アルファベットが複数の場合
$ echo AAA列車 | mecab
AAA 名詞,一般,*,*,*,*,*
列車 名詞,一般,*,*,*,*,列車,レッシャ,レッシャ,,
EOS

おっけー。

ついでに記号についても少し気になる。

$ echo "(--;" | mecab
(--; 名詞,サ変接続,*,*,*,*,*
EOS

こういう顔文字がサ変接続になっている。
顔文字は顔文字辞書でカバーするとして、記号が並んだ場合は「記号,一般」辺りがよさそう。

SYMBOL,1356,1356,3740,名詞,サ変接続,*,*,*,*,*

これを

SYMBOL,5,5,3740,記号,一般,*,*,*,*,*

こうしてみる。

$ echo "(--;" | mecab
(--; 記号,一般,*,*,*,*,*
EOS

あと気になった点として、未知語を取得する際に「・」(中黒)がカタカナとベッタリになる。「ヘレン・ケラー」のように取れるなら良いのだけど、「・カタカナ・」のように、前後にもくっついた状態で出てしまう。

中黒の文字コードは「30FB」。Unicodeのカタカナは「30A0〜30FF」。というわけで、Unicode的には中黒はカタカナに入るらしい。EUC-JPだと記号のところにいるのだけどね。

$ vi char.def
# KATAKANA
0x30A1..0x30FF KATAKANA
0x31F0..0x31FF KATAKANA # Small KU .. Small RO
# 0x30FC KATAKANA HIRAGANA # !シ




【関連記事】

MeCabのコマンドライン引数一覧とその実行例
http://www.mwsoft.jp/programming/munou/mecab_command.html

日本テレビ東京で学ぶMeCabのコスト計算
http://www.mwsoft.jp/programming/munou/mecab_nitteretou.html