GiNZAの学習モデルモジュールが見つからない!?エラーと解決策・調べたことまとめ

たかが一文字、されど一文字
2022.12.14

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

地域限定のTipsをお話しようと思います。
栗きんとんと言えば数個まとめて箱に入っているものが売られているものですが、岐阜県のアンテナショップでは単品の栗きんとんが買えます。
単品の栗きんとんも美味しいのですが、栗きんとんを作る過程で生まれる「栗きんとんのおこげ」というものがこの世には存在します。それがかなり美味しいので、見かける機会があったら是非買ってみてください。


▲ 栗きんとんと聞いてこれしか浮かばなかった方は「岐阜 栗きんとん」でググってみてください

こんにちは。データアナリティクス事業本部 インテグレーション部 機械学習チームのShirotaです。
これは「 クラスメソッド 機械学習チーム アドベントカレンダー 2022 」12/14(水)の記事となっております。
前日 12/13(火)の記事は以下よりご覧ください。Vertex AI Workbenchのマネージドノートブックでカスタムコンテナを利用する、Google Cloudで機械学習をやる人には特に嬉しいカスタムコンテナに関するブログになっております。

自然言語処理強化月間 と銘打って私は今回のアドベントカレンダーを執筆しているのですが、今回は前回の裏話という名のトラブル解決までの小ネタ備忘録を残しておきたいと思います。

GiNZAの検証時、実は初手でこけていた

前回のアドベントカレンダーでは、難なくGiNZAが動いたよ!という体でブログを書いておりました。

すいません。実は初手でつまずいて数時間ずっと唸っていました。

何が起きていたのか、どうやって解決したのか、また解決した今調べてみたことを折角なので備忘録として残しておきたいと思います。

動かしたコードとエラーメッセージ

以下、実際に動かしたコードとエラーメッセージを載せておきます。
良かったらどこが間違っていたのか考えてみてください。

動かなかったコード

import spacy
nlp = spacy.load('ja-ginza')
doc = nlp('文書の形態素解析をしてみたよ')
for sent in doc.sents:
    for token in sent:
        print(
            token.i,
            token.orth_,
            token.lemma_,
            token.norm_,
            token.morph.get("Reading"),
            token.pos_,
            token.morph.get("Inflection"),
            token.tag_,
            token.dep_,
            token.head.i,
        )
    print('EOS')

エラーメッセージ

ModuleNotFoundError: No module named 'ja-ginza'

ちなみに私はメッセージ通りのエラーだと思い、パッケージの存在確認をしたのでその結果も載せておきます。

pip list | grep ja-ginza

ja-ginza                      5.1.2
ja-ginza-electra              5.1.2

この情報を載せたことでだいぶ詰んだのではないかと思われます。
更におまけの話ですが、Google Colaboratory(以下 Colab)上でもローカル環境上でも同様のエラーが出ました。
ブログだと皆さんと対話して情報の追加をすることができないので、早速答えの方を公開したいと思います。

エラーの解決方法

ja-ginza(ハイフン繋ぎ)を ja_ginza(アンダースコア繋ぎ)に変えたところ、無事コードが動きました。

ここの解決に至るまでにかなりの時間を要してしまったのですが、(何なら自力で答えが見つからず、チームメンバーの nokomoro3 さんがひらめいてくれたお陰で解決しました。ありがとうございました!)

調べたこと

ここまででお話ししたかったことは話し終えたのですが、折角なのでこのモジュールやPythonパッケージを管理するリポジトリであるPython Package Index(以下 PyPI)について調べたことをまとめておきます。

ja_ginzaのモジュール情報

pip show ja_ginza でモジュール情報を調べてみると以下のようになっていました。

Name: ja-ginza
Version: 5.1.2
Summary: Japanese multi-task CNN trained on UD-Japanese BCCWJ r2.8 + GSK2014-A(2019). Assigns word2vec token vectors. Components: tok2vec, parser, ner, morphologizer, atteribute_ruler, compound_splitter, bunsetu_recognizer.
Home-page: https://github.com/megagonlabs/ginza
Author: Megagon Labs Tokyo.
Author-email: ginza@megagon.ai
License: MIT License
Location: /usr/local/lib/python3.8/dist-packages
Requires: ginza, sudachidict-core, sudachipy, spacy
Required-by:

