2009-05-31
■MeCabの辞書にはてなキーワードを追加しよう 
MeCabは形態素解析のためのソフトウェアです。日本語を分かち書きするために使われるものとしては最も人気の高いものだと思われますが、チャットや掲示板に書き込まれるような崩した日本語や、正しく書かれた日本語でも新語を期待した通りに分かち書きしてくれないことがあります。これはMeCabの内部で使われている辞書が一般的な言葉を情報源としているわけではないことに関係があります。MeCabというか、より一般的な話ですが以下のような認識が一般的かと思われます。
というのも、一番広く使われているであろう自然言語処理技術は形態素解析(単語分かち書き、と言ったほうがいいのかもしれない)であろうが、これは現在99%くらいの精度になっていて、すでに人間がやるより遙かに高精度で行えるのだが、これだけ高い精度が出せるのは新聞記事を相手にしたときだけであって、それは新聞記事をコーパスとして用いる機械学習により形態素解析器を作っているので仕方ない面はあるが、医療分野や特許分野のように専門分野になったりウェブ文書を相手にしたりすると、一気に精度が落ち込むのは広く知られた事実である。大規模なコーパスがある状況で、実際に適用したい分野がそのコーパスと違う状況でも精度を落とさず解析できるようにしましょう、というのは自然言語処理の言葉では分野適応というのだが、これもここ数年研究が始まったばかりで、機械翻訳の現状を見ると「こうすれば万事解決」というようなものではない。(もっとも、ある分野を解析したいとして、数千語辞書を作れば精度95%くらいには上がるそうなので、現実的には地道に辞書を作ればいい、という話でもある)
Wolfram|alpha や Bing のすごくないがゆえにすごいところ - 生駒日記
じゃあ辞書ってどこにあんの
現在MeCabではnaist-jdicという辞書が推奨されています。5月の下旬に最新版が更新されています。
しかし、前述の通りこれだけでは最新の単語に対応することは出来ません。そこで辞書として新語やネット上で一般的な言葉でMeCabに入っていないような語はどこにあるんだと考えたところ、エリートスパム集団はてなのはてなキーワードとwikipediaの項目名が浮かびました。Googleで単語検索したとき誰もが目にする情報源ですから関心を持って項目を更新している方が多いと思われ、今後もしばらく有用であると考えられます。
両方とも公式に最新のダンプが公開されています。入手方法は以下を参照してください。
単語数:24万(2009年5月)
page_namespace=0の項目数:85万(2008年12月)
かなり性質の違うものなので項目数以上のことは特に調べていません。注意点としては両方とも日付など単語として不適な項目が含まれていることです。目的に応じて中身をチェックしておくことが必要です。
今回ははてなキーワードを追加してみます。
MeCabのインストール
以下はCentOS5,MacOSXで使うためのメモです。*1(19:58追記)
依存ライブラリ等が入っていない場合は下記のコマンドの前に公式のインストールガイドを参照してインストールしてください。
MeCab
wget http://nchc.dl.sourceforge.net/sourceforge/mecab/mecab-0.98pre3.tar.gz
tar xzvf mecab-0.98pre3.tar.gz
cd mecab-0.98pre3
./configure && make
sudo make install
naist-jdic
MeCabだけではまだ利用できません。MeCabが利用する辞書をインストールしましょう。naist-jdicを使います。
wget http://iij.dl.sourceforge.jp/naist-jdic/40117/mecab-naist-jdic-0.6.0-20090616pre3.tar.gz
tar xzvf mecab-naist-jdic-0.6.0-20090616pre3.tar.gz
cd mecab-naist-jdic-0.6.0-20090616pre3
./configure --with-charset=utf8
make
sudo make install
デフォルトだと辞書が/usr/local/lib_mecab/dic/naist-jdicに作成されます。
sudo vi /usr/local/etc/mecabrc
にて利用する辞書を選択できるので作成したnaist-jdicを利用するように変更します。
dicdir = /usr/local/lib/mecab/dic/ipadic
を以下のように変更
テスト
これでMeCabが使えるようになりました。プロンプトで以下のようにして動作を確認してください。
echo ずっと日曜日ならいいのに | mecab
ずっと日曜日ならいいのに
ずっと 副詞,一般,*,*,*,*,ずっと,ズット,ズット
日曜日 名詞,副詞可能,*,*,*,*,日曜日,ニチヨウビ,ニチヨービ
いい 形容詞,自立,*,*,形容詞・イイ,基本形,いい,イイ,イイ
のに 助詞,接続助詞,*,*,*,*,のに,ノニ,ノニ
辞書の更新
MeCabはユーザーが辞書を更新することが出来ます。MeCab: 単語の追加方法を参考にはてなキーワードを追加します。
mecab-naist-jdic-0.6.0-20090616pre3/で作業します。はてなキーワードをダウンロードして辞書に追加します。
wget http://d.hatena.ne.jp/images/keyword/keywordlist_furigana.csv -O hatena.txt
nkf -Ew hatena.txt | python createDict.py | nkf -e > hatenakeyword.csv
辞書の構築では辞書候補として.csvが自動選択されるため、はてなキーワードはhatena.txtにリネームして保存していることに注意してください。
createDict.pyってなんだよ。って話なんですが、はてなキーワードのcsvとMeCabの辞書csvのフォーマットが異なるため、整形するためのフィルタです。
僕は文字コードが非常に苦手ですのでファイル整形にPythonを使いましたがPythonの中はUTF8でやりたかったのでまぬけなnkfが付いてます。
vi createDict.pyなどして以下のようなスクリプトを配置した上で、上記のコマンドを実行してください。
# -*- encoding: utf-8 -*- import sys import re #数字四桁が入ったキーワードは役に立ちませんので検出して飛ばします。 year = re.compile("[0-9]{4}") #驚くべきことにはてなキーワードには%00というキーワードがありますが、 #これがcsvとして提供されているダンプではヌル文字になっているのでシステム制御文字を非許可にします。 ng = [chr(i) for i in range(0,32)] def main(): for x in sys.stdin: if re.search(year,x): continue #日付スキップ k = x.split("\t")[1].strip() if len(k) < 2: continue #一文字スキップ for word in ng: if word in k: continue #システム制御文字スキップ k = k.lower() #MeCabはケースセンシティブなので小文字に統一して辞書作成 cost = int(max(-36000, -400 * len(k)**1.5)) #コストについては後述 print "%s,0,0,%s,名詞,一般,*,*,*,*,%s,*,*,はてなキーワード," % (k,cost,k) #0については後述 if __name__ == '__main__': main()
若干複雑になってきましたが、以下のような物が作成されていればokです。
grep "google wave" hatenakeyword.csv | nkf -Ew
google wave,0,0,-14593,名詞,一般,*,*,*,*,google wave,*,*,はてなキーワード,
ここまで確認できたら辞書を再構築してインストールし直しましょう。
make clean && make
sudo make install
辞書が再構築され、はてなキーワードがMeCabでも切り出せるようになったことを確認してください。
echo ニコニコ動画の車載動画は楽しいですが東のエデンの方が面白い。 | mecab
ニコニコ動画 名詞,一般,*,*,*,*,ニコニコ動画,*,*,はてなキーワード,
の 助詞,連体化,*,*,*,*,の,ノ,ノ,,
車載動画 名詞,一般,*,*,*,*,車載動画,*,*,はてなキーワード,
は 助詞,係助詞,*,*,*,*,は,ハ,ワ,,
楽しい 名詞,一般,*,*,*,*,楽しい,*,*,はてなキーワード,
です 助動詞,*,*,*,特殊・デス,基本形,です,デス,デス,,
が 助詞,接続助詞,*,*,*,*,が,ガ,ガ,,
東のエデン 名詞,一般,*,*,*,*,東のエデン,*,*,はてなキーワード,
の 助詞,連体化,*,*,*,*,の,ノ,ノ,,
方 名詞,非自立,一般,*,*,*,方,ホウ,ホー,,
が 助詞,格助詞,一般,*,*,*,が,ガ,ガ,,
面白い 形容詞,自立,*,*,形容詞・アウオ段,基本形,面白い,オモシロイ,オモシロイ,おもしろい/面白い,
。 記号,句点,*,*,*,*,。,。,。,,
単語に設定するコストと文脈IDをどう決めるか
話が前後して申し訳ないのですが、前述のcreateDict.pyの中で
cost = int(max(-36000, -400 * len(k)**1.5)) #コストについては後述
print "%s,0,0,%s,名詞,一般,*,*,*,*,%s,*,*,はてなキーワード," % (k,cost,k) #0については後述
としていた箇所について説明します。MeCab: 単語の追加方法によれば、
コストは,その単語がどれだけ出現しやすいかを示しています. 小さいほど, 出現しやすいという意味になります. 似たような単語と 同じスコアを割り振り, その単位で切り出せない場合は, 徐々に小さくしていけばいいと思います.
とのことなので文字列の長さに応じてコストを設定しています。gooとgoogleが同じコストで登録されるとgoogleと書いてもgooで切り出されたります。それは期待した動作ではないのでMeCab の辞書構造と汎用テキスト変換ツールとしての利用のケーススタディを参考にコストを設定しています。
これらの数値の設定によって挙動は変わるので、期待した動作にならない場合は調節してみましょう。その際にはmecabの-Nオプションを使ってどのような候補のなかで結果が選ばれたのか参照しながら進めると若干楽かもしれません。以下は上記のコスト関数で実行した例です。すべてのキーワードのコストを同じにしてはいけないことがなんとなく分かります。
google 名詞,一般,*,*,*,*,google,*,*,はてなキーワード,
goo 名詞,一般,*,*,*,*,goo,*,*,はてなキーワード,
gl 名詞,一般,*,*,*,*,gl,*,*,はてなキーワード,
e 記号,アルファベット,*,*,*,*,e,イー,イー,,
#もう一例
ニコニコ 名詞,一般,*,*,*,*,ニコニコ,*,*,はてなキーワード,
動 名詞,一般,*,*,*,*,動,ドウ,ドー,,
ニコ 名詞,一般,*,*,*,*,*
文脈ID
print "%s,0,0,%s,名詞,一般,*,*,*,*,%s,*,*,はてなキーワード," % (k,cost,k) #0については後述
次に文脈ID(0,0,とか指定されていた部分)ですが、これは正直に言うとどのような数値を設定するのが妥当なのかわかりません。ただし、-1によって自動判定してくれる機能がバグっていて利用できないので1345とか0とか名詞,一般の他の単語にどのような数値が設定されているかを参考に設定するのがよいかもしれません。何かご存知の方は教えてくださると助かります。

