spaCyで依存構造を自力で出力する【自然言語処理】
- 作成日: 2021-03-02
- 更新日: 2023-12-24
- カテゴリ: 自然言語処理
spaCyで依存構造を出力する
今回は自然言語処理ライブラリであるspaCyを使い日本語の文章を解析し、その依存構造を出力するということをPythonでやってみたいと思います。
日本語の文章の依存構造を自力で出力することにより、spaCyによる依存構造の走査を把握することができます。
再帰的にトークンの子供を走査して、そのトークンの持つテキストを出力します。
今回は具体的には↓を見ていきます。
- spaCyとは?
- 依存関係とは?
- spaCyの基本的な使い方
- deplacyによる依存構造の出力
- 自力で依存構造の出力
spaCyとは?
spaCy(スパイシー)とはExplosion AIが開発している自然言語処理ライブラリです。
PythonとCythonで書かれています。
オープンソースなプロジェクトで、MITライセンスで利用することができます。
様々な自然言語の学習済み統計モデルを利用し、文章を解析することができます。
spaCyで日本語の文章を解析する場合は統計モデルにGiNZAのモデルを使います。
GiNZAはリクルートと国立国語研究所の共同研究で生まれた自然言語ライブラリです。
依存関係とは?
文章における依存関係とは、単語や文節間の依存関係のことです。
依存関係とは修飾/被修飾関係や係り受け関係のことを言います。
文章の依存関係を解析することを依存構造解析と言い、依存構造は通常はツリー構造で表現されます。
この木構造は単語や文節をノードとして持っています。
文節や単語は修飾/被修飾の親子関係で表現されます。
依存構造を解析することによってどの単語がどの単語に係っているかとか、どの単語がどの単語を修飾しているかなどがわかります。
自然言語処理ではこの依存構造を扱うことによって意味解析や文脈解析に発展するということになります。
spaCyの基本的な使い方
spaCyはPythonではspacy
モジュールをインポートして使います。
import spacy
それから最初に統計モデルのロードを行います。
今回はGiNZAの統計モデルを使いますので、spacy.load()
に文字列ja_ginza
を渡します。
nlp = spacy.load('ja_ginza')
spacy.load()
の返り値はja_ginza
を読み込んだ場合はginza.Japanese
クラスが返ってきます。
この返り値には慣例的にnlp
と命名します。
nlp
に文字列を渡すと解析を行うことができます。
doc = nlp('棚の上にあるミカン')
オブジェクトにしたnlp
は慣例的にdoc
と命令されます。
このdoc
から解析した結果にアクセスすることができます。
たとえばdoc
をfor
文で回すとトークン列を取得できます。
import spacy
nlp = spacy.load('ja_ginza') # モデルのロード
doc = nlp('棚の上にあるミカン') # 文章解析
for tok in doc: # トークン列の抜き出し
print(tok.text)
↑のコードを実行すると↓のような結果になります。
棚
の
上
に
ある
ミカン
deplacyによる依存構造の出力
deplacy
というモジュールを使うとspaCyの解析した構造を簡単に画面に出力することができます。
pipでインストールが必要です。
$ pip deplacy
deplacy
を使うにはdeplacy
モジュールをインポートします。
それからdeplacy.render()
にdoc
を渡します。
import spacy
import deplacy
nlp = spacy.load('ja_ginza') # モデルのロード
doc = nlp('棚の上にあるミカン') # 文章の解析
deplacy.render(doc, Japanese=True) # 依存構造の出力
↑のコードを実行すると↓のような結果になります。
棚 NOUN ═╗<╗ nmod(体言による連体修飾語)
の ADP <╝ ║ case(格表示)
上 NOUN ═╗═╝<╗ iobj(間接目的語)
に ADP <╝ ║ case(格表示)
ある VERB ═════╝<╗ acl(連体修飾節)
ミカン NOUN ═══════╝ ROOT(親)
↑のように矢印で依存関係が表現されたテキストが出力されます。
自力で依存構造の出力
nlp
で解析したdoc
から木構造のトークン列を参照することができますが、このトークン列は各トークンごとにその依存先のトークンを持っています。
これは例えばtoken.children
などで参照することができます。
import spacy
nlp = spacy.load('ja_ginza') # モデルのロード
doc = nlp('棚の上にあるミカン') # 文章の解析
for token in doc:
print(token.children) # トークンの持つ子供を参照
↑のコードを実行すると↓のような出力が得られます。
<generator object at 0x7f3256a04550>
<generator object at 0x7f3256a04550>
<generator object at 0x7f3256a04550>
<generator object at 0x7f3256a04550>
<generator object at 0x7f3256a04550>
<generator object at 0x7f3256a04550>
token.children
の参照でジェネレーターが返ってきてますね。
token.children
をlist()
に渡すと、
for token in doc:
print(list(token.children)) # トークンの持つ子供を参照
↓のように子要素のトークンが出力されます。
[の]
[]
[棚, に]
[]
[上]
[ある]
トークン自体のテキストも出力すると↓のようになります。
for token in doc:
print(token.text, list(token.children)) # トークンの持つ子供を参照
棚 [の]
の []
上 [棚, に]
に []
ある [上]
ミカン [ある]
先ほどの依存構造のツリーと見比べると、矢印の通りに子供が生成されているのがわかります。
このtoken.children
をlist()
に渡した結果は、トークン列です。
そのためこのトークンを参照することで再帰的に子供を辿ることができます。
これを利用して再帰関数を定義すれば、非常にチープな木構造の出力器を作ることができます。
↓のように定義します。
import spacy
nlp = spacy.load('ja_ginza') # モデルのロード
doc = nlp('棚の上にあるミカン') # 文章の解析
def show_tree(token, dep=0):
"""
再帰的にtoken.childrenを辿る再帰関数
"""
print(dep * '_' + token.text)
for child in token.children:
show_tree(child, dep + 1)
# docからセンテンスを取り出しルート要素から木構造を出力する
for sent in doc.sents:
show_tree(sent.root)
↑のコードを実行すると↓のような出力になります。
ミカン
_ある
__上
___棚
____の
___に
doc.sents
で文章全体をセンテンスごとに取り出すことができます。
そしてこのセンテンスはroot
という属性を持っています。
これはツリー構造におけるルートのトークンです。
show_tree()
という関数にこのルートのトークンを渡し、show_tree()
内ではトークンのchildren
を参照します。
children
のトークンを再帰的にshow_tree()
に渡して、dep
というカウント変数を増加させます。
dep
の値だけインデントしてトークンのtext
を出力します。
おわりに
今回はspaCyで依存構造のツリー構造を出力してみました。
依存構造の出力はtoken.children
を参照すれば簡単に行えます。
🦝 < 依存関係を制する者は
🐭 < NLPを制す