ユーニックス総合研究所

  • home
  • archives
  • spacy-nouns

spaCyとGiNZAで名詞を抽出する【自然言語処理, Python】

spaCyとGiNZAで名詞を抽出する

私たちの話す言葉は「自然言語」と言いますが、これを計算機に処理させることを「自然言語処理」と言います。
自然言語処理を行うライブラリは沢山ありますが、今回はその中でもPythonのライブラリであるspaCyを使って見たいと思います。

今回はspaCyを使って日本語の文章から名詞を取り出してみます。
具体的にはspaCyについて↓を見ていきたいと思います。

  • spaCyとは?
  • GiNZAとは?
  • spaCyとGiNZAのインストール
  • spaCyの使い方を調べる
  • spaCyで名詞を抽出する
  • spaCyで名詞、代名詞、固有名詞を抽出する
  • spaCyで名詞句を抽出する

spaCyとは?

spaCy(スパイシー)とは自然言語処理を行うライブラリです。
PythonとCythonで書かれており、Pythonから使うことができます。
オープンソースのソフトウェアで、MITライセンスで公開されており、商用利用も可能です。

また英語、ドイツ語、スペイン語などといった色々な言語において学習済み統計モデルと単語ベクトルを利用することができます。
プロダクトの製品化を意識した開発がされているのが特徴です。

spaCyを使えば日本語の文章を解析して単語ごとに分割したり(字句解析)、単語間の依存関係を見たり(構文解析)することが可能になるということです。
つまりspaCyを使えば機械学習に使用するデータを簡単に用意出来たり、あるいは自然言語処理として文章を解析することが出来るようになるということになります。

Pythonから利用するにはpipでインストールすればすぐに使うことができます。
インストール方法については後述します。

GiNZAとは?

spaCyは内部的には学習済み統計モデルを使って解析を行います。
最近までこの学習済みモデルに日本語のモデルが無い状態でした。
つまりspaCyで日本語の文章について深い解析が出来ない状態が続いていました。

しかし、GiNZAの登場によってspaCyの日本語対応は大きく変わりました。
GiNZAはリクルートと国立国語研究所が開発したライブラリで字句解析や構文解析を行えます。
つまりspaCyからGiNZAのモデルをロードすることでGiNZAを使った日本語の解析ができるということになります。

GiNZAはオープンソースなライブラリで、MITライセンスで利用することが可能です。

spaCyとGiNZAのインストール

spaCyとGiNZAはともにPythonのライブラリで、pipを使えばすぐにインストールすることができます。

GiNZAをインストールするには↓のコマンドを実行します。

$ pip install "https://github.com/megagonlabs/ginza/releases/download/latest/ginza-latest.tar.gz"  

spaCyをインストールするには↓のコマンドを実行します。

$ pip install spacy  

GiNZAのバージョンによってはspaCyの実行時に警告が出ることがあります。
例えば↓のような警告です。

UserWarning: [W031] Model 'ja_ginza' (2.2.0) requires spaCy v2.2 and is incompatible with the current spaCy version (2.3.5). This may lead to unexpected results or runtime errors. To resolve this, download a newer compatible model or retrain your custom model with the current spaCy version. For more details and available updates, run: python -m spacy validate  
  warnings.warn(warn_msg)  

↑のようにバージョンによる警告が出た場合はspaCyのインストールバージョンを指定します。
↑の場合はインストールしたGiNZA(2.2.0)がspaCy(2.3.5)に対応しておらず、spaCy(2.2)にしてくれと言ってるので↓のようにします。

$ pip uninstall spacy  
$ pip install spacy==2.2  

spaCyの使い方を調べる

spaCyのAPIのドキュメントは↓を参照します。

DocTokenの属性の説明などについては↑から調べればわかります。英語なので機械翻訳を活用しましょう。

🦝 < 自然言語だけに

spaCyで名詞を抽出する

それではspaCyで実際に日本語の文章から名詞を抽出してみたいと思います。
spaCyはお決まりとして最初にspacyをインポートして、モデルをロードしておく必要があります。
今回はロードするモデルはGiNZAのモデルです。
↓のようにします。

import spacy  

nlp = spacy.load('ja_ginza')  # モデルのロード  

load()の結果は慣例としてnlpという変数で受け取っておきます。
このnlpを操作することで自然言語処理を行えます。
ja_ginzaをロードした場合はこのnlpginza.Japaneseクラスになります。

日本語の文章を解析させるには↓のようにnlp()に文章を渡します。

doc = nlp('アマゾンの奥地で彼は毒キノコを取った。')  # 解析を行う  

nlp()に文章を渡すと解析が行われ、その結果が返り値として返ってきます。
この返り値は慣例的にdocとするのが一般的みたいです。
このdocspacy.tokens.doc.Docクラスです。

docfor文で回すとトークン列のジェネレーターを生成してくれます。
したがってdocfor文で回すと↓のようにトークンを取り出すことができます。

import spacy  

nlp = spacy.load('ja_ginza')  
doc = nlp('アマゾンの奥地で彼は毒キノコを取った。')  

for tok in doc:  
    print(  
        tok.i,  # 親ドキュメント内のトークンのインデックス。  
        tok.text,  # 逐語的なテキストコンテンツ  

        # 逐語的なテキストコンテンツ(Token.textと同じ)  
        # 主に他の属性との一貫性のために存在します。  
        tok.orth_,  

        tok.lemma_,  # 語尾変化のない接尾辞のない、トークンの基本形式  
        tok.pos_,  # 品詞(英語の大文字)  
        tok.tag_,  # きめの細かい品詞  
        tok.head.i, # headはこのトークンの構文上の親、または「ガバナー」  
        tok.dep_,  # 構文従属関係  

        # トークンの基準、つまりトークンテキストの正規化された形式  
        # 通常、言語のトークナイザー例外またはノルム例外で設定されます。  
        tok.norm_,  

        # 名前付きエンティティタグのIOBコード  
        # 「B」はトークンがエンティティを開始することを意味し、「I」はそれがエンティティの内部にあることを意味し、  
        # 「O」はそれがエンティティの外部にあることを意味し、「」はエンティティタグが設定されていないことを意味します。  
        tok.ent_iob_,  
        tok.ent_type_,  # 名前付きエンティティタイプ  
    )  

docfor文で回すとspacy.tokens.token.Tokenを返してくれます。
このTokenには↑のようにさまざまな属性があり、それらの属性を参照することでその単語が持つ情報を参照することができます。
つまり今回はこのトークンの属性を参照して名詞を抽出してみようという話です。

tok.textなどの「逐語的なテキストコンテンツ」というのはなんか難しい表現ですが、要は表層形のことだと思われます。
表層形とは元の文章のそのままの表記の文字列のことです。

↑のコードを実行すると↓のような結果になります。

0 アマゾン アマゾン アマゾン PROPN 名詞-固有名詞-地名-一般 2 nmod アマゾン B LOC  
1 の の の ADP 助詞-格助詞 0 case の O  
2 奥地 奥地 奥地 NOUN 名詞-普通名詞-一般 9 nmod 奥地 O  
3 で で で ADP 助詞-格助詞 2 case で O  
4 彼 彼 彼 PRON 代名詞 9 nsubj 彼 O  
5 は は は ADP 助詞-係助詞 4 case は O  
6 毒 毒 毒 NOUN 名詞-普通名詞-一般 7 compound 毒 O  
7 キノコ キノコ 茸 NOUN 名詞-普通名詞-一般 9 obj キノコ O  
8 を を を ADP 助詞-格助詞 7 case を O  
9 取っ 取っ 取る VERB 動詞-一般 9 ROOT 取っ O  
10 た た た AUX 助動詞 9 aux た O  
11 。 。 。 PUNCT 補助記号-句点 9 punct 。 O  

↑のうち、tok.pos_を参照して得られるPROPNADP, NOUNなどがそのトークンの品詞を表しています。
品詞を参照することでそのトークンが名詞かどうか判別することができます。
「名詞」の場合、NOUNというのがそれに当たります。
つまりtok.pos_の値がNOUNのトークンを保存しておけば名詞のトークンを抽出できるということになります。
↓のようにです。

import spacy  

nlp = spacy.load('ja_ginza')  # モデルのロード  
doc = nlp('アマゾンの奥地で彼は毒キノコを取った。')  # 文章の解析  

# NOUN(名詞)のトークンを抽出  
noun_toks = []  
for tok in doc:  
    if tok.pos_ == 'NOUN':  
        noun_toks.append(tok)  

# 抽出したトークン列を表示する  
for tok in noun_toks:  
    print(tok.text)  

↑のコードを実行すると↓のような結果になります。

奥地  
毒  
キノコ  

tok.pos_NOUNPROPNの意味を調べるには以前は↓のドキュメントが有効でした。

しかし現在はドキュメントの仕様が変わったみたいで、参照できなくなっています。
第3者の人が作成したQiitaのドキュメントがありますのでこちらを参照してみてください。

spaCyで名詞、代名詞、固有名詞を抽出する

さきほどの名詞を抽出した要領で今度は名詞、代名詞、固有名詞のトークンを抽出してみたいと思います。
名詞はpos_の値ではNOUNで、代名詞はPRON, そして固有名詞はPROPNです。

  • NOUN ... 名詞
  • PRON ... 代名詞
  • PROPN ... 固有名詞

これらのトークンを抽出するコードを書くと↓のようになります。

import spacy  

nlp = spacy.load('ja_ginza')  # モデルのロード  
doc = nlp('アマゾンの奥地で彼は毒キノコを取った。')  # 文章の解析  

# 名詞、代名詞、固有名詞のトークンを抽出  
noun_toks = []  
for tok in doc:  
    if tok.pos_ in ('NOUN', 'PRON', 'PROPN'):  
        noun_toks.append(tok)  

# 抽出したトークン列を出力  
for tok in noun_toks:  
    print(tok.text, tok.pos_)  

↑のコードを実行すると↓のような結果になります。

アマゾン PROPN  
奥地 NOUN  
彼 PRON  
毒 NOUN  
キノコ NOUN  

spaCyで名詞句を抽出する

spaCyでは名詞句を抽出することができます。
名詞句を抽出するにはdoc.noun_chunksを参照します。

import spacy  

nlp = spacy.load('ja_ginza')  # モデルのロード  
doc = nlp('アマゾンの奥地で彼は毒キノコを取った。')  # 文章の解析  

# 名詞句を抽出  
for span in doc.noun_chunks:  
    print(span)  

↑のコードを実行すると↓のような結果になります。

アマゾン  
奥地  
彼  
毒キノコ  

「毒キノコ」が「毒」と「キノコ」にわけられずに1つの名詞句として取得できてるのがわかります。
ちなみにnoun_chunksが生成するオブジェクトはspacy.tokens.span.Spanです。

おわりに

今回はspaCyとGiNZAを使って日本語の文章から名詞を抽出してみました。
spaCyとGiNZAを使えば簡単に日本語の文章を解析できそうです。

🦝 < 簡単と言っても意味解析以上のことはけっこうしんどそう

🐭 < それはどこでもいっしょ

🦝 < NLPウォリアーに栄光あれ