spaCyで目的語を抽出する【自然言語処理, Python】
- 作成日: 2021-03-02
- 更新日: 2023-12-24
- カテゴリ: 自然言語処理
spaCyで目的語を抽出する
私たちが使う言語は「自然言語」と言います。
そしてその自然言語をプログラム的に解析することを「自然言語処理」と言います。
Pythonには自然言語処理を行うライブラリspaCy(スパイシー)があります。
今回はこのspaCyを使って日本語の文章から目的語を抽出するプログラムを作ってみたいと思います。
具体的には↓を見ていきます。
- spaCyとは?
- 目的語とは?
- spaCyの基本的な使い方
- spaCyで目的語を抽出
関連記事
spaCyの品詞ラベル(POS)一覧(日本語訳)【自然言語処理, Python】
spaCyのis_punctで句読点かどうか調べる【自然言語処理, Python】
spaCyのdisplacy.serve()の使い方【自然言語処理, Python】
spaCyとGiNZAで名詞を抽出する【自然言語処理, Python】
spaCyで虫食い文章を穴埋めする【自然言語処理, Python】
spaCyとは?
spaCyとはプログラミング言語のPythonとCythonで開発されたライブラリです。
自然言語を解析することができます。
さまざまな言語で学習済み統計モデルを使うことが出来ます。
オープンソースで、MITライセンスで利用することができます。
spaCyではGiNZAによる統計モデルをロードすることで、日本語の文章を解析することができます。
具体的には字句解析、構文解析などです。
GiNZAは日本語処理のための自然言語ライブラリです。
GiNZAはリクルートと国立国語研究所の共同研究で生まれました。
spaCyによる日本語の文章の解析では、基本的にはspaCyとGiNZAのモデルを組み合わせて使います。
目的語とは?
目的語とは、動詞などの動作の影響を受ける単語を指します。
助詞の「を」が付く単語です。
直接目的語と間接目的語に区別されることがあります。
たとえば「私は猫を撫でた」という文章では、目的語は「猫」です。
「撫でた」という動作を受けるのが「猫」だからです。
ほかにも「猫は私を舐めた」という文章では、目的語は「私」になります。
「舐めた」という動作を受けるのが「私」だからです。
spaCyの基本的な使い方
spaCyを使うにはspacy
モジュールをインポートします。
それから統計モデルをロードします。
ここではGiNZAの統計モデルをロードします。
import spacy
nlp = spacy.load('ja_ginza')
spacy.load()
の返り値には慣例的にnlp
と命名します。
nlp
に文章を渡すとその文章を解析することができます。
doc = nlp('私は猫を撫でた')
nlp
の返り値には慣例的にdoc
と命名します。
このdoc
のメソッドや属性を使うことで解析した結果を参照することが可能です。
たとえばdoc.sents
を参照すると、文章全体のセンテンスを取得できます。
import spacy
nlp = spacy.load('ja_ginza')
doc = nlp('私は猫を撫でた。猫も私を舐めた。')
for sent in doc.sents: # センテンスを参照
print(sent)
↑のコードを実行すると↓のような結果になります。
私は猫を撫でた。
猫も私を舐めた。
spaCyで目的語を抽出
spaCyは文章の依存構造を解析します。
依存構造はたとえば単語間の係り受けの関係などです。
中でもspaCyは依存構造の解析で、単語にラベルを振ってその単語の依存構造における役割を保存します。
これはトークンのdep
属性がそうです。
この属性を参照することにより、その単語が依存構造においてどういう役割を持っているか知ることができます。
たとえばトークンのdep
がobj
であれば、そのトークンは目的語の役割を持っているという具合です。
spaCyでトークン列を参照するにはdoc
をfor
文で回します。
そしてトークンのdep
を参照するには↓のようにします。
for token in doc:
print(token.dep)
目的語を表すobj
は、spacy.symbols
モジュール内に定義されています。
from spacy.symbols import obj
dep
の値をobj
と比較することでそのトークンが目的語かどうか判定できます。
for token in doc:
print(token.dep == obj)
つまり目的語であるトークンのテキストを表示するコードは↓のようになります。
import spacy
from spacy.symbols import obj
nlp = spacy.load('ja_ginza') # モデルをロード
doc = nlp('私は猫を撫でた') # 文章を解析
for tok in doc:
if tok.dep == obj: # トークンが目的語なら
print(tok.text) # テキストを表示
↑のコードを実行すると↓のような結果になります
猫
質問への回答
質問をいただきました。
{猫:撫でた}と表示したいです。
「撫で」が動詞(VERB)になるので、「撫で」で引っかけて取得することができます。
それから「撫でた」の「た」は助動詞(AUX)になりますのでこれも引っかけます。
import spacy
from spacy.symbols import obj, VERB, AUX
nlp = spacy.load('ja_ginza') # モデルをロード
doc = nlp('私は猫を撫でた') # 文章を解析
s = ''
for tok in doc:
if tok.dep == obj: # トークンが目的語なら
print(tok.text) # テキストを表示
s += tok.text + ': '
elif tok.pos == VERB or tok.pos == AUX: # トークンが動詞または助動詞なら
print(tok.text) # テキストを表示
s += tok.text
# 「撫で」は動詞、「た」は助動詞になる
print(s) # 猫: 撫でた
ただこの方法は汎用的ではありません。
目的語や動詞が複数登場した場合は取得結果がおかしくなる可能性があります。
依存構造を親子関係からたどる方法もあります。
import spacy
from spacy.symbols import obj, VERB, AUX
# import deplacy
nlp = spacy.load('ja_ginza') # モデルをロード
doc = nlp('私は猫を撫でた') # 文章を解析
# deplacy.render(doc)
s = ''
for tok in doc:
if tok.dep == obj:
s += tok.text + ': '
s += tok.head.text
s += list(tok.head.rights)[0].text
print(s) # 猫: 撫でた
tok.head
で親のトークン、tok.rights
で右の子供(ジェネレータ)を参照できます。
この方法も汎用性があるかと言われると微妙ですが、もっとコードを複雑にすればある程度汎用的にできると思います。
doc
の木構造を視覚的に見たい場合は、deplacy
という便利ライブラリがあります。
pip install deplacy
でインストールできます。
これを使うと↓のように表示できます。
私 PRON ═╗<══╗ nsubj
は ADP <╝ ║ case
猫 NOUN ═╗<╗ ║ obj
を ADP <╝ ║ ║ case
撫で VERB ═╗═╝═╝ ROOT
た AUX <╝ aux
おわりに
今回はspaCyで目的語を抽出してみました。
目的語がわかると動詞の行為の対象を得ることができます。
「だれが『だれに』やった」の「だれに」の部分がわかるということになります。
この記事が参考になれば幸いです。
🦝 < もっと撫でて
🐭 < 目的語がしゃべった