ハイライトもしましたが、 ja-ginza となっています。ちなみに、冒頭に載せた通り pip listの結果も ja-ginza でした。
これはどういうことなのか、と気になったのでPythonのパッケージ管理リポジトリであるPyPIについて調べてみたところ、以下のようなルールがあることが公式ドキュメントから分かりました。

PEP 508 に従えば、正当なプロジェクト名は以下の条件を満たさなければなりません:

ASCII文字・数字・アンダースコア(_)・ハイフン(-)・ピリオド(.)だけを含むこと、かつ、

先頭と最後の文字がASCII文字ないし数字であること。

プロジェクト名の比較では、大文字小文字を区別せず、また、アンダースコア・ハイフン・ピリオドは何文字連続していても同じものとして扱います。例えば、あなたが cool-stuff という名前のプロジェクトを登録したなら、利用者がダウンロードしたり依存関係を宣言したりするのに、次に挙げる綴りのどれでも使うことができます:

Cool-Stuff
cool.stuff
COOL_STUFF
CoOl__-.-__sTuFF

つまり、インストール時に pip install ja-ginzapip install ja_ginza は区別されず、エラーが発生することなくライブラリはインストールできてしまうのです。

また、今回はこの事例に当てはまるかは分かりませんが、PyPIでは意図せずパッケージ名がアンダースコアからハイフンに変更される事例があるようでした。

先ほど確認してみたモジュール情報には、モジュールが格納されているディレクトリが「Location」という項目に記載されていました。
実際に、ディレクトリを確認してみます。

ls -la /usr/local/lib/python3.8/dist-packages | grep ginza
drwxr-xr-x  3 root root     4096 Dec 13 09:14 ginza
drwxr-xr-x  2 root root     4096 Dec 13 09:14 ginza-5.1.2.dist-info
drwxr-xr-x  4 root root     4096 Dec 13 09:14 ginza_transformers
drwxr-xr-x  2 root root     4096 Dec 13 09:14 ginza_transformers-0.4.2.dist-info
drwxr-xr-x  4 root root     4096 Dec 13 09:14 ja_ginza
drwxr-xr-x  2 root root     4096 Dec 13 09:14 ja_ginza-5.1.2.dist-info
drwxr-xr-x  4 root root     4096 Dec 13 09:14 ja_ginza_electra
drwxr-xr-x  2 root root     4096 Dec 13 09:14 ja_ginza_electra-5.1.2.dist-info

この時点で、モジュール名は ja_ginza になっています。
このため、ja-ginza で呼び出そうとしたらそんなモジュールはないよ、と ModuleNotFoundError が出て怒られていた訳でした。

Pythonのコーディング規約

そもそもPythonにおいては、import でハイフンを使おうとするとシンタックスエラーが出ます。
これについて、Pythonコードのコーディング規約であるPEP8を調べてみると以下のような命名規則があり、ハイフンについては言及されていないもののアンダースコアの利用はできることになっている旨が記載されていました。

モジュールの名前は、全て小文字の短い名前にすべきです。読みやすくなるなら、アンダースコアをモジュール名に使っても構いません。

自戒と次回予告

今回のエラーですが、もしかしたらPythonのモジュール名に関するコーディング規約の知識があったりインストールしたモジュールの詳細情報を真っ先に調べていたらこんなに悩むことはなかったかもしれないな……と感じました。
次に似たようなエラーがあったり、同様のエラーで悩んでいる方がいた時にこのブログが何らかの一助となれば幸いです。


▲ 最高、天才、Genius

さて、今回この備忘録を書くに当たって私はあるネタを先送りにしてしまいました。
このままだとどんどこ先送りにしてしまうので前もって次回予告をしておこうと思います。
次回は「コサイン類似度」の話をしたいと思います。次回こそします。

明日の機械学習チームのアドベントカレンダーもどうぞお楽しみに!