|
オープンソースの自然言語処理ライブラリの代表格はNLTKと呼ばれるフレームワークでした。近年、 Explosion AI 社が開発した Python/Cython で実装されたオープンソースの自然言語処理ライブラリ spaCy が公開されました。MIT ライセンスで利用が可能です。多くの言語をサポートし、学習済みの統計モデルと単語ベクトルが付属しています。研究用ではなく製品作成環境での本番利用を念頭に開発されていることも NLTK などの自然言語処理ライブラリと異なるところです。
また、最近までは spaCy の学習済みモデルには日本語に対応したものがなく、 バックエンドでMeCab を用いて形態素解析を行っていました。その結果、spaCy を利用して記述された自然言語処理のアプリケーションやライブラリでは日本語の文書を処理することができない状況が続いていました。
2019年4月に、リクルートと国立国語研究所の研究成果である日本語処理ライブラリ GiNZA が公開されました。リクルート社の公開ページはこちらです。主な特徴は、以下の通り。
これまで、高度な自然言語処理を行うためには複雑な導入作業が必要でしたが、「GiNZA」はワンステップでモジュールとモデルファイルの導入を完了できます。同時に、「spaCy」の国際化機能により、複数の欧米言語と日本語の言語リソースを切り替えて使用することが可能となり、エンジニアは複数言語の解析を単一のライブラリで行うことができます。
このページでは、 GiNZA と spaCy を用いた日本語の処理について簡単に紹介したいと思います。使用しているOSはMacOS Big Sur 11.2 です。Anaconda で Python 環境はインストールされていて、Python バージョンは Python 3.8 です。
Last updated: 2021.2.10
spaCyとGiNZAのインストールと基本的使用方法 |
spaCy と GiNZA の関係性について整理しておくと、spaCy のアーキテクチャは以下のような構造となっていて、図中の上段の、 自然言語の文字列を形態素に分割する Tokenizer, spaCy の統計モデルに相当する Language といった部分の実装を GiNZA が提供しているという関係になります。ja が日本語の部分です。
GiNZA は日本語のトークン化に SudachiPy を使用しており、GiNZAが提供する Tokenizer の実装は ginza.sudachi_tokenizer.SudachiTokenizer なので、 SudachiPy のラッパーになります。現在(2020年10月)でのGiNZA のバージョンは ginza-4.0.5 です。Sudachi のGitHub repoはここにあります
最初に、GiNZA NLP Library をインストールします。
pip install -U ginza
ginza 及び sudachipy 関連のモジュールは python 3の site-packages に保存されます。ここでインストールした GiNZA に整合する spacy も同時にインストールされます。Spacy を独立にインストールすると、ginza との間の適合性が破れますので、注意ください。
エラーが出なければ、GiNZAは正常にインストールされています。GiNZAのGitHub Repoはhttps://github.com/megagonlabs/ginzaです。
始めに、command line で使用する方法について説明します。ターミナルの command line でginzaを起動するために、
(base) $ ginza 銀座でランチをご一緒しましょう。
と入力すると、以下の通りに、日本語の形態素解析が実行されます。品詞タグと依存関係ラベリングが表示されています。
# text = 銀座でランチをご一緒しましょう。 1 銀座 銀座 PROPN 名詞-固有名詞-地名-一般 _ 6 obl SpaceAfter=No|BunsetuBILabel=B|BunsetuPositionType=SEM_HEAD|NP_B|Reading=ギンザ|NE=B-GPE|ENE=B-City 2 で で ADP 助詞-格助詞 _ 1 case _ SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=SYN_HEAD|Reading=デ 3 ランチ ランチ NOUN 名詞-普通名詞-一般 _ 6 obj SpaceAfter=No|BunsetuBILabel=B|BunsetuPositionType=SEM_HEAD|NP_B|Reading=ランチ 4 を を ADP 助詞-格助詞 _ 3 case _ SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=SYN_HEAD|Reading=ヲ 5 ご ご NOUN 接頭辞 _ 6 compound _ SpaceAfter=No|BunsetuBILabel=B|BunsetuPositionType=CONT|Reading=ゴ 6 一緒 一緒 VERB 名詞-普通名詞-サ変可能 _ 0 root SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=ROOT|Reading=イッショ 7 し する AUX 動詞-非自立可能 _ 6 advcl _ SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=SYN_HEAD|Inf=サ行変格,連用形-一般|Reading=シ 8 ましょう ます AUX 助動詞 _ 6 aux _ SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=SYN_HEAD|Inf=助動詞-マス,意志推量形|Reading=マショウ 9 。 。 PUNCT 補助記号-句点 _ 6 punct _ SpaceAfter=No|BunsetuBILabel=I|BunsetuPositionType=CONT|Reading=。
次に、MeCab と同じような機能を持つ ginzame を使ってみましょう。
$ ginzame 銀座でランチをご一緒しましょう。
銀座 名詞,固有名詞,地名,一般,*,*,銀座,ギンザ,* で 助詞,格助詞,*,*,*,*,で,デ,* ランチ 名詞,普通名詞,一般,*,*,*,ランチ,ランチ,* を 助詞,格助詞,*,*,*,*,を,ヲ,* ご 接頭辞,*,*,*,*,*,御,ゴ,* 一緒 名詞,普通名詞,サ変可能,*,*,*,一緒,イッショ,* し 動詞,非自立可能,*,*,サ行変格,連用形-一般,為る,シ,* ましょう 助動詞,*,*,*,助動詞-マス,意志推量形,ます,マショウ,* 。 補助記号,句点,*,*,*,*,。,。,* EOS
と解析されます。次に、spaCyで日本語処理を実行してみます。 spaCy をインポートして、GiNZA のモデルをロードします。また、慣例としてロードしたモデルは nlp という変数名で保持します。jupyter notebookを起動して、以下のように入力します。
import spacy nlp = spacy.load('ja_ginza')
実際に日本語の文章をトークン化してみます。
import spacy nlp = spacy.load('ja_ginza') doc = nlp('恵比寿にあるあのイタリアンにはよく行く。美味しいんだ。') for sent in doc.sents: for token in sent: info = [ token.i, # トークン番号 token.text, # テキスト token.lemma_, # 基本形 token.pos_, # 品詞 token.tag_, # 品詞詳細 ] print(info)
トークン化された結果は以下の通りです。token._.reading, token._.inf などは以前は使用できましたが、現在使用できません。
[0, '恵比寿', '恵比寿', 'PROPN', '名詞-固有名詞-地名-一般'] [1, 'に', 'に', 'ADP', '助詞-格助詞'] [2, 'ある', 'ある', 'VERB', '動詞-非自立可能'] [3, 'Garden', 'Garden', 'NOUN', '名詞-普通名詞-一般'] [4, 'Place', 'Place', 'NOUN', '名詞-普通名詞-一般'] [5, 'に', 'に', 'ADP', '助詞-格助詞'] [6, 'は', 'は', 'ADP', '助詞-係助詞'] [7, 'よく', 'よく', 'ADV', '副詞'] [8, '行く', '行く', 'VERB', '動詞-非自立可能'] [9, '。', '。', 'PUNCT', '補助記号-句点'] [10, '三つ星', '三つ星', 'NOUN', '名詞-普通名詞-一般'] [11, 'フレンチ', 'フレンチ', 'NOUN', '名詞-普通名詞-一般'] [12, 'の', 'の', 'ADP', '助詞-格助詞'] [13, 'ロブション', 'ロブション', 'PROPN', '名詞-固有名詞-一般'] [14, 'に', 'に', 'ADP', '助詞-格助詞'] [15, 'も', 'も', 'ADP', '助詞-係助詞'] [16, '行く', '行く', 'VERB', '動詞-非自立可能'] [17, '。', '。', 'PUNCT', '補助記号-句点'] [18, '美味しい', '美味しい', 'ADJ', '形容詞-一般'] [19, 'けど', 'けど', 'SCONJ', '助詞-接続助詞'] [20, '、', '、', 'PUNCT', '補助記号-読点'] [21, '高額', '高額', 'ADJ', '名詞-普通名詞-形状詞可能] [22, 'な', 'だ', 'AUX', '助動詞', 'aux', 21] [23, '店', '店', 'NOUN', '名詞-普通名詞-一般'] [24, 'だ', 'だ', 'AUX', '助動詞'] [25, '。', '。', 'PUNCT', '補助記号-句点']
nlp(・) を読み込んだ時点で、解析済みの Doc オブジェクトが返ってきます。 つまり、トークン化、品詞のタギング、依存関係のラベリング、固有表現抽出の一通りの処理が終わっています。
Doc オブジェクトには、以下のような便利なメソッドが定義されています。
詳しくは、spacyの公式ドキュメントを参照ください。ここでの説明のは、第4回 spaCy/GiNZA を用いた自然言語処理の解説を参考していますが、古いバージョンを用いた解説です。
spaCyとGiNZAを用いた言語処理 |
日本語の文書に対して前処理を行う際、以下のステップが必要とされます。
上で見た通り、spaCyではこれらの処理はパイプラインとして記述され、上記の処理は予めパイプラインに組み込まれています。この結果、nlp()を読み込んだ段階で、いきなりメインの処理から書き始めることが可能です。例えば、
import spacy # 日本語自然言語処理のパイプラインを構築されたGiNZAをspaCyから読み込む nlp = spacy.load('ja_ginza') doc = nlp("1976年、ジョブズは友人のスティーブ・ウォズニアックが自作したマイクロコンピュータ「Apple I」を販売するために起業することを決意し、同年4月1日にウォズニアックおよびロナルド・ウェインと共同で「アップルコンピュータ・カンパニー」を創業した。") for token in doc: print(token.text, token.pos_, token.vector[:2]) # 単語ベクトルの最初の2次元のみ出力
のような操作が簡単にできます。spacy.loadを通じて事前に定義されたパイプラインと学習済みモデルを読み込み、そこに文を入力することで内部で単語分割と品詞情報を付与したDocオブジェクトが生成されます。各単語はTokenオブジェクトになっており、品詞情報や埋め込みベクトルを呼び出すことが出来ます。
形態素解析した文章を文単位に分割するときは、doc.sents を用いて
doc =nlp('spaCy はオープンソースの自然言語処理ライブラリです。学習済みの統計モデルと単語ベクトルが付属しています。') for s in doc.sents: print(s)
と入力します。
spaCy はオープンソースの自然言語処理ライブラリです。 学習済みの統計モデルと単語ベクトルが付属しています。
名詞句のみ抽出するときは、以下のようにします。doc.noun_chunks を用います。
for np in doc.noun_chunks: print(np) spaCy オープンソース 自然言語処理ライブラリ 学習済み 統計モデル 単語ベクトル
依存関係を可視化する機能もあります。jupyter notebook を起動して、以下のコードを実行します。
from spacy import displacy displacy.render(doc, style="dep", options={"compact":True})
依存関係のグラフが表示されます。
固有表現抽出は文中の単語がそれぞれどんな固有表現を持つかを予測することです。以下の文に対して固有表現を取り出してみます。
doc = nlp('2018年の夏にフランスに行った。ジベルニー村のジャン・クロード・モネの家で池に浮かぶ睡蓮を見た。') for ent in doc.ents: print(ent.text, ent.start_char, ent.end_char, ent.label_)
以下の結果が表示されます。
2018年 0 5 DATE 夏 6 7 DATE フランス 8 12 LOC ジベルニー村 17 23 LOC ジャン・クロード・モネ 24 35 PERSON
spaCyでは出力の可視化機能が豊富にあります。Jupyter環境で可視化するには以下のspacy.displacyを使います。
doc = nlp('恵比寿にあるGarden Placeにはよく行く。三つ星フレンチのロブションにも行く。美味しいけど、高額な店だ。') displacy.render(doc, style="ent")
以下のように、固有名詞に色がつきます。
恵比寿 LOC にあるGarden Placeにはよく行く。三つ星フレンチの ロブション ORG にも行く。美味しいけど、高額な店だ。
LOCは場所、ORGは組織名です。Garden Place は固有名詞としてはタグ付きされていません。英文では、普通名詞ですね。
文章のトークン化で生成された単語のベクトル表現を見るときは、以下のように入力します。
token = doc[6] print(token) print(token.vector) 以下が結果です に [-0.3364834 -0.3304015 0.48512152 0.00971493 -0.53814125 -0.40941057 0.11331341 0.37284666 1.7968493 0.03470068 -0.2087467 0.14600077 0.5679184 0.6358313 0.74924064 -0.31182808 0.06420083 -1.1553096 0.7298233 0.5770952 -0.13159536 -0.37240943 -0.16940811 -0.41190833 0.19864053 -0.9732325 0.02771017 1.039847 1.191609 0.5982838 0.6307686 1.0688727 -0.8462963 -0.6300568 -0.26891077 -0.11861878 -0.298805 -0.4609053 -0.03465112 -0.42766702 -0.69790787 0.40475303 -0.64429235 -0.16863054 0.07201386 0.80741704 1.6020759 0.909806 -1.3255128 -0.10489891 0.68750393 -0.32993212 0.28356764 -0.18942814 0.5340779 -0.08497069 0.7528766 -0.13633768 -0.06377768 0.15060464 -0.28614843 -0.5399209 0.7124946 -0.5067208 0.03513991 -0.43718255 0.30380574 -0.49380696 -0.09729388 0.1833167 0.56754035 0.5102274 0.3808821 0.5141533 -1.3498139 0.34355974 -0.04191615 1.0142361 0.7048285 -0.58027756 0.14902164 -0.31125942 0.07090253 -0.57392216 -0.03852378 -0.01111794 0.15501869 -0.78775823 0.06303703 0.73310703 -0.5367188 0.88056207 0.10123526 0.4752925 -0.30720884 0.03859398 -0.119146 0.15512064 0.78491586 -0.8594853 ]
単語のベクトル表現の次数は100です。次に、文章の類似度の計算をしましょう。
doc1 = nlp("これまで様々なセミナーに参加してきましたが、個人的に一番満足できたセミナーでした。プログラミング関係に疎い私でも理解でき、逆にプログラミングに興味を持ち、自分でもプログラミングに携わりたい・作りたいと思いました。") doc2 = nlp("難しいように見えますが仕組みは簡単につくれる環境があって誰でもできるところが素晴らしいと思いました。AIに関して全く知識が無く、漠然としていましたが、このセミナーを受けて少し霧が晴れた気がすると同時に、AIへの興味が強くなりました。") doc1.similarity(doc2)
結果は、0.9436557774027887 となりました。どう思いますか?