複合語とか除外しないと精度が悪くなるような気がしましたが、固有名詞として考えるとむしろその方が精度向上しますね。
IPADICだけの場合は「twitter」という文字列を1つの単語として認識してくれますが、naist-jdicを追加すると、twitterをt_w_i_t_t_e_rと分割してしまい不便に感じています。
辞書に登録していない英文字列を取得したときの処理?みたいなものがnaist-jdic記述してあるのでしょうか?
IPADICにはアルファベットが登録してあるために、それぞれが分割して認識されていました。
(品詞 (記号 アルファベット)) ((見出し語 (T 2226)) (読み ティー) (発音 ティー) )
全ての単語を同じように扱ってしまうことは確かにリスクがあります。複合語をどのように捉えるかは難しいですね。ひろく使われている言葉であればそれはもう通常の語と同じであると言えるのではないかと思います。はてなキーワードに登録されていることをもって広く使われているというには強引な気もしますが、やはり目的を設定した上で最善のものを使うしかないと思います。
対して、アルゴリズムによる複合語抽出は解析対象にたいして柔軟であることの裏返しとして対象によっては毎回同じ分かち書きがされることを期待できなくなると考えています。もう少し勉強してみたいと思います。
>>snkken
たしかに。アルファベット削除参考になります。作成した辞書を使って、英語が混ざった対象を扱っていて気がついたのですが、例えばdictionaryという単語がうまく切り出せません。はてなキーワードにdictionaryの部分文字列が存在しているためです。かといってIPA辞書のように扱ってもステミングは自分でやらなければいけないし英日を一つのレイヤーで統一的に扱うのは難しそうですがなるべく簡単に扱いたいですね。ふと英単語を全部登録するのはどうかしらと考えましたがもっといい方法がある気もします
で,こちらの方法で追加させていただいたところ,地域名など,名詞で入れると困ったりしたので,
該当部分を省く用のソースを書いて掲載しました.
といってもid:code46さんのソースそのまんまなので,不味そうでしたら消します.
宜しくお願いします.
http://d.hatena.ne.jp/rin1024/20090830/1251608698
なるほど正規表現で判別するのはいいアイデアだと思います!
掲載は全く問題ないです。よろしくお願いします。
ありがとうございます!
今後とも宜しくお願いします><
上のソースを参考に、Rubyで同様の処理を行うソースとその説明コンテンツを作成してみました。
http://www.mwsoft.jp/programming/munou/mecab_hatena.html
もし掲載内容に問題がありましたらご連絡ください。すぐに対応致します。
それから正確かどうかはちょっと自信がないのですが、文脈IDについても説明を入れてみましたので、宜しければご覧ください。