会社と私物でそれぞれMacbookを持っていて、AWSのアカウントとそこで動くEC2インスタンスもそれぞれあり、さらにDocker等含めていくつもPython環境を使っています。その中の一つで、突然PythonからMeCabが動かせなくなってしまったのでその解決方法のメモです。
具体的にはそれまで普通にMeCabが動かせていた環境にもかかわらず、次の様なエラーが出るようになってしまいました。MeCabをインポートして、MeCab.Tagger()するだけでエラーになるのでお手上げ状態でした。
>>> import MeCab
>>> MeCab.Tagger()
Failed initializing MeCab. Please see the README for possible solutions:
https://github.com/SamuraiT/mecab-python3#common-issues
If you are still having trouble, please file an issue here, and include the
ERROR DETAILS below:
https://github.com/SamuraiT/mecab-python3/issues
issueを英語で書く必要はありません。
------------------- ERROR DETAILS ------------------------
arguments:
error message: [ifs] no such file or directory: {Macのユーザー名を含む長いPathなのでマスク}/site-packages/unidic/dicdir/mecabrc
この事象が発生した環境には、MeCabは正常にインストールされていて、元々 MeCab.Tagger() も正常に実行できていました。
mecabrc ファイルも $ mecab-config –sysconfdir で取得できる場所、
つまり、/usr/local/etc/mecabrc にしっかり配置されているのに、全然違う .pyenv が管理してる各種ライブラリの配置場所を見に行ってそこに mecabrc が無いというエラーを起こしています。
原因調査編
確認してみると確かに site-packages ディレクトリ配下に mecabrc ファイルはありませんでした。というよりも、site-packages/unidic/dicdir 自体がありませんでした。
ネットで検索するとこのエラーメッセージで表示されたパスに /usr/local/etc/mecabrc をコピーして配置するという応急処置を取っている人がいましたが、ここは pipやcondaなどのパッケージ管理システムが管理している場所なので軽はずみに手動でいじりたくはありません。ということで腰を入れて原因と対策を調査しました。
結果、以下のことが原意で起きてるのがわかりました。
– transformers を、ドキュメントに沿って、[ja] というオプション付きで入れた。
-依存パッケージとしてunidic(MeCabの辞書の一つ)が一緒にインストールされた。
– unidicはインストールしただけでは辞書本体がダウンロードされない。
参考: unidic · PyPI
– 最近のmecab-python3はunidicを優先的に使おうとする。
(事象が発生したのは mecab-python3==1.0.4。mecab-python3==0.996では発生しない。)
transformers というのはBert等の学習済みモデルを手軽に使えるパッケージですね。インストール時にtransformers[ja] として入れると、日本語モデルを使うためのパッケージも一緒に入れてくれます。
これをやった時に、unidicというパッケージが入ったのです。
そして、unidicのドキュメントにある通り、この時点では辞書本体は端末にダウンロードされていませんでした。
しかし、インストールはされているので、Pythonコード上で、 import unidic は成功するし、unidic.DICDIR という値も取得できるわけです。(しかしそのディレクトリに辞書本体は無い。)
そしてさらに、このエラーが発生した環境のmecab-python3は割と最近の version 1.0.4 が入っていたのです。
mecab-python3のリポジトリで、 Add support for unidic installs via pypi というコミット を見ていただくとわかりやすいと思うのですが、この修正以降、 unidicが import できたら unidic を使おうとするようになっています。
def try_import_unidic():
"""Import unidic or unidic-lite if available. Return dicdir.
This is specifically for dictionaries installed via pip.
"""
try:
import unidic
return unidic.DICDIR
except ImportError:
try:
import unidic_lite
return unidic_lite.DICDIR
except ImportError:
# This is OK, just give up.
return
class Tagger(_MeCab.Tagger):
def __init__(self, rawargs=""):
# First check for Unidic.
unidicdir = try_import_unidic()
args = rawargs
if unidicdir:
mecabrc = os.path.join(unidicdir, 'mecabrc')
args = '-r "{}" -d "{}" '.format(mecabrc, unidicdir) + args
# The first argument here isn't used. In the MeCab binary the argc and
# argv from the shell are re-used, so the first element will be the
# binary name.
args = ['', '-C'] + shlex.split(args)
# need to encode the strings to bytes, see here:
# https://stackoverflow.com/questions/48391926/python-swig-in-typemap-does-not-work
args = [x.encode('utf-8') for x in args]
try:
super(Tagger, self).__init__(args)
except RuntimeError as ee:
raise RuntimeError(error_info(rawargs)) from ee
Tagger作る時に最初に unidicを調べて、インポートに成功したら、引数に
args = ‘-r “{}” -d “{}” ‘.format(mecabrc, unidicdir) + args
として、unidicのmecabrcファイルと辞書のパスを追加していますね。
私物のMacの環境は、 mecab-python3 のバージョンが古く、この処理が無かったので素直にIPA辞書を使ってくれているようです。そして、このエラーが発生した環境は、つい最近までunidicが入ってなかったので、 「# This is OK, just give up.」 のコメントの通り、importできなかったので、unidicを使うのを諦めてIPA辞書を使ってくれていたようです。
対応編
エラーになる原因が分かったので対応案を検討してやっていきましょう。
案1. mecab-python3のバージョンを下げる。
要するにversion 0.996 だったら無理してunidic使おうとしないので解決です。
ただ、この先ずっとmecab-python3だけバージョンを上げずに使い続けるのか、という問題があるので個人的にはこれはお勧めしません。僕も採用しませんでした。
案2. unidicの本体をダウンロードする。
unidicのimport ができるのに、辞書本体がダウンロードされていないのが原因なので本体ダウンロードしましょうというのが方針です。実際はこれを採用しました。
コマンドはドキュメントの通りです。1回だけ実行すればOK。
python -m unidic download
これを実行すると、 MeCab.Tagger() が成功するようになりました。ただし、デフォルトでunidicが使われるようになります。 テキストをparseした結果の品詞等の情報の出力がIPA辞書と全然違うものになってしまいました。
今後、IPA辞書を使いたいときは次のようにしてIPA辞書のディレクトリを明示的に指定する必要があります。ちょっと面倒になりました。(環境によってIPA辞書のパスは違うので注意してください。)
import MeCab
MeCab.Tagger("-d /usr/local/lib/mecab/dic/ipadic/")
案3. mecabrc ファイルパスを指定してTaggerを生成する。
実はこれも試し、成功しています。何らかの事情でunidicをダウンロードしたく無い場合は、
-r オプションで mecabrc ファイルを指定し、-d で辞書を指定することで動かすことができます。 上で参照した mecab-python3のソースコードで、
「args = ‘-r “{}” -d “{}” ‘.format(mecabrc, unidicdir) + args」
となっていますが、自分が指定した -r と -d もargsとしてMeCabに渡す引数に加えられるようなのです。そしてこれらはどちらも1つしか指定できないので後に付け加えられる分が先に書かれたunidic分を上書きしているようです。
具体的には次のように使います。(具体的なパスは環境に応じて変えてください。)
MeCab.Tagger("-r /usr/local/etc/mecabrc -d /usr/local/lib/mecab/dic/ipadic/")
-r と -d は両方必須なので面倒です。片方だけだと指定しなかった方が unidicを見に行ってしまいます。(mecabrcファイル内に辞書ディレクトリのパスが指定されていますが、-dで指定した方が優先。)
結局これは採用しませんでした。記述量が多いから。
感想と今後の方針
MeCabは動かせるようになりましたが、デフォルトの辞書がunidicになってしまって毎回IPA辞書を指定しないといけない不便を感じるようになりました。
しかし、そもそもなぜ mecab-python3がunidic推しになったのかという問題があります。これは結構明らかで、IPA辞書がかなり昔に更新が止まってしまっているのに対して、unidicの方は最近も更新が続いているからでしょう。
新目の単語がIPA辞書に含まれていないので、その点では確かにunidicの方が優れているのですが、ざっと比較したところ、全面的にunidicが優秀というわけでもなく慣れもあってまだ個人的にIPA辞の方が使いやすい印象でいます。語彙だけでなく出力形式はかなり違いますし。
とはいえ、これを機会に、unidicの思想や特徴、活用方法をきちんと学んで、こちらを使うように寄せていくことも検討した方がいいのかなと思う出来事でした。
とりあえず、MeCab.Tagger() したときの挙動が僕が持っている環境間で異なり、コードの使い回しがしにくくなったというのが目下の自分の課題なので、これをどうにかしていこうと思います。全環境でIPA辞書を指定したコードを書くのか、もう諦めて全面的にunidicに移行するのか。