以前、「【超簡単】windowsのpythonとmecabで形態素解析しようぜ!」 の記事と「 【超簡単】windowsのpythonとjanomeで形態素解析しようぜ!」 の記事で形態素解析についての方法を紹介しました。
形態素解析を行うことで、単語の区切りが分りづらい日本語文書から、簡単に品詞を取り出す事ができるようになります。
この結果を集計したり、ワードクラウドによる可視化を行うことで、文書全文を読まずとも何が掛かれているのかの大筋を把握することができます。
しかし、例えば「サポート」「画質」「良い」「悪い」という4つの単語が取り出された場合、「サーポート」と「画質」のどちらが良くて、どちらが悪いのかが判断つきません。
係り受け解析を行うことで、「良い」は「サポート」と「画質」のどちらに係っているのかが分かるようになり、文書の理解が随分やり安くなります。
今までと同様、今回もクラス化しているので、コピペですぐにお試し頂けるようにしています。
係り受け解析に興味のある方は、是非ご一読ください。
係り受け解析の概要
冒頭でも申しました通り、係り受けとは「文中の言葉同士の関係性」の事です。
日本語に限らず、文書にはどちらとも解釈できるケースも多々あるため、係り受けにより品詞の前後関係を分析する必要があります。
では次の文章はどうでしょう?
毛が長く黒目の大きな犬
文章からだと「黒目の大きな犬」なのか「大きな犬」なのかが判断しづらいですね。
このようなケースも解析はしてくれますが、意図した結果が得られないかもしれません。
この辺は人それぞれの解釈が異なるので、たとえ係り受けができたとしても、最終的に前後の文書を人が見て判断せざるを得ないでしょう。
GiNZAとは
GiNZAとは、リクルートのグループ企業であるMegagon Labsと国立国語研究所の共同研究によって誕生した自然言語処理用のライブラリです。
欧米で使用されている自然言語処理ライブラリ「spaCy」を日本語に対応させたものであり、以下の特徴があります。
- 形態素解析、係り受け解析が可能
- 64BitのPythonに対応
- Python だけでなく、コマンドラインでの処理も可能
- pip installで完了する簡単インストール
- GPUに対応
- オープンソース(MTIランセンス)
係り受け解析では Mecab と組み合わせて使う CaboCha が有名ですが、Mecab は公式的に32bit 版しか存在しないので、必然的にCaboCha を利用する場合は 32bit版Pythonを使わなければなりません。
この点、GiNZAは6bit版Pythonに対応しているので、扱いやすくなっています。
GiNZAの構造
GiNZAは 自然言語処理のフレームワークであるspaCy と 形態素解析機である SudachiPy の2つで構成されています。
つまり、係り受け解析を実行する前に、SudachiPy で形態素解析が行われているのです。
GiNZAの詳細説明
GiNZAの詳細(メソッドの仕様等)については、以下のページに書かれていますので、必要に応じてご参照下さい。
インストール方法
GiNZAは pip コマンドでインストールが可能で、次の書式になります。
pip install -U ginza モデル名
モデルにはいくつかの種類が用意されていますが、一般的には ja_ginza を指定します。
pip install -U ginza ja_ginza
精度を求める場合は、 ja_ginza_electra というモデルを指定します。
こちらは、20億文以上の日本語文書で事前学習させたモデルなので、メモリ16GB搭載PCでの利用が推奨されています。
pip install -U ginza ja_ginza_electra
ja_ginza_electra を使う場合は、巨大なモデルファイル(1GB程度)のダウンロードが必要となります。
通常は初回利用時にモデルファイルがダウンロードされるのですが、あらかじめダウンロードしておきたい場合は、次のコマンドで強制的にダウンロードすることも出来ます。
pip install -U ginza https://github.com/megagonlabs/ginza/releases/download/latest/ja_ginza_electra-latest-with-model.tar.gz
以降の説明では、 ja_ginza_electra を使っています。
係り受け解析の実行手順
係り受け解析する場合、ginza.bunsetu_spans と ginza.bunsetu_phrase_spansの2通りの指定方法があります。
ginza.bunsetu_spans は、係り付け結果が文節で返されます。
例えば、「図っています」の場合は、そのまま「図っています」が返されます。
一方、ginza.bunsetu_phrase_spans は、係り付け結果が主辞(もっとも重要な部分)で返されます。
主辞とは「文節で一番重要な単語」の事で、「図っています」は「図っ」として返されます。
下記は、必要なライブラリのimportを含んだサンプルです。
import spacy
import ginza
text = 'チーム開発を通して生徒同士の交流を図っています。'
nlp = spacy.load("ja_ginza_electra")
doc = nlp(text)
print('---- bunsetu_spans ----')
for span in ginza.bunsetu_spans(doc):
for token in span.lefts:
print(f'{token} : {str(ginza.bunsetu_span(token))} → {str(span)}')
print('---- bunsetu_phrase_spans (主辞) ----')
for span in ginza.bunsetu_phrase_spans(doc):
for token in span.lefts:
print(f'{token} : {str(ginza.bunsetu_span(token))} → {str(span)}')
結果は次の様になります。
係り受け解析クラスについて
では、さっそく自作した感情分析クラスの概要、リファレンス、ソースコードの順に紹介していきたいと思います。
クラスの概要
クラス名は DependencyAnalysis にしました。
また、クラスの構造は感情分析や文書要約と合わせていますので、以下の様になっています。
リファレンス
メソッドはコンストラクタ含めて3つです。
機能 | メソッド仕様 | 戻り値 |
---|---|---|
コンストラクタ | __init__() | |
ファイルの読み込み | read_file( filename, #入力ファイル名 encoding='utf-8' #エンコード名 ) | なし |
テキストの読み込み | read_text( text #入力テキスト ) | なし |
係り受け解析の実行 | analyze( text = None, #入力テキスト mode = 0 #動作モード ) | [ (係り受け元1,係り受け先1), (係り受け元2,係り受け先2), ・・・・・ ] |
analyze メソッドの引数と戻り値
analyze の第一引数に解析したいテキストを指定すると、read_file や read_test で読み込んだテキストは無視され、第一引数のテキストが解析されます。
analyze の 第2引数は動作モードであり、mode は0~2が指定できます。
以下は mode の値に応じた結果の例です。
使い方
使い方は2通りありますが、次の順番にメソッドを呼ぶだけでOKです。
以下に実際のサンプルを掲載しておきます。
dep = DependencyAnalysis()
res = dep.analyze('チーム開発を通して生徒同士の交流を図っています。')
for r in res:
print(f'{r[0]} → {r[1]}')
実行結果は次の様になりました。
ソースコード
それでは、最後にクラスの全ソースコードを紹介しておきます。
import spacy
import ginza
import codecs
class DependencyAnalysis:
def __init__(self):
"""
コンストラクタ
"""
self.nlp = spacy.load("ja_ginza_electra")
self.document = None
def read_file(self,filename,encoding='utf-8'):
'''
ファイルの読み込み
Parameters:
--------
filename : str 分析対象のファイル名
'''
with codecs.open(filename,'r',encoding,'ignore') as f:
self.read_text(f.read())
def read_text(self,text):
'''
テキストの読み込み
Parameters:
--------
text : str 分析対象のテキスト
'''
self.document = text
def analyze(self,text = None,mode = 0):
'''
係り受け結果を主辞(文節で一番重要な単語)で出力
Parameters:
--------
mode : int 0=通常の係り受け 1:係り受けされる側(左)のみに主辞を適用 2: 両方に主辞を適用
'''
ret = []
# mode が 1以下の場合、通常の係り受け関数、2の場合は主辞の関数を使用する
func = ginza.bunsetu_spans if mode <= 1 else ginza.bunsetu_phrase_spans
# 引数があればそれを、Noneの場合はインスタンス変数の self.textを使う
doc = self.nlp(self.document if text == None else text)
# 文書から一文を取り出す
for sentence in doc.sents:
# 係り受けの関数を適用
for span in func(sentence):
# 係り受け解析結果を取り出す
for token in span.lefts:
# modeが 1 の場合のみ、係り受け元を文節として取り出す。(例 mode=0 or 2:交流を mode=1:交流)
key = token if mode >= 1 else ginza.bunsetu_span(token)
# 結果を辞書に登録
ret.append((str(key),str(span)))
return ret
まとめ
今回はGiNZAという自然言語処理ライブラリを用いて、係り付け解析を行ってみました。
係り付け解析の実行速度はMeCab+CaboChaの方が速いという評価もあるようですが、GiNZAはインストールが簡単で、64bitのPython環境で動作するなど、とても魅力的です。
Amazonや価格コムの口コミから全体の評価を推測しようとした場合、単純な形態素解析やワードクラウドでは判断しづらい単語の関連が分かるので、今まで以上に内容が把握し易くなると思います。
とりあえず係り受け解析を手軽に試してみたいという方は、是非ソースコードをコピペしてお試しください。
この記事が皆様の一助になれば幸いです。
コメント