前回は、Pythonで自然言語処理を行う方法について解説を開始しました。テキストファイルとPDFファイルの読み書きを見てきました。今回は、トークン化、ステミング、レムマター化など、より基本的な自然言語処理のタスクを実行するために、spaCyライブラリの操作を開始します。
スパシー入門
spaCyライブラリは、NLTKと並んで人気のあるNLPライブラリの一つです。この2つのライブラリの基本的な違いは、NLTKが1つの問題を解決するために多種多様なアルゴリズムを含んでいるのに対し、spaCyは1つの問題解決に最適なアルゴリズムのみを含んでいるという事実です。
NLTKは2001年にリリースされたのに対して、spaCyは2015年に開発された比較的新しいライブラリです。この連載では、spaCyの最新技術について紹介します。ただし、spaCyよりもNLTKを使った方が楽なタスクがある場合は、NLTKにも触れる予定です。
スパシーのインストール
Pythonのライブラリをpip installerでインストールする場合は、コマンドラインから以下の記述を実行します。
$ pip install -U spacy
そうでない場合は、Anacondaを使用している場合、Anacondaプロンプトで次のコマンドを実行する必要があります。
$ conda install -c conda-forge spacy
spaCyをダウンロード、インストールしたら、次は言語モデルのダウンロードです。今回は英語の言語モデルを使用します。言語モデルは様々なNLPタスクを実行するために使用されますが、それは後のセクションで見ていきます。
言語モデルのダウンロードは、以下のコマンドで行います。
$ python -m spacy download en
基本機能
様々なspaCyの機能を深く掘り下げる前に、spaCyをどのように扱うかを簡単に見てみましょう。
まず最初に、以下のように spacy
ライブラリをインポートする必要があります。
import spacy
次に、spaCyの言語モデルをロードする必要があります。
sp = spacy.load('en_core_web_sm')
上のスクリプトでは、spacy
ライブラリの load
関数を用いて、英語のコア言語モデルをロードしています。モデルは変数 sp
に格納されます。
では、このモデルを用いて小さな文書を作ってみよう。ドキュメントは文や文の集まりであり、長さは無制限である。以下のスクリプトは簡単なspaCy文書を作成する。
sentence = sp(u'Manchester United is looking to sign a forward for $90 million')
SpaCyでは、このモデルを使って文書を作成すると、自動的に文書をトークンに分割します。
トークンとは簡単に言うと、何らかの意味的な価値を持つ文の個々の部分を指します。私たちのドキュメントにどんなトークンがあるか見てみましょう。
for word in sentence:
print(word.text)
上のスクリプトの出力は次のようになる。
Manchester
United
is
looking
to
sign
a
forward
for
$
90
million
この文書には次のようなトークンがあることがわかる。また、以下に示す .pos_
属性を使って、各トークンの品詞を確認することができます。
for word in sentence:
print(word.text, word.pos_)
出力されます。
Manchester PROPN
United PROPN
is VERB
looking VERB
to PART
sign VERB
a DET
forward NOUN
for ADP
$ SYM
90 NUM
million NUM
文中の各単語やトークンに品詞が割り当てられていることがわかります。たとえば、「Manchester」は固有名詞としてタグ付けされ、「Looking」は動詞としてタグ付けされ、などです。
最後に、品詞だけでなく、依存関係も確認できます。
別の文書を作ってみましょう。
sentence2 = sp(u"Manchester United isn't looking to sign any forward.")
依存関係の解析には、以下のように dep_
属性を用います。
for word in sentence2:
print(word.text, word.pos_, word.dep_)
出力はこのようになります。
Manchester PROPN compound
United PROPN nsubj
is VERB aux
n't ADV neg
looking VERB ROOT
to PART aux
sign VERB xcomp
any DET advmod
forward ADV advmod
. PUNCT punct
出力から、spaCyはトークン間の依存関係を見つけるのに十分な知能を持っていることがわかります。例えば、この文にはis'nt
という単語がありました。依存性パーサーはこれを2つの単語に分解し、n't
が前の単語の否定であることを特定します。
依存性解析の詳細な理解については、こちらの記事を参照してください。
単語を印刷するだけでなく、文書から文章を印刷することもできます。
document = sp(u'Hello from Stackabuse. The site with the best Python Tutorials. What are you looking for?')
では、次のスクリプトを使って各文を繰り返し表示してみましょう。
for sentence in document.sents:
print(sentence)
スクリプトの出力は次のようになる。
Hello from Stackabuse.
The site with the best Python Tutorials.
What are you looking for?
また、ある文が特定のトークンで始まっているかどうかを調べることもできる。インデックスと角括弧を使って、配列のように個々のトークンを取得することができる。
document[4]
上のスクリプトでは、文書中の5番目の単語を検索しています。インデックスは0から始まり、ピリオドは1つのトークンとしてカウントされることを覚えておいてください。出力には、次のように表示される。
The
次に、文書内のどの文が The
で始まっているかを調べるには、以下のように is_sent_start
属性を使用します。
document[4].is_sent_start
出力では、トークン The
が2番目の文の冒頭で使用されているため、 True
と表示されます。
このセクションでは、spaCyライブラリの基本的な操作をいくつか見てきました。ここでは、トークン化、ステミング、そしてレマット化について詳しく見ていきましょう。
トークン化
先に説明したように、トークン化とは、文書を単語、句読点、数字などに分解することです。
それでは、spaCyのトークン化について詳しく見ていきましょう。次のようなスクリプトで新しい文書を作成します。
sentence3 = sp(u'"They're leaving U.K. for U.S.A."')
print(sentence3)
この文には、冒頭と末尾に引用符が含まれていることがわかります。また、”U.K” と “U.S.A.” という略語の中に句読点が含まれています。
この文章をspaCyがどのようにトークン化するか見てみましょう。
for word in sentence3:
print(word.text)
出力されます。
"
They
're
leaving
U.K.
for
U.S.A.
"
出力では、spaCyが開始と終了のダブルクォートをトークン化していることがわかります。しかし、U.K.やU.S.A.のような略語の間に使われる句読点はトークン化されないという賢さを持っています。
もう一つのトークン化の例を見てみましょう。
sentence4 = sp(u"Hello, I am non-vegetarian, email me the menu at abc-xyz@gmai.com")
print(sentence4)
上の文では、”non-vegetarian “という単語とEメールアドレスの中にダッシュがあります。これをspaCyがどのようにトークン化するか見てみましょう。
for word in sentence4:
print(word.text)
出力されます。
Hello
,
I
am
non
-
vegetarian
,
email
me
the
menu
at
abc-xyz@gmai.com
出力から明らかなように、spaCyは実際にメールを検出することができ、”-“があるにもかかわらずトークン化しませんでした。一方、”non-vegetarian “という単語はトークン化されました。
それでは、文書内の単語をどのように数えるかを見てみましょう。
len(sentence4)
出力には、sentence4
に含まれるトークンの数である14が表示されます。
エンティティの検出
文書を単語にトークン化するだけでなく、その単語が会社、場所、建物、通貨、機関などの実体であるかどうかを検出することもできます。
名前付き実体を認識する簡単な例を見てみましょう。
sentence5 = sp(u'Manchester United is looking to sign Harry Kane for $90 million')
まず、単純にトークン化することを試みます。
for word in sentence5:
print(word.text)
出力
Manchester
United
is
looking
to
sign
Harry
Kane
for
$
90
million
Manchester United “は1つの単語なので、2つの単語にトークン化してはいけないことがわかります。同様に、”Harry Kane “は人の名前であり、”$90 million “は通貨価値である。これらもトークン化すべきではありません。
ここで、名前付きエンティティの認識が必要になります。文書から名前付き実体を取得するには、 ents
属性を使用する必要があります。上の文から名前付き実体を取得してみよう。以下のスクリプトを実行する。
for entity in sentence.ents:
print(entity.text + ' - ' + entity.label_ + ' - ' + str(spacy.explain(entity.label_)))
上のスクリプトでは、実体のテキスト、実体のラベル、実体の詳細を出力している。出力は次のようになります。
出力されます。
Manchester United - ORG - Companies, agencies, institutions, etc.
Harry Kane - PERSON - People, including fictional
$90 million - MONEY - Monetary values, including unit
このように、spaCyの名前付きエンティティ認識器は、「Manchester United」を組織として、「Harry Kane」を個人として、「$90 million」を通貨価値として認識することに成功したことが分かります。
名詞の検出
名前付きエンティティの検出に加え、名詞も検出することができる。そのためには、 noun_chunks
属性を使用します。以下の文章を考えてみよう。
sentence5 = sp(u'Latest Rumours: Manchester United is looking to sign Harry Kane for $90 million')
この文から名詞を検索してみよう。
for noun in sentence5.noun_chunks:
print(noun.text)
出力する。
Latest Rumours
Manchester United
Harry Kane
出力から、名詞は名前付きエンティティにもなりうるし、その逆もありうることがわかる。
ステミング
ステミングとは、単語をその語源に還元することです。自然言語処理タスクを実行していると、同じ語根を持つ異なる単語を見つけるさまざまなシナリオに遭遇します。例えば、compute、computer、computing、computed など。統一性を持たせるために、単語をそのルートフォームに還元したいと思うかもしれません。そこで登場するのが、ステミングです。
意外かもしれませんが、spaCy にはレムマター化のみに依存しているため、ステミングのための関 数が含まれていません。したがって、このセクションでは、NLTK を使用してステミングを行います。
NLTK には、Porter Stemmer と Snowball stemmers の 2 種類のステム機能があります。どちらも、異なるアルゴリズムを使用して実装されています。
ポーター ステマー
ポーターステマーの動きを見てみましょう。
import nltk
from nltk.stem.porter import *
PorterStemmer` クラスを作成してみましょう。
stemmer = PorterStemmer()
次のリストがあり、これらの単語をステムに還元したいとします。
tokens = ['compute', 'computer', 'computed', 'computing']
次のスクリプトは、ポーターステムマーを使用して、リスト内の単語のステムを検索します。
for token in tokens:
print(token + ' --> ' + stemmer.stem(token))
出力は次のとおりです。
compute --> comput
computer --> comput
computed --> comput
computing --> comput
4 つの単語がすべて、実際にはまったく単語ではない「comput」に短縮されていることがわかります。
スノーボールステマー
雪だるま式ステマーは、ポーター式ステマーの少し改良されたバージョンで、通常、後者よりも優先されます。スノーボールステムマーがどのように動作するか見てみましょう。
from nltk.stem.snowball import SnowballStemmer
stemmer = SnowballStemmer(language='english')
tokens = ['compute', 'computer', 'computed', 'computing']
for token in tokens:
print(token + ' --> ' + stemmer.stem(token))
上記のスクリプトでは、Snowball stemmer を使用して、porter stemmer で使用したものと同じ 4 つの単語のステムを検索しています。出力は次のようになります。
compute --> comput
computer --> comput
computed --> comput
computing --> comput
結果は同じであることがわかります。結果は同じで、”comput” がステムとして得られました。繰り返しますが、この「comput」という単語は、実は辞書に載っていない単語です。
そこで、lemmatizationが役に立ちます。レムマトイズは、単語を辞書に記載されているとおりのステムに変換します。レムマト化によって返されるステムは、実際の辞書の単語であり、ステム機能によって返される単語と は異なり、意味的に完全です。
Lemmatization
spaCyではステミングを行うことができませんでしたが、spaCyを使ってlemmatizationを行うことができます。
そのためには、spaCyのドキュメントにある lemma_
属性を使う必要があります。例えば、次のような文があるとします。
sentence6 = sp(u'compute computer computed computing')
この文の語根をspaCyのlemmatizationを使って以下のように求めることができる。
for word in sentence6:
print(word.text, word.lemma_)
上のスクリプトの出力は次のようなものである。
compute compute
computer computer
computed compute
computing computing
ステミングで得られたルートが「comput」であったのとは異なり、ここで得られたルートは辞書にある実際の単語であることがおわかりになるでしょう。
レマット化では、第2または第3形式の単語が第1形式のバリアントに変換されます。次の例を見てください。
sentence7 = sp(u'A letter has been written, asking him to be released')
for word in sentence7:
print(word.text + ' ===>', word.lemma_)
出力。
A ===> a
letter ===> letter
has ===> have
been ===> be
written ===> write
, ===> ,
asking ===> ask
him ===> -PRON-
to ===> to
be ===> be
released ===> release
出力から、”written”, “released” などの第2、第3形式の単語が第1形式、すなわち “write” と “release” に変換されていることがよくわかります。
さらに上を目指す – 手作りのEnd to Endプロジェクト
好奇心旺盛なあなたは、もっと先を目指したいと思っていませんか?ガイド付きプロジェクトのチェックをお勧めします。「CNNとKerasのTransformerを使った画像キャプション作成」をご覧ください。
をご覧ください。
このガイド付きプロジェクトでは、画像を入力として受け取り、出力としてテキストキャプションを生成する画像キャプションモデルの構築方法を学びます。
このプロジェクトでは、次のことを学びます。
- テキストを前処理する
- 入力されたテキストを簡単にベクトル化する
-
tf.data
APIを使用し、パフォーマンスの高いデータセットを構築する。 - TensorFlow/Keras と KerasNLP – 最先端の NLP モデルを構築するための Keras への公式な水平方向追加機能で、ゼロから Transformers を構築します。
- あるネットワークの出力が別のネットワークにエンコードされるハイブリッドアーキテクチャの構築
画像キャプションをどのようにとらえるか?私たちはネットワークに説明を生成するように教えているので、ほとんどの人が生成的な深層学習の例だと考えています。しかし、私はこれをニューラル機械翻訳の一例と見なしたいと思います。つまり、画像の視覚的特徴を言葉に翻訳しているのです。翻訳することで、単に新しい意味を生成するだけでなく、その画像の新しい表現を生成しているのです。翻訳、ひいては生成と捉えることで、このタスクは別の観点から見ることができ、もう少し直感的に理解できるようになります。
問題を翻訳の問題としてとらえることで、どのアーキテクチャを使いたいかを考えることが容易になる。エンコーダのみのトランスフォーマーは、エンコーダが意味のある表現をエンコードするため、テキストの理解(感情分析、分類など)に適している。デコーダのみのモデルは、デコーダが意味のある表現を同じ意味を持つ別のシーケンスに推論することができるため、生成(GPT-3など)に最適である。翻訳とは通常、エンコーダーとデコーダーのアーキテクチャによって行われ、エンコーダーは文(我々の場合は画像)の意味のある表現をエンコードし、デコーダーはこのシーケンスを我々にとってより解釈しやすい別の意味のある表現(文など)に変換することを学習するものである。
結論
トークン化、ステミング、レマット化は、自然言語処理の最も基本的なタスクの1つです。この記事では、spaCyライブラリを使用してトークン化とレムマタイズを実行する方法を学びました。また、NLTKがステミングにどのように使用できるかも見てきました。次回は、Pythonの語彙とフレーズのマッチングについて説明します。