日本語自然言語処理オープンソースライブラリ「GiNZA」で構文解析をやってみた

GiNZAでレッツおしゃれに構文解析
2022.12.08

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

だいぶ寒くなってきたので、慌てて冬支度を始めました。毎日のように何かしらの荷物が届きます。


▲ 今年は猫用のホットカーペットを買いました、たまに乗っていただけます

こんにちは。データアナリティクス事業本部 インテグレーション部 機械学習チームのShirotaです。
これは「 クラスメソッド 機械学習チーム アドベントカレンダー 2022 」12/8(木)の記事となっております。
前日 12/7(水)の記事は以下よりご覧ください。Amazon SageMaker Studio Labの新機能を早速試してみたブログになっております。

自然言語処理強化月間 ということにして私は今回のアドベントカレンダーを執筆していくことにしましたが、今回もそんなわけで自然言語処理に関するお話をしていこうと思います。
それではいきましょう!

日本語自然言語処理オープンソースライブラリ 「GiNZA」

いきなりライブラリの名前を見出しにしました。
今回はこのGiNZAを使い、構文解析をやってみた話をします。
早速やってみた、に入ってもいいのですがまずはこのGiNZAについて簡単に内容をまとめておこうと思います。

GiNZAとは?

GiNZAは、2019年4月にMegagon Labsが国立国語研究所との共同研究を行い公開したオープンソースの日本語自然言語処理ライブラリです。

GiNZAの特徴について、以下にまとめてみました。

導入が簡単

Pythonコマンドであるpipでのインストールが可能になっています。

spaCyとSudachiPyが採用されている

以下二つの技術が採用されています。

  1. Pythonで利用できる多言語対応している自然言語処理ライブラリ「spaCy
  2. Pythonで利用できる日本語形態素解析器「SudachiPy

またデフォルトで「SudachiDict」という辞書がインストールされるので、GiNZA1つをインストールすることで簡単に日本語の自然言語処理に取り掛かることができます。

文節を処理できるAPIが追加されている(v4.0以降)

日本語特化型の自然言語処理ライブラリであるGiNZAは、v4.0以降(2022年12月8日現在、現行バージョンはv5.1)文節を処理できる便利なAPIが追加されています。
これにより、構文解析がやりやすくなりました。
構文解析ってなんぞや?という方は前回私が書いたブログを読んでいただけると嬉しいです。

また、文節処理APIでできるようになったことや、GiNZAの文節処理のパイプラインの詳細が知りたい方は公式ドキュメントを参考にしてください。

今回は構文解析をGiNZAでやるところのお話をしたいと思いますが、その前にもう1つだけGiNZAの大きなアップデートのお話をさせていただきます。

Transformerモデル採用による精度向上(v5.0以降)

GiNZAは元々、学習モデルとして ja_ginzaというCNNベースの学習モデルを採用していました。
v5.0以降利用できるようになった ja_ginza_electra は、深層学習モデルTransformerを採用しています。
ここではTransformerの詳細の説明は省略しますが、気になった方は以下Wikipediaのページを参考にしてください。

ja_ginza_electraは、解析処理速度は ja_ginza に劣るもののその代わり解析精度は高いモデルとなっています。
詳細な精度差などについては、以下公式ページをご覧ください。

様々なGiNZAの特徴について簡単にですがお話させていただいたところで、早速先ほど話した構文解析を試していきたいと思います。

GiNZAで構文解析をやってみた

早速、GiNZAを触っていきましょう。
今回は、Google Colaboratory(以下 Colab)上にGiNZAを導入して触ってみました。(後述するおまけ部分に関してはローカル環境を同様のものに整えて実施しております)
Colab環境上のPythonは 3.8.5 を利用しております。

GiNZAのインストール

特徴としても挙げた通り、GiNZAとGiNZAの学習モデルはpipで簡単にインストールができます。

!pip install ginza ja_ginza ja_ginza_electra

今回、Colab環境でインストールを実施したのでコマンドの頭に ! がついております。Colab環境以外にGiNZAを導入する際にはこちらを外してコマンドを実行してください。

コマンドとして解析処理を試してみる

GiNZAはコマンドとして解析処理を実行することができます。
折角なので、試してみました。
ginza コマンドを入力するとその後に文を入力できるようになるので文を入力すると、その文書を解析してCoNNL-U Syntactic Annotation形式で出力してくれます。
出力の形式については、以下の資料を参考にしてください。

今回は「文書の形態素解析をしてみたよ」という文書の解析をGiNZAにやってもらいました。
コマンドと、出力された結果は以下のようになります。

コマンド

ginza
文書の形態素解析をしてみたよ

結果

# text = 文書の形態素解析をしてみたよ
1       文書    文書    NOUN    名詞-普通名詞-一般      _       4       nmod    _       SpaceAfter=No|BunsetuBILabel=B|BunsetuPositionType=SEM_HEAD|NP_B|Reading=ブンショ
2       の      の      ADP     助詞-格助詞     _       1       case    _       SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=SYN_HEAD|Reading=ノ
3       形態素  形態素  NOUN    名詞-普通名詞-一般      _       4       compound        _       SpaceAfter=No|BunsetuBILabel=B|BunsetuPositionType=CONT|NP_B|Reading=ケイタイソ
4       解析    解析    NOUN    名詞-普通名詞-サ変可能  _       6       obj     _       SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=SEM_HEAD|NP_I|Reading=カイセキ
5       を      を      ADP     助詞-格助詞     _       4       case    _       SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=SYN_HEAD|Reading=ヲ
6       し      する    VERB    動詞-非自立可能 _       0       root    _       SpaceAfter=No|BunsetuBILabel=B|BunsetuPositionType=ROOT|Inf=サ行変格,連用形-一般|Reading=シ
7       て      て      SCONJ   助詞-接続助詞   _       6       mark    _       SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=SYN_HEAD|Reading=テ
8       み      みる    VERB    動詞-非自立可能 _       7       fixed   _       SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=CONT|Inf=上一段-マ行,連用形-一般|Reading=ミ
9       た      た      AUX     助動詞  _       6       aux     _       SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=SYN_HEAD|Inf=助動詞-タ,終止形-一般|Reading=タ
10      よ      よ      PART    助詞-終助詞     _       6       mark    _       SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=SYN_HEAD|Reading=ヨ

コマンド1つで上記のように形態素解析が実施できます。
ここで1つ補足があります。
ja-ginzaja-ginza-electra をインストールしている場合、特に指定しないと ja-ginza-electra で解析が実行されます。
モデルの指定をしたい場合は ginza -m [モデル名] で指定することができ、spaCyが提供している様々な言語モデルを(インストールしていなかった場合)ダウンロード・解析の実行までしてくれます。
他にも ginzame というコマンドが用意されており、これを使うとMeCab形式で先ほどの結果が出力されます。

ginzameの出力結果

文書の形態素解析をしてみたよ
文書    名詞,普通名詞,一般,*,*,*,文書,ブンショ,*
の      助詞,格助詞,*,*,*,*,の,ノ,*
形態    名詞,普通名詞,一般,*,*,*,形態,ケイタイ,*
素      接尾辞,名詞的,一般,*,*,*,素,ソ,*
解析    名詞,普通名詞,サ変可能,*,*,*,解析,カイセキ,*
を      助詞,格助詞,*,*,*,*,を,ヲ,*
し      動詞,非自立可能,*,*,サ行変格,連用形-一般,為る,シ,*
て      助詞,接続助詞,*,*,*,*,て,テ,*
み      動詞,非自立可能,*,*,上一段-マ行,連用形-一般,見る,ミ,*

MeCabに慣れている人にとってはこれは嬉しいかもしれませんね。

Pythonで解析処理を試してみる

次に、公式ドキュメントを参考にしてPythonで解析処理を実行してみました。

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')

今回はColabのCPU環境を使っているため、処理が重くならないように従来モデルの ja_ginza モデルを指定しました。
こちらの実行結果は以下のようになりました。

/usr/local/lib/python3.8/dist-packages/torch/cuda/__init__.py:497: UserWarning: Can't initialize NVML
  warnings.warn("Can't initialize NVML")
0 文書 文書 文書 ['ブンショ'] NOUN [] 名詞-普通名詞-一般 nmod 3
1 の の の ['ノ'] ADP [] 助詞-格助詞 case 0
2 形態素 形態素 形態素 ['ケイタイソ'] NOUN [] 名詞-普通名詞-一般 compound 3
3 解析 解析 解析 ['カイセキ'] NOUN [] 名詞-普通名詞-サ変可能 obj 5
4 を を を ['ヲ'] ADP [] 助詞-格助詞 case 3
5 し する 為る ['シ'] VERB ['サ行変格;連用形-一般'] 動詞-非自立可能 ROOT 5
6 て て て ['テ'] SCONJ [] 助詞-接続助詞 mark 5
7 み みる 見る ['ミ'] VERB ['上一段-マ行;連用形-一般'] 動詞-非自立可能 fixed 6
8 た た た ['タ'] AUX ['助動詞-タ;終止形-一般'] 助動詞 aux 5
9 よ よ よ ['ヨ'] PART [] 助詞-終助詞 mark 5
EOS

ColabのCPU環境で実行した場合にNVML(NVIDIA Management Library)のWarningが発生しますが、プログラム自体は問題なく実行されました。
GPU環境で実行したところこのWarningメッセージは消えましたので、動作には問題なさそうですが上記メッセージが気になる方はGPU環境での実行をおすすめいたします。
(このメッセージが出る原因等が分かりましたら、またブログに追記・もしくはその話でブログを書こうと思います)

文節APIを試してみた

v4.0以降に導入された文節APIを試して、構文解析をやっていきたいと思います。

今回、ドキュメントに載っていたAPIを3つほど試したのでコードと出力結果、結果の簡単な解説を載せていきます。

構文解析処理

import spacy
import ginza
nlp = spacy.load('ja_ginza')
doc = nlp('文書の形態素解析をしてみたよ')
print(ginza.bunsetu_spans(doc))
print(ginza.bunsetu_phrase_spans(doc))
for phrase in ginza.bunsetu_phrase_spans(doc):
  print(phrase, phrase.label_)

sentence = list(doc.sents)[0]
for sentence in doc.sents:
  for relation, bunsetu in ginza.sub_phrases(sentence.root, ginza.bunsetu):
    print(relation, bunsetu)
  print("root", ginza.bunsetu(sentence.root))

構文解析処理の結果

[文書の, 形態素解析を, してみたよ]
[文書, 形態素解析, し]
文書 NP
形態素解析 NP
し VP
obj 形態素+解析+を
root し+て+み+た+よ

結果の解説

  • ginza.bunsetu_spans:文節ごとに区切られた文書を返す
  • ginza.bunsetu_phrase_spans:文節ごとの主辞(主要語)
  • phrase.label_:主辞がもつラベル
  • ginza.sub_phrases:文節の係り先に関する依存関係を返す。「してみたよ」というroot(親)に係るobj(目的語)が「形態素解析を」となっている

APIで手軽に欲しかった情報を得ることができました。

おまけ:文節構造を可視化してみた

手軽に構文解析を実施して結果を出力できたのですが、分かりやすくするために構文木の図にしてみようと思い、MacBookのマジックパッドで指を攣りながらふと思いました。

「指を痛めずに文節構造を可視化したい……」

そこで色々と調べてみたところ、嬉しいものを見つけました。

spaCyの可視化機能と、PythonでWebアプリケーションを作成するフレームワーク Streamlit を組み合わせた spacy-streamlit です。

上記リポジトリを参照しながら、今回はローカル環境からspacy-streamlitを使って文節構造を可視化してみました。

インストール方法

pipで簡単にインストールができます。

pip install spacy-streamlit

プログラムを作成し実行する

以下プログラムを作成し、実行しました。

test.py

import spacy_streamlit

models = ["ja_ginza"]
text = "文書の形態素解析をしてみたよ"
spacy_streamlit.visualize(models, text)

実行コマンド

streamlit run test.py

実行すると以下ブラウザページが開きます。


▲ 上記ページ上でまた別の文書の文節構造を可視化できます、便利

実際に「文書の形態素解析をしてみたよ」という文書の文節構造の解析結果は、以下のような画像になりました。


▲ 指も攣らないし綺麗だし良いことずくめです

便利なライブラリを使いこなしたいし中身について詳しくなりたい

実際にpipでサクッとライブラリを導入して形態素解析や構文解析ができました。

試したい、と思ったことが簡単に試せて結果も得られる(実は今回、色々とトラブルが発生し導入に恐ろしく時間がかかってしまったのですが、そこさえ片付いてしまえば検証〜可視化部分は1時間程度でできてしまいました)と、そこ以外の自然言語処理の仕組みや理論の勉強に時間が割けるのでとても私としては嬉しいです。

また色々な便利なライブラリを使ったり、その裏側にある仕組みについて勉強したことをブログにしていきたいと思います。

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