本連載では、「Pythonによる自然言語処理」の3回目をお届けします。前回は、PythonのNLTKとspaCyライブラリを使用して、トークン化、ステミング、レンマタイゼーションなどの簡単なNLPタスクを実行する方法について説明しました。また、品詞タグ付け、名前付き固有表現、名詞構文解析の実行方法も紹介しました。しかし、これらの操作はすべて、個々の単語に対して実行されます。
今回は、さらに一歩進んで、spaCyライブラリを使った語彙とフレーズのマッチングについて考えてみましょう。パターンを定義し、定義したパターンにマッチするフレーズを見ていきます。これは、品詞を含む正規表現を定義するのと似ています。
ルールベースマッチング
spaCyライブラリには、フレーズマッチのカスタムルールを指定するための Matcher
ツールが付属しています。Matcherツールの使い方はとても簡単です。まず最初に、マッチさせたいパターンを定義する必要がある。次に、そのパターンを
Matcherツールに追加し、最後に
Matcher` ツールをマッチングさせたいドキュメントに適用します。これは例を使って説明するのが一番わかりやすいでしょう。
ルールベースのマッチングを行うには、以下のステップを実行する必要があります。
Matcherオブジェクトの作成
まず最初に、Matcherオブジェクトを作成します。
import spacy
nlp = spacy.load('en_core_web_sm')
from spacy.matcher import Matcher
m_tool = Matcher(nlp.vocab)
パターンの定義
次のステップは、類似のフレーズをフィルタリングするために使用するパターンを定義することである。例えば、「quick-brown-fox」、「quick brown fox」、「quickbrownfox」、「quick brownfox」というフレーズを探したいとする。そのためには、次の4つのパターンを作成する必要がある。
p1 = [{'LOWER': 'quickbrownfox'}]
p2 = [{'LOWER': 'quick'}, {'IS_PUNCT': True}, {'LOWER': 'brown'}, {'IS_PUNCT': True}, {'LOWER': 'fox'}]
p3 = [{'LOWER': 'quick'}, {'LOWER': 'brown'}, {'LOWER': 'fox'}]
p4 = [{'LOWER': 'quick'}, {'LOWER': 'brownfox'}]
上のスクリプトでは
- p1は、”quickbrownfox “というフレーズを探す。
- p2は、”quick-brown-fox “というフレーズを探します。
- p3は、”qucik brown fox “というフレーズを検索しようとする。
- p4 は “クイックブラウンフォックス” というフレーズを探します。
トークン属性 LOWER
は、マッチングの前にフレーズを小文字に変換することを定義しています。
パターンを定義したら、先ほど作成した Matcher
オブジェクトに追加する必要があります。
m_tool.add('QBF', None, p1, p2, p3, p4)
ここで、”QBF” はマッチャーの名前です。任意の名前をつけることができます。
ドキュメントにMatcherを適用する
Matcher の準備ができました。次のステップでは、マッチャーをテキスト ドキュメントに適用して、マッチングが行われるかどうかを確認します。まず、簡単なドキュメントを作成しましょう。
sentence = nlp(u'The quick-brown-fox jumps over the lazy dog. The quick brown fox eats well.
the quickbrownfox is dead. the dog misses the quick brownfox')
ドキュメントに Matcher を適用するには、以下のようにします。ドキュメントは matcher オブジェクトのパラメータとして渡される必要があります。結果は、ドキュメント内でマッチしたフレーズの全てのidと、ドキュメント内の開始位置と終了位置になります。以下のスクリプトを実行してください。
phrase_matches = m_tool(sentence)
print(phrase_matches )
上のスクリプトの出力は、次のようになります。
[(12825528024649263697, 1, 6), (12825528024649263697, 13, 16), (12825528024649263697, 21, 22), (12825528024649263697, 29, 31)]
この出力から、4つのフレーズがマッチしたことがわかります。各出力の最初の長い数字は、マッチしたフレーズのIDで、2番目と3番目の数字はフレーズの開始と終了の位置である。
実際に結果をよりよく見るには、マッチした各フレーズを繰り返し処理し、その文字列値を表示すればよい。以下のスクリプトを実行します。
for match_id, start, end in phrase_matches:
string_id = nlp.vocab.strings[match_id]
span = sentence[start:end]
print(match_id, string_id, start, end, span.text)
出力する。
12825528024649263697 QBF 1 6 quick-brown-fox
12825528024649263697 QBF 13 16 quick brown fox
12825528024649263697 QBF 21 22 quickbrownfox
12825528024649263697 QBF 29 31 quick brownfox
出力結果から、マッチしたすべてのフレーズと、その語彙ID、開始位置と終了位置を見ることができます。
ルールベースマッチングのオプションの追加
sPacyライブラリの公式ドキュメントには、フレーズマッチングに使用できる全てのトークンとワイルドカードの詳細が記載されています。
例えば、” *” 属性は、そのトークンの1つ以上のインスタンスを検索するように定義されています。
quick–brown–fox “またはquick-brown–foxというフレーズを識別できる簡単なパターンを書いてみましょう。
まず、前のマッチャー QBF
を削除しましょう。
m_tool.remove('QBF')
次に、新しいパターンを定義する必要があります。
p1 = [{'LOWER': 'quick'}, {'IS_PUNCT': True, 'OP':'*'}, {'LOWER': 'brown'}, {'IS_PUNCT': True, 'OP':'*'}, {'LOWER': 'fox'}]
m_tool.add('QBF', None, p1)
パターン p1
は、フレーズ quick brown fox
の中に1つ以上の句読点があるすべてのフレーズにマッチします。次に、フィルタリングのためのドキュメントを定義しましょう。
sentence = nlp(u'The quick--brown--fox jumps over the quick-brown---fox')
この文書には、quick–brown–fox と quick-brown—fox という2つのフレーズがあり、パターンにマッチすることがわかります。それでは、このパターンを文書に適用して、結果を見ましょう。
phrase_matches = m_tool(sentence)
for match_id, start, end in phrase_matches:
string_id = nlp.vocab.strings[match_id]
span = sentence[start:end]
print(match_id, string_id, start, end, span.text)
上のスクリプトの出力は次のようになる。
12825528024649263697 QBF 1 6 quick--brown--fox
12825528024649263697 QBF 10 15 quick-brown---fox
この出力から、マッチャーが二つのフレーズにうまくマッチしたことがわかるでしょう。
フレーズベースマッチング
前節では、ドキュメントからフレーズを特定するために使用するルールを定義する方法を見ました。ルールを定義するだけでなく、探しているフレーズを直接指定することもできます。
これは、より効率的なフレーズマッチの方法です。
このセクションでは、Wikipediaの人工知能の記事の中でフレーズマッチングを行います。
フレーズマッチの手順を見る前に、まずフレーズマッチに使用するWikipediaの記事を解析してみましょう。以下のスクリプトを実行してください。
import bs4 as bs
import urllib.request
import re
import nltk
scrapped_data = urllib.request.urlopen('https://en.wikipedia.org/wiki/Artificial_intelligence')
article = scrapped_data .read()
parsed_article = bs.BeautifulSoup(article,'lxml')
paragraphs = parsed_article.find_all('p')
article_text = ""
for p in paragraphs:
article_text += p.text
processed_article = article_text.lower()
processed_article = re.sub('[^a-zA-Z]', ' ', processed_article )
processed_article = re.sub(r's+', ' ', processed_article)
このスクリプトは、Gensimライブラリを使ったPythonでのWord2Vecの実装で詳しく説明しています。Pythonで構文解析がどのように行われるかを理解したい場合は、その記事を読んでみてください。
processed_article` には、フレーズマッチに使用する文書が格納されています。
フレーズマッチの手順は、ルールベースのマッチングとよく似ています。
フレーズマッチャーオブジェクトの作成
まず、PhraseMatcher
オブジェクトを作成する必要がある。以下のスクリプトがそれを行う。
import spacy
nlp = spacy.load('en_core_web_sm')
from spacy.matcher import PhraseMatcher
phrase_matcher = PhraseMatcher(nlp.vocab)
前のセクションで、Matcher
オブジェクトを作成したことに注目してください。ここでは、PhraseMathcer
オブジェクトを作成します。
フレーズリストの作成
第2ステップでは、以下のスクリプトのように、マッチするフレーズのリストを作成し、そのリストをspaCy NLP文書に変換する必要があります。
phrases = ['machine learning', 'robots', 'intelligent agents']
patterns = [nlp(text) for text in phrases]
最後に、作成したフレーズリストをフレーズマッチャーに追加します。
phrase_matcher.add('AI', None, *patterns)
ここでは、マッチャーの名前をAIとしています。
ドキュメントにMatcherを適用する
ルールベースのマッチングと同様に、ここでもフレーズマッチャーをドキュメントに適用する必要があります。しかし、パースされた記事はspaCy文書形式ではありません。そこで、記事をspaCy文書形式に変換し、その記事にフレーズマッチャーを適用することにします。
sentence = nlp (processed_article)
matched_phrases = phrase_matcher(sentence)
出力には、以下のように、マッチしたフレーズのIDと、ドキュメント内の開始インデックスと終了インデックスが出力されます。
[(5530044837203964789, 37, 39),
(5530044837203964789, 402, 404),
(5530044837203964789, 693, 694),
(5530044837203964789, 1284, 1286),
(5530044837203964789, 3059, 3061),
(5530044837203964789, 3218, 3220),
(5530044837203964789, 3753, 3754),
(5530044837203964789, 5212, 5213),
(5530044837203964789, 5287, 5288),
(5530044837203964789, 6769, 6771),
(5530044837203964789, 6781, 6783),
(5530044837203964789, 7496, 7498),
(5530044837203964789, 7635, 7637),
(5530044837203964789, 8002, 8004),
(5530044837203964789, 9461, 9462),
(5530044837203964789, 9955, 9957),
(5530044837203964789, 10784, 10785),
(5530044837203964789, 11250, 11251),
(5530044837203964789, 12290, 12291),
(5530044837203964789, 12411, 12412),
(5530044837203964789, 12455, 12456)]
マッチしたフレーズの文字列の値を見るには、以下のスクリプトを実行します。
for match_id, start, end in matched_phrases:
string_id = nlp.vocab.strings[match_id]
span = sentence[start:end]
print(match_id, string_id, start, end, span.text)
出力には、以下のように、マッチしたフレーズの文字列値が表示されます。
5530044837203964789 AI 37 39 intelligent agents
5530044837203964789 AI 402 404 machine learning
5530044837203964789 AI 693 694 robots
5530044837203964789 AI 1284 1286 machine learning
5530044837203964789 AI 3059 3061 intelligent agents
5530044837203964789 AI 3218 3220 machine learning
5530044837203964789 AI 3753 3754 robots
5530044837203964789 AI 5212 5213 robots
5530044837203964789 AI 5287 5288 robots
5530044837203964789 AI 6769 6771 machine learning
5530044837203964789 AI 6781 6783 machine learning
5530044837203964789 AI 7496 7498 machine learning
5530044837203964789 AI 7635 7637 machine learning
5530044837203964789 AI 8002 8004 machine learning
5530044837203964789 AI 9461 9462 robots
5530044837203964789 AI 9955 9957 machine learning
5530044837203964789 AI 10784 10785 robots
5530044837203964789 AI 11250 11251 robots
5530044837203964789 AI 12290 12291 robots
5530044837203964789 AI 12411 12412 robots
5530044837203964789 AI 12455 12456 robots
出力には、検索対象となった3つのフレーズと、その開始インデックス、終了インデックス、文字列IDが表示されます。
ストップワード
この記事を締めくくる前に、ストップワードという概念に触れておきたいと思います。ストップワードとは、”the”、”a”、”an “などの、それ自体が意味を持たない英単語のことです。ストップワードは、テキストの分類や言語モデリングなどの自然言語処理タスクにはあまり有用でないことが多い。そのため、文書をさらに処理する前に、これらのストップワードを削除した方が良い場合が多い。
spaCyライブラリには305個のストップワードが含まれています。さらに、我々の要求に応じて、spaCyライブラリからストップワードを追加したり削除したりすることもできます。
デフォルトのspaCyストップワードを見るには、以下のようにspaCyモデルのstop_words
属性を使うことができる。
import spacy
sp = spacy.load('en_core_web_sm')
print(sp.Defaults.stop_words)
出力では、すべてのspacyストップワードが表示されます。
{'less', 'except', 'top', 'me', 'three', 'fifteen', 'a', 'is', 'those', 'all', 'then', 'everyone', 'without', 'must', 'has', 'any', 'anyhow', 'keep', 'through', 'bottom', 'get', 'indeed', 'it', 'still', 'ten', 'whatever', 'doing', 'though', 'eight', 'various', 'myself', 'across', 'wherever', 'himself', 'always', 'thus', 'am', 'after', 'should', 'perhaps', 'at', 'down', 'own', 'rather', 'regarding', 'which', 'anywhere', 'whence', 'would', 'been', 'how', 'herself', 'now', 'might', 'please', 'behind', 'every', 'seems', 'alone', 'from', 'via', 'its', 'become', 'hers', 'there', 'front', 'whose', 'before', 'against', 'whereafter', 'up', 'whither', 'two', 'five', 'eleven', 'why', 'below', 'out', 'whereas', 'serious', 'six', 'give', 'also', 'became', 'his', 'anyway', 'none', 'again', 'onto', 'else', 'have', 'few', 'thereby', 'whoever', 'yet', 'part', 'just', 'afterwards', 'mostly', 'see', 'hereby', 'not', 'can', 'once', 'therefore', 'together', 'whom', 'elsewhere', 'beforehand', 'themselves', 'with', 'seem', 'many', 'upon', 'former', 'are', 'who', 'becoming', 'formerly', 'between', 'cannot', 'him', 'that', 'first', 'more', 'although', 'whenever', 'under', 'whereby', 'my', 'whereupon', 'anyone', 'toward', 'by', 'four', 'since', 'amongst', 'move', 'each', 'forty', 'somehow', 'as', 'besides', 'used', 'if', 'name', 'when', 'ever', 'however', 'otherwise', 'hundred', 'moreover', 'your', 'sometimes', 'the', 'empty', 'another', 'where', 'her', 'enough', 'quite', 'throughout', 'anything', 'she', 'and', 'does', 'above', 'within', 'show', 'in', 'this', 'back', 'made', 'nobody', 'off', 're', 'meanwhile', 'than', 'neither', 'twenty', 'call', 'you', 'next', 'thereupon', 'therein', 'go', 'or', 'seemed', 'such', 'latterly', 'already', 'mine', 'yourself', 'an', 'amount', 'hereupon', 'namely', 'same', 'their', 'of', 'yours', 'could', 'be', 'done', 'whole', 'seeming', 'someone', 'these', 'towards', 'among', 'becomes', 'per', 'thru', 'beyond', 'beside', 'both', 'latter', 'ours', 'well', 'make', 'nowhere', 'about', 'were', 'others', 'due', 'yourselves', 'unless', 'thereafter', 'even', 'too', 'most', 'everything', 'our', 'something', 'did', 'using', 'full', 'while', 'will', 'only', 'nor', 'often', 'side', 'being', 'least', 'over', 'some', 'along', 'was', 'very', 'on', 'into', 'nine', 'noone', 'several', 'i', 'one', 'third', 'herein', 'but', 'further', 'here', 'whether', 'because', 'either', 'hereafter', 'really', 'so', 'somewhere', 'we', 'nevertheless', 'last', 'had', 'they', 'thence', 'almost', 'ca', 'everywhere', 'itself', 'no', 'ourselves', 'may', 'wherein', 'take', 'around', 'never', 'them', 'to', 'until', 'do', 'what', 'say', 'twelve', 'nothing', 'during', 'sixty', 'sometime', 'us', 'fifty', 'much', 'for', 'other', 'hence', 'he', 'put'}
また、ある単語がストップワードであるかどうかを調べることもできます。そのためには、以下のように is_stop
属性を使用します。
sp.vocab['wonder'].is_stop
wonder “はspaCyストップワードではないので、出力にはFalse
と表示されます。
spaCyのストップワードを追加したり削除したりするには、それぞれ sp.Defaults.stop_words.add()
と sp.Defaults.stop_words.remove()
メソッドを使用します。
sp.Defaults.stop_words.add('wonder')
次に、以下のように wonder
の is_stop
タグを ‘True` に設定する必要があります。
sp.vocab['wonder'].is_stop = True
さらに上を目指す – 手作りのEnd to Endプロジェクト
好奇心旺盛なあなたは、もっと先を目指したいと思っていませんか?ガイド付きプロジェクトのチェックをお勧めします。「CNNとKerasのTransformerを使った画像キャプション作成」をご覧ください。
をご覧ください。
このガイド付きプロジェクトでは、画像を入力として受け取り、出力としてテキストキャプションを生成する画像キャプションモデルの構築方法を学びます。
このプロジェクトでは、次のことを学びます。
- テキストを前処理する
- 入力されたテキストを簡単にベクトル化する
-
tf.data
APIを使用し、パフォーマンスの高いデータセットを構築する。 - TensorFlow/Keras と KerasNLP – 最先端の NLP モデルを構築するための Keras への公式な水平方向追加機能で、ゼロから Transformers を構築します。
- あるネットワークの出力が別のネットワークにエンコードされるハイブリッドアーキテクチャの構築
画像キャプションをどのようにとらえるか?私たちはネットワークに説明を生成するように教えているので、ほとんどの人が生成的な深層学習の例だと考えています。しかし、私はこれをニューラル機械翻訳の一例と見なしたいと思います。つまり、画像の視覚的特徴を言葉に翻訳しているのです。翻訳することで、単に新しい意味を生成するだけでなく、その画像の新しい表現を生成しているのです。翻訳、ひいては生成と捉えることで、このタスクは別の観点から見ることができ、もう少し直感的に理解できるようになります。
問題を翻訳の問題としてとらえることで、どのアーキテクチャを使いたいかを考えることが容易になる。エンコーダのみのトランスフォーマーは、エンコーダが意味のある表現をエンコードするため、テキストの理解(感情分析、分類など)に適している。デコーダのみのモデルは、デコーダが意味のある表現を同じ意味を持つ別のシーケンスに推論することができるため、生成(GPT-3など)に最適である。翻訳とは通常、エンコーダーとデコーダーのアーキテクチャによって行われ、エンコーダーは文(我々の場合は画像)の意味のある表現をエンコードし、デコーダーはこのシーケンスを我々にとってより解釈しやすい別の意味のある表現(文など)に変換することを学習するものである。
結論
フレーズと語彙のマッチングは、最も重要な自然言語処理タスクの1つです。今回は、Pythonを使ってルールベース、フレーズベースのマッチングを行う方法について、引き続き解説しました。また、spaCyストップワードについても見ていきました。
次回は、品詞タグ付けと名前付き実体認識について詳しく見ていきます。