Pythonで日本語テキストを解析する【GiNZA】

Pythonで日本語テキストを解析する

日本語テキストを形態素解析係り受け関係解析を行うためのPythonライブラリを探していました。

今回見つけたGiNZAを試してみたので、その仕組みと使い方を紹介します。

megagonlabs.github.io

形態素解析とは

形態素とは意味を持つ最小の言語単位であり、形態素解析とは与えられた文を形態素単位に区切り、各形態素に品詞などの情報を付与する処理である [1]

係り受け関係解析とは

係り受けとは、文節間の意味的な修飾関係のことであり、格関係など意味的な関係を捉える上で重要である [1]

モチベーション

「レシピ構造化をやりたい」というのが、個人的なモチベーションです。 材料リストから主材料・主調理工程を抽出したり、調理フローグラフを取得したりです。

GiNZA

GiNZAとは、リクルートMegagon研究所と国立国語研究所が共同開発を行っているPure Pythonの日本語NLPライブラリです。 spaCyという、Explosion AI社の開発する自然言語処理ライブラリをベースとしています。 spaCyのパイプラインは、 Tagger→Parser→Nerとなっています。 GiNZAでは、その前後に処理を加えることで、日本語対応させています。

GiNZAによる日本語自然言語処理

形態素解析について、英語だとスペースで分割すればいいので、品詞分類から始まります。 しかし、日本語の場合、辞書にある単語を認識・分割する(トークン化)という処理が必要になります。

(日本語のように自立語に複数の付属語が分かち書きされずに結合される構造を持つ言語を「膠着語」と言うそうです。)

そのためGiNZAでは、SudachiPyという日本語トークン化の処理(Tokenizer)をTaggerの前に追加されています。

また、依存関係ラベリングと用法曖昧性解決処理(CompoundSplitter, BunsetuRecognizer)を最後に追加することで、解析性能を向上されています。[3]

使用方法

インストール

pip install ginza

調べると、-Uのオプションを追加したりするコマンドが出てきましたが、 最終的にこのコマンドだけでインストールができました。

解析処理

import spacy
nlp = spacy.load("ja_ginza")
doc = nlp(
    "ひき肉に、炒めて冷ましたたまねぎ、パン粉と牛乳と卵を混ぜる。"
)

これだけです。すごく簡単ですね。

instruction_list = [
  "ひき肉に、炒めて冷めたたまねぎ、パン粉と牛乳と卵を混ぜる。",
  "小判の形に成形し、冷蔵庫で1時間寝かせる。",
  "熱したフライパンにサラダ油を入れて焼く。"
]
docs = list(nlp.pipe(instruction_list))

文字列リストをこのように入力することもできます。

“内部的にバッチ化されるので、より効率的な処理が可能” [3]だそうです。

結果表示

コード:

print('\n'.join([token.text for token in doc]))

出力:

ひき肉
に
、
炒め
て
冷まし
た
たまねぎ
、
パン粉
と
牛乳
と
卵
を
混ぜる
。
  • 名詞抽出

コード:

print(', '.join([n.text for n in doc.noun_chunks]))

出力:

ひき肉, 炒めて冷ましたたまねぎ, パン粉, 牛乳, 卵
  • 固有表現

コード:

for ent in doc.ents:
  print(ent.text, ent.start_char, 
        ent.end_char, ent.label_)

出力:

たまねぎ 12 16 Food_Other
パン粉 17 20 Food_Other
牛乳 21 23 Food_Other
卵 24 25 Food_Other
  • 単語ベクトル

コード:

token = doc[3]
print(token)
print(token.vector)

出力:

炒め
[ 0.08003599 -0.12529491  0.41058803 -0.02664721  0.04041548  0.78141636
 -0.23311277 -0.15073359  0.00955659 -0.5930606   0.34050038  0.1425334
  0.15794998  0.37364605 -0.00280757 -0.24731137 -0.01622627 -0.17575407
・・・
 -0.40049294  0.32097352  0.05157278 -0.0260973  -0.20139831  0.08696187
  0.4490269  -0.05321052 -0.29156387  0.08571155  0.05183061  0.42766365
  0.01349372  0.45254493  0.304076   -0.24617903  0.01391126  0.3111292 ]

コード:

for sent in doc.sents:
    for token in sent:
          print(ent.text, ent.start_char, 
                   ent.end_char, ent.label_)

出力:

0 ひき肉 ひき肉 NOUN obl 15
1 に に ADP case 0
2 、 、 PUNCT punct 0
3 炒め 炒める VERB advcl 5
4 て て SCONJ mark 3
5 冷まし 冷ます VERB acl 7
6 た た AUX aux 5
7 たまねぎ たまねぎ NOUN obl 15
8 、 、 PUNCT punct 7
9 パン粉 パン粉 NOUN nmod 11
10 と と ADP case 9
11 牛乳 牛乳 NOUN nmod 13
12 と と ADP case 11
13 卵 卵 NOUN obj 15
14 を を ADP case 13
15 混ぜる 混ぜる VERB ROOT 15
16 。 。 PUNCT punct 15

コード:

from spacy import displacy

for sent in doc.sents:
    svg = displacy.render(sent, style="dep", options={"compact":True})

出力: f:id:harukary7518:20210825181221p:plain

結論

日本語の自然言語処理を数行のコードで素早く行えるGiNZAを紹介しました。 また、以下のリンク [3]では、単語ベクトルなどデータセットを入れ替えたり、各処理で手法を入れ替えたりしており、カスタマイズ性も高いといえます。

個人的には、係り受け関係木をもとに調理フローグラフを取得できるか試してみようと思います。 少し試した範囲では、レシピ手順の文章は主語がなく、崩れているため難しいかもしれません。

また、単語のベクトル表現について、Word2Vecを試してみようかなと思いました。

例えば、「焼く+水=煮る・茹でる」になるのかなど。 ただ、「水を入れて火にかける」という説明を学習データに含めないと、マッピングができないかもなと思っています。

参考文献

[1] Python による日本語自然言語処理

[2] NLP2019 松田寛 - GiNZA

[3] はじめての自然言語処理 spaCy/GiNZA を用いた自然言語処理 | オブジェクトの広場