自然言語処理ライブラリGiNZAをインストールして簡単に動かすまでの手順

公開日:2019/05/29 更新日:2019/05/29
自然言語処理ライブラリGiNZAをインストールして簡単に動かすまでの手順のサムネイル

japan-image.jpg Photo by Nicole Y-C on Unsplash

はじめに

2019年4月に株式会社リクルートのAI研究機関であるMegagon Labsから、Python向けの日本語自然言語処理オープンソースライブラリ「GiNZA」(ギンザ)が公開されました(プレスリリースのリンクは以下)。GiNZAには国立国語研究所との共同研究成果の学習済モデルが組み込まれているそうです。この記事では、GiNZAをインストールして簡単に使ってみるまでの手順をまとめます。

www.recruit.co.jp

リクルートグループの関連事業・サービスの紹介、ニュースリリース等のグループ情報サイトです。一人ひとりに「まだ、ここにない、出会い。」を提供していきます。

できるようになること

GiNZAをインストールし、簡単な使い方例をまとめます。GiNZAは自然言語処理ライブラリであるspaCyを使っており、spaCyと同じことができるようになっています。ここでは、以下のような処理を行う簡単なコードをまとめています。

  • 品詞のタグ付けを行う
  • 文単位でリストに格納する
  • 文章、トークンの類似度を調べる
  • 固有表現を抽出する

参考文献

この記事を書くにあたって、以下の文献、ページを参考にさせて頂きました。情報量が多く整理されておりとても勉強になりました。

www.anlp.jp

言語横断的な係り受け構造を設計する試みとして、Universal Dependencies (UD) [1, 5]と、そこで使われる単語の品詞体系のUniversal PoS tags [7]が注目を浴びている。著者らは、UDの日本語版を設計する活動として、品詞体系、ラベル付き依存構造の定義の策定、そのgithub上での文書化と、参照用のコーパスの作成に着手した。本稿では、その最初の報告として、定義の原案とこれまでの主な論点、また既存のコーパスをUDの形式に変換する試みの状況について述べる。

GiNZAの実際の使い方例として以下がとても参考になりました。

qiita.com

GiNZAは内部でspaCyを使用しているので,tokenのインスタンス変数についてはspaCy公式ドキュメントを読めば大体わかります。ただし,pos_detailなどはGiNZAで独自に追加している変数なので,現状GitHubのソースコードを読み解くしかなさそうです。

前提と環境

以下の通りです。

  • OS : Ubuntu18.04
  • Python3.7以上とAnaconda、もしくはpipはインストール済とする
  • この記事では、Anaconda環境を使用する
もしまだPython環境とAnacondaをインストールしておらず必要な方は以下に手順をまとめているので見てみてください。
virment.com

機械学習やデータ解析に触れる機会があり、今更ながら今後も必要になりそうなためまずはPythonを実行できる環境を構築しようと思いAnacondaをUbuntuにインストールしました。Anacondaをインストールすることで、PythonとJupyter Notebookの環境を構築できます。ここではその手順をメモします。

GiNZAをインストールする

インストールは以下のコマンド1つで完了です。v1.0.2は2019年5月29日時点で最新のバージョンです。最新版はこちらの公式ページで確認できます。

$ pip install "https://github.com/megagonlabs/ginza/releases/download/v1.0.2/ja_ginza_nopn-1.0.2.tgz"

なお、私のパソコンではAnaconda環境を構築済なため、Anacondaの仮想環境を有効化した上で上記コマンドを実行しました。本来ならばAnaconda環境でのパッケージインストールにはcondaコマンドを使う必要があり、pipを混ぜて使うと後々パッケージの干渉など問題が起きる可能性があります。よって、Anaconda環境でpipを使う場合は、別途新しくAnacondaの仮想環境を構築し、壊れて良い前提で使ったほうがいいかもしれません。私は今回触って見る程度の目的だったため、GiNZA用にAnacondaの仮想環境を構築しました。

GiNZAのインストールは以上で完了です。

簡単な使い方例

GiNZAはSpaCyがベースとなっているため、spaCyのドキュメントを見てみると基本的なことは大体できると思います。ここでは、簡単な例と実行結果をそれぞれいくつかまとめます。 なお、以降の使い方については、自然言語処理に関する知識がない私が書いています。したがって、理論的に正しいのか誤っているのか、そもそも意味があるのかなど、実行結果については各自で判断頂ければと思います。ここではあくまでどのようなコードでどのような結果が得られたかをそのまま記載します。 spaCyの公式ドキュメントは以下になります。

spacy.io

spaCy is a free open-source library for Natural Language Processing in Python. It features NER, POS tagging, dependency parsing, word vectors and more.

品詞のタグ付けを行う

いわゆる形態素解析になると思いますが、各単語の品詞をタグ付けします。これはGiNZAの公式ドキュメントにあるサンプルがそのまま品詞のタグ付けを行っています。

sample.py
import spacy
nlp = spacy.load('ja_ginza_nopn')
doc = nlp('依存構造解析の実験を行っています。')
for sent in doc.sents:
    for token in sent:
        print(token.i, token.orth_, token.lemma_, token.pos_, token.dep_, token.head.i)
    print('EOS')

上記をここではsample.pyというファイル名で保存し、保存したディレクトリパスで端末から実行します。

$ python sample.py

sample.pyの実行結果は以下です。

実行結果
$ python sample.py 
0 依存 依存 NOUN compound 2
1 構造 構造 NOUN compound 2
2 解析 解析 NOUN nmod 4
3 の の ADP case 2
4 実験 実験 NOUN obj 6
5 を を ADP case 4
6 行っ 行う VERB root 6
7 て て SCONJ mark 6
8 い 居る AUX aux 6
9 ます ます AUX aux 6
10 。 。 PUNCT punct 6
EOS

上記では読み込んだテキストの各トークン(字句)に関する情報を出力しています。各トークンが持つ情報、すなわち取得できる情報はspaCyの公式ドキュメントに全て記載されています。

spacy.io

An individual token — i.e. a word, punctuation symbol, whitespace, etc.

上記のサンプルプログラムでは、token.itoken.orth_というようにトークンのプロパティを指定して出力しています。サンプルプログラムで出力しているプロパティは以下です。

プロパティ名 内容
i int ドキュメント内でのインデックス。トークン番号
orth_ unicode 表層形
lemma_ unicode 基本形
pos_ unicode 品詞
dep_ unicode 依存関係。rootラベルが付いたトークンと各トークンの依存関係
head.i int headが依存関係の親のトークンであり、そのheadのインデックスiを返す。
なお、品詞(上記のpos_)や依存関係(上記のdep_)の英語名、またそもそもの定義などは冒頭にも参考文献として記載しましたが、こちら日本語Universal Dependenciesの試案 | 金山博、言語処理学会に全て記載されています。

また、spaCy公式ドキュメントの該当部分は以下です。

spacy.io

spaCy is a free open-source library for Natural Language Processing in Python. It features NER, POS tagging, dependency parsing, word vectors and more.

解析対象をファイルから読み込む

実際に解析する場合には、ある程度大きなテキストをファイルから読み込みたい場合が多いと思います。例としてtest.txtというテキストファイルから読みみたい場合は以下のようにします。

read-file.py
import spacy
nlp = spacy.load('ja_ginza_nopn')

# ファイルをオープンする
test_file = open("test.txt", "r")

# 内容を全て読み込む
contents = test_file.read()

doc = nlp(contents)

for sent in doc.sents:
    for token in sent:
        print(token.i, token.orth_, token.lemma_, token.pos_, token.dep_, token.head.i)
    print('EOS')

なお、上記ではtest.txtに含まれる内容を全て読み込んでいますが、状況に応じて1行づつ読み込むreadline()を使用するなど使い分けが必要かもしれません。

以降の説明で、以下のように夏目漱石の「吾輩は猫である」の冒頭部分が保存されたtest.txtを使ってみます。

test.txt
吾輩は猫である。名前はまだ無い。
 どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。吾輩はここで始めて人間というものを見た。
しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。この書生というのは時々我々を捕えて煮て食うという話である。
しかしその当時は何という考もなかったから別段恐しいとも思わなかった。ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。
掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始であろう。この時妙なものだと思った感じが今でも残っている。
第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。その後猫にもだいぶ逢ったがこんな片輪には一度も出会わした事がない。
のみならず顔の真中があまりに突起している。そうしてその穴の中から時々ぷうぷうと煙を吹く。どうも咽せぽくて実に弱った。
これが人間の飲む煙草というものである事はようやくこの頃知った。

文単位でリストに格納する

読み込んだファイル内のテキストを文単位で分けて各文をリストに保存します。ここでは、夏目漱石の「吾輩は猫である」の冒頭部分が保存されているtest.txtを読み込んでいます。

save-sents.py
import spacy
nlp = spacy.load('ja_ginza_nopn')

# ファイルを開く
test_file = open("test.txt", "r")

# 内容を読み込む
contents = test_file.read()

doc = nlp(contents)

# 文単位でリストに保存
sents = list(doc.sents)

# リストの長さ、すなわち文の数を保存
length = len(sents)

print(sents)
print("文章数 : {}".format(length))

実行結果は以下です。

実行結果
$ python save-sents.py 
[吾輩は猫である。, 名前はまだ無い。, どこで生れたかとんと見当がつかぬ。, 何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。, 吾輩はここで始めて人間というものを見た。, しかもあとで聞くとそれは書生という人間中で一番獰悪な種族であったそうだ。, この書生というのは時々我々を捕えて煮て食うという話である。, しかしその当時は何という考もなかったから別段恐しいとも思わなかった。, ただ彼の掌に載せられてスーと持ち上げられた時何だかフワフワした感じがあったばかりである。, 掌の上で少し落ちついて書生の顔を見たのがいわゆる人間というものの見始であろう。, この時妙なものだと思った感じが今でも残っている。, 第一毛をもって装飾されべきはずの顔がつるつるしてまるで薬缶だ。, その後猫にもだいぶ逢ったがこんな片輪には一度も出会わした事がない。, のみならず顔の真中があまりに突起している。, そうしてその穴の中から時々ぷうぷうと煙を吹く。, どうも咽せぽくて実に弱った。, これが人間の飲む煙草というものである事はようやくこの頃知った。]
文章数 : 17

文章、トークンの類似度を調べる

以下のように、ある2つの文章の類似度をスコア化して表示できます。

similarity.py
import spacy

nlp = spacy.load('ja_ginza_nopn')

doc1 = nlp("今日の天気はとても良い。")
doc2 = nlp("明日は雨の予報が出ている。")

similarity = doc1.similarity(doc2)
print(similarity)

実行結果は以下です。0 ~ 1 の値で算出されます。

実行結果
$ python similarity.py
0.7973139064355349

文章に出てくる各トークンの類似度をそれぞれ調べたい場合は以下です。

similarity-token.py
import spacy

nlp = spacy.load('ja_ginza_nopn')

tokens = nlp('今日は晴れです')

for token1 in tokens:
    for token2 in tokens:
        print(token1.text, token2.text, token1.similarity(token2))

実行結果は以下です。

実行結果
$ python similarity-token.py
今日 今日 1.0
今日 は 0.33590525
今日 晴れ 0.2859564
今日 です 0.52462006
は 今日 0.33590525
は は 1.0
は 晴れ 0.058222905
は です 0.12581731
晴れ 今日 0.2859564
晴れ は 0.058222905
晴れ 晴れ 1.0
晴れ です 0.3370021
です 今日 0.52462006
です は 0.12581731
です 晴れ 0.3370021
です です 1.0

固有表現を抽出する

以下は意味のない文章ですが、以下のようにentsで文章の固有表現を取得できます。doc.sentsで取得できる各文章に対してもentsは使用できます。

named-entities.py
import spacy
nlp = spacy.load('ja_ginza_nopn')

doc = nlp("本日2019年5月29日に神奈川県の小田原城付近のは晴れです。")

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

実行結果は以下です。以下のように日付がDATE、地名がLOC(location)とラベルが付けられていることを確認できます。

実行結果
$ python named-entities.py
本日2019年5月29日 0 12 DATE
神奈川県 13 17 LOC
小田原城 18 22 LOC

spaCyの該当部分は以下です。

spacy.io

spaCy is a free open-source library for Natural Language Processing in Python. It features NER, POS tagging, dependency parsing, word vectors and more.

まとめ

まだGiNZAも公開されたばかりであり公式ドキュメントがなく情報も少ないため色々と間違っている、もしくは意味のない部分もあるかもしれません。もしお気づきの点やご指摘あればご連絡頂けますと幸いです。今後情報が増えてきたらより応用例などもまとめれればと思います。

開発アプリ

nanolog.app

毎日の小さな出来事をなんでも記録して、ログとして残すためのライフログアプリです。