NLPのためのPython: パターンライブラリ入門

Python for NLPを紹介する連載の8回目です。

前回は、PythonのTextBlobライブラリを使って、トークン化から品詞タグ付け、テキスト分類からセンチメント分析まで、様々なNLPタスクを実行できることを説明しました。

今回は、もう一つの非常に便利な自然言語処理ライブラリである、PythonのPatternライブラリについて解説します。

パターン・ライブラリは、以下のようなタスクを処理できる多目的ライブラリです。

  • 自然言語処理。自然言語処理:トークン化、ステミング、品詞タグ付け、感情分析などのタスクを実行する。
  • データマイニング。データマイニング:Twitter、Facebook、Wikipedia などのサイトからデータをマイニングするための API が含まれています。
  • 機械学習。分類、回帰、クラスタリングタスクに使用できるSVM、KNN、パーセプトロンなどの機械学習モデルが含まれています。

この記事では、上記のリストの中からPatternライブラリの最初の2つのアプリケーションを見ることにする。

トークン化、ステミング、センチメント分析などのタスクを実行することで、パターン・ライブラリのNLPへの利用を探る。

また、パターン・ライブラリがどのようにウェブマイニングに使われるかを見ていく。

ライブラリのインストール

ライブラリをインストールするには、以下のpipコマンドを使用します。

$ pip install pattern


また、AnacondaディストリビューションのPythonを使用している場合は、以下のAnacondaコマンドを使用してライブラリをダウンロードすることができます。

$ conda install -c asmeurer pattern


NLPのためのパターン・ライブラリ関数

このセクションでは、パターン・ライブラリのNLPへの応用のいくつかを見ていきます。

トークン化、品詞タグ付け、チャンキング

NLTKとspaCyのライブラリでは、テキスト文書中のトークン化、品詞タグ付け、名詞句の検索を行う機能が別途用意されている。

一方、Patternライブラリには、文字列を入力パラメータとして受け取り、文字列中の対応するトークンを品詞タグとともに返すオールインワンの parse メソッドがある。

この parse メソッドは、トークンが名詞句なのか動詞句なのか、あるいは主語なのか目的語なのかを知ることもできる。

また、 lemmata パラメータを True に設定することで、レムマター化されたトークンを取得することができます。

parse` メソッドのシンタックスと、各パラメータのデフォルト値は以下の通りである。

parse(string,
    tokenize=True,      # Split punctuation marks from words?
    tags=True,          # Parse part-of-speech tags? (NN, JJ, ...)
    chunks=True,        # Parse chunks? (NP, VP, PNP, ...)
    relations=False,    # Parse chunk relations? (-SBJ, -OBJ, ...)
    lemmata=False,      # Parse lemmata? (ate => eat)
    encoding='utf-8',   # Input string encoding.
    tagset=None         # Penn Treebank II (default) or UNIVERSAL.
)


それでは、実際に parse メソッドを動かしてみましょう。

from pattern.en import parse
from pattern.en import pprint


pprint(parse('I drove my car to the hospital yesterday', relations=True, lemmata=True))


parseメソッドを使用するには、patternライブラリからenモジュールをインポートする必要があります。

en モジュールは英語の NLP 関数を含んでいます。

pprintメソッドを使ってparse` メソッドの出力をコンソールに表示すると、以下のような出力が得られるはずです。

         WORD   TAG    CHUNK   ROLE   ID     PNP    LEMMA


I   PRP    NP      SBJ    1      -      i
         drove   VBD    VP      -      1      -      drive
            my   PRP$   NP      OBJ    1      -      my
           car   NN     NP ^    OBJ    1      -      car
            to   TO     -       -      -      -      to
           the   DT     NP      -      -      -      the
      hospital   NN     NP ^    -      -      -      hospital
     yesterday   NN     NP ^    -      -      -      yesterday


この出力では、トークン化された単語とその品詞タグ、トークンが属するチャンク、そしてロールが表示されます。

また、トークンのレムマター化された形も見ることができる。

parseメソッドが返すオブジェクトに対してsplit` メソッドを呼び出すと、文のリストが出力されます。

各文はトークンのリスト、各トークンは単語のリストで、単語に関連付けられたタグも一緒に出力されます。

例えば、次のようなスクリプトを見てください。

from pattern.en import parse
from pattern.en import pprint


print(parse('I drove my car to the hospital yesterday', relations=True, lemmata=True).split())


上のスクリプトの出力は次のようなものである。

[[['I', 'PRP', 'B-NP', 'O', 'NP-SBJ-1', 'i'], ['drove', 'VBD', 'B-VP', 'O', 'VP-1', 'drive'], ['my', 'PRP$', 'B-NP', 'O', 'NP-OBJ-1', 'my'], ['car', 'NN', 'I-NP', 'O', 'NP-OBJ-1', 'car'], ['to', 'TO', 'O', 'O', 'O', 'to'], ['the', 'DT', 'B-NP', 'O', 'O', 'the'], ['hospital', 'NN', 'I-NP', 'O', 'O', 'hospital'], ['yesterday', 'NN', 'I-NP', 'O', 'O', 'yesterday']]]


トークンの複数化・単数化

単数形の単語を複数形に変換するには、 pluralizesingularize メソッドをそれぞれ使用する。

from pattern.en import pluralize, singularize


print(pluralize('leaf'))
print(singularize('theives'))


出力はこのようになる。

leaves
theif


形容詞を比較級・最上級に変換する

比較級と最上級は comparativesuperlative 関数で取得することができる。

例えば、good の比較級は better で、good の最上級は best です。

では、実際に見てみましょう。

from pattern.en import comparative, superlative


print(comparative('good'))
print(superlative('good'))


出力してみましょう。

better
best


Nグラムの発見

N-gramとは、文中の単語の組み合わせが「n」個あることを指す。

例えば、「彼は病院に行く」という文の場合、2-gramは(He goes), (goes to) and (to hospital)となる。

N-gramはテキストの分類や言語モデリングにおいて重要な役割を果たすことができる。

パターン・ライブラリでは、文字列中の全てのN-gramを見つけるために ngram メソッドが使用されます。

ngramメソッドの最初のパラメータはテキストの文字列です。

n-gramの個数はメソッドのn` パラメータに渡されます。

次の例を見てください。

from pattern.en import ngrams


print(ngrams("He goes to hospital", n=2))


出力されます。

[('He', 'goes'), ('goes', 'to'), ('to', 'hospital')]


センチメントを探す

センチメントとは、ある物事に対する意見や感情を指します。

Pattern ライブラリは、テキスト文字列からセンチメントを見つける機能を提供します。

Pattern では、sentiment オブジェクトを使って、テキストの極性 (肯定的か否定的か) と主観を見つけることができます。

最もよく使われる肯定的な形容詞 (good, best, excellent など) と否定的な形容詞 (bad, awful, pathetic など) に応じて、1 から -1 の間のセンチメントスコアがテキストに割り当てられます。

このセンチメントスコアは、極性とも呼ばれます。

センチメントスコアに加えて、主観性も返されます。

主観性は、テキストに含まれる個人的な意見と事実の情報の量を定量化します。

主観性が高いほど、そのテキストには事実情報よりも個人的な意見が含まれていることを意味します。

from pattern.en import sentiment


print(sentiment("This is an excellent movie to watch. I really love it"))


上記のスクリプトを実行すると、次のような出力が表示されます。

(0.75, 0.8)


この文は、「これは見るべき素晴らしい映画です。

I really love it “のセンチメントは0.75であり、非常に肯定的であることがわかります。

同様に、0.8の主観は、この文がユーザーの個人的な意見であることを意味します。

ある文章が事実かどうかの確認

パターンライブラリの modality 関数を使うと、テキスト文字列の確からしさの度合いを調べることができます。

modality関数は -1 から 1 の間の値を返します。

ファクトの場合、modality` 関数は 0.5 より大きな値を返します。

以下は、その動作例です。

from pattern.en import parse, Sentence
from pattern.en import modality


text = "Paris is the capital of France"
sent = parse(text, lemmata=True)
sent = Sentence(sent)


print(modality(sent))


1.0


上のスクリプトでは、まず parse メソッドを Sentence クラスと一緒にインポートしています。

2 行目では、 modality 関数をインポートしています。

parseメソッドはテキストを入力として受け取り、トークン化したテキストを返します。

このテキストはSentenceクラスのコンストラクタに渡されます。

modality メソッドは Sentence クラスのオブジェクトを受け取り、その文のモダリティを返します。

Paris is the capital of France” という文字列は事実であるため、出力には 1 という値が表示されます。

同様に、確かでない文の場合、 modality メソッドが返す値は 0.0 程度になります。

次のスクリプトを見てください。

text = "I think we can complete this task"
sent = parse(text, lemmata=True)
sent = Sentence(sent)


print(modality(sent))


0.25


上の例の文字列はあまり確実ではないので、モダリティは0.25になります。

スペルチェック

suggestメソッドを使用すると、単語のスペルが正しいかどうかを調べることができます。

suggest メソッドは、単語のスペルが100%正しい場合に 1 を返します。

そうでない場合は、 suggest メソッドはその単語の修正候補を、その正しさの確率とともに返します。

次の例を見てください。

from pattern.en import suggest


print(suggest("Whitle"))


上のスクリプトでは、Whitleという単語のスペルが間違っています。

出力には、この単語に対する候補が表示されます。

[('While', 0.6459209419680404), ('White', 0.2968881412952061), ('Title', 0.03280067283431455), ('Whistle', 0.023549201009251473), ('Chile', 0.0008410428931875525)]


suggest` メソッドによると、この単語が “While” である確率は 0.64 であり、同様に “White” である確率は 0.29 である、というようになります。

では、ある単語を正しく綴ってみましょう。

from pattern.en import suggest
print(suggest("Fracture"))


出力してください。

[('Fracture', 1.0)]


出力から、この単語が正しく綴られる確率は100%であることがわかる。

数字を扱う

パターン・ライブラリには、テキスト文字列から数値に変換したり、その逆を行うための関数が含まれています。

テキストから数値表現に変換するには、 number 関数を使用します。

同様に、数値から対応するテキスト表現に戻すには、 numerals 関数が使用されます。

次のスクリプトを見てください。

from pattern.en import number, numerals


print(number("one hundred and twenty two"))
print(numerals(256.390, round=2))


出力

122
two hundred and fifty-six point thirty-nine


出力には、”one hundred and twenty-two “というテキストの数値表現である122が表示されます。

同様に、”two hundred and fifty-six point thirty-nine “は256.390という数値のテキスト表現であることがわかるはずです。

数値計算 numerals 関数では、四捨五入して求める整数の値を指定しなければならないことに注意してください。

quantify関数は、リスト内のアイテムの単語数の推定値を取得するために使用され、グループを参照するためのフレーズを提供します。

リストに3~8個の類似したアイテムがある場合、quantify` 関数はそれを “several” に数値化します。

2つの項目は「カップル」に定量化される。

from pattern.en import quantify


print(quantify(['apple', 'apple', 'apple', 'banana', 'banana', 'banana', 'mango', 'mango']))


このリストには、リンゴが3つ、バナナが3つ、マンゴーが2つあります。

このリストに対する quantify 関数の出力は次のようになります。

several bananas, several apples and a pair of mangoes


同様に、次の例は他の単語数の推定を示すものです。

from pattern.en import quantify


print(quantify({'strawberry': 200, 'peach': 15}))
print(quantify('orange', amount=1200))


出力

hundreds of strawberries and a number of peaches
thousands of oranges


データマイニングのためのパターンライブラリ機能

前節では、NLPのためにパターン・ライブラリーの最もよく使われる関数のいくつかを見た。

このセクションでは、様々なデータマイニングタスクを実行するためにパターンライブラリーをどのように使うことができるかを見ていきます。

パターンライブラリの web モジュールはウェブマイニングタスクに使われます。

ウェブページにアクセスする

URL` オブジェクトは、Web ページからコンテンツを取得するために使用されます。

ウェブページを開いたり、ウェブページからコンテンツをダウンロードしたり、ウェブページを読んだりするために利用できるいくつかのメソッドを持っています。

任意のウェブページの HTML コンテンツをダウンロードするには、download メソッドを直接使用することができます。

次のスクリプトは、Wikipediaの人工知能に関する記事のHTMLソースコードをダウンロードする。

from pattern.web import download


page_html = download('https://en.wikipedia.org/wiki/Artificial_intelligence', unicode=True)


また、URLメソッドを使って、ウェブページからイメージなどのファイルをダウンロードすることもできる。

from pattern.web import URL, extension


page_url = URL('https://upload.wikimedia.org/wikipedia/commons/f/f1/RougeOr_football.jpg')
file = open('football' + extension(page_url.page), 'wb')
file.write(page_url.download())
file.close()


上記のスクリプトでは、まず URL メソッドを使ってウェブページに接続する。

次に、開いたページで extension メソッドを呼び出し、ファイルの拡張子を返している。

ファイル拡張子は、”football “という文字列の末尾に付加される。

openメソッドを呼び出してこのパスを読み込み、最後に download() メソッドで画像をダウンロードして、デフォルトの実行パスに書き込んでいます。

テキスト内のURLの検索

findurl` メソッドを使うと、テキスト文字列から URL を抽出することができます。

以下はその例です。

from pattern.web import find_urls


print(find_urls('To search anything, go to www.google.com', unique=True))


出力には、以下のようなGoogleのウェブサイトのURLが表示されます。

['www.google.com']


Webページへの非同期リクエストの作成

ウェブページは非常に大きく、ウェブページの全コンテンツをダウンロードするのに非常に時間がかかることがあり、ウェブページが完全にダウンロードされるまで、ユーザーはアプリケーション上で他のタスクを実行できなくなる可能性があります。

しかし、パターンライブラリの web モジュールには asynchronous という関数があり、ウェブページのコンテンツを並列的にダウンロードすることができる。

asynchronous` メソッドはバックグラウンドで実行されるので、ユーザーはウェブページのダウンロード中にアプリケーションと対話することができる。

ここでは、 asynchronous メソッドの非常に簡単な例を見てみましょう。

from pattern.web import asynchronous, time, Google


asyn_req = asynchronous(Google().search, 'artificial intelligence', timeout=4)
while not asyn_req.done:
    time.sleep(0.1)
    print('searching...')


print(asyn_req.value)


print(find_urls(asyn_req.value, unique=True))


上のスクリプトでは、”artificial intelligence “という検索クエリに対するGoogleの検索結果1ページを取得しています。

ページをダウンロードしている間、並行してwhileループを実行しているのがわかると思います。

最後に、クエリで取得した結果を asynchronous モジュールが返すオブジェクトの value 属性を用いて出力しています。

次に、検索結果の URL を抽出して、画面に表示しています。

APIを使った検索エンジンの結果取得

パターンライブラリは、Google、Bing、Facebook、Wikipedia、Twitter などの検索エンジンやウェブサイトの API を呼び出すために利用できるクラスから派生した SearchEngine クラスを含む。

SearchEngine` オブジェクトのコンストラクトでは、3つのパラメータを受け取ることができる。

  • license: 対応する検索エンジンやウェブサイトの開発者ライセンスキー
  • throttle: サーバーへの連続したリクエストの時間差に対応する。
  • langauge: 検索結果の表示言語を指定する

SearchEngineクラスのsearchメソッドは、特定の検索クエリに対して検索エンジンにリクエストを行うために使用される。

search メソッドには以下のパラメータを指定することができます。

  • query: 検索文字列
  • type: 検索したいデータの種類。3つの値を取ることができる。SEARCHNEWSIMAGE`の3つの値を取ることができます。
  • start: 検索を開始するページ。
  • count: 1ページあたりの検索結果の件数

SearchEngineクラスとそのsearchメソッドを継承する検索エンジンクラスは以下のとおりである。

Google, Bing, Twitter, Facebook, Wikipedia, そして Flickr である。

検索クエリは、各項目についてオブジェクトを返す。

そして、 result オブジェクトを使用して、検索した結果に関する情報を取得することができる。

resultオブジェクトの属性はurl,title,text,language,author,date` である。

それでは、パターンライブラリを使って Google で検索を行う簡単な例を見てみましょう。

この例を動かすには、Google API の開発者ライセンスキーが必要なことを忘れないでください。

from pattern.web import Google


google = Google(license=None)
for search_result in google.search('artificial intelligence'):
    print(search_result.url)
    print(search_result.text)


上のスクリプトでは、Google クラスのオブジェクトを生成しています。

Google のコンストラクタでは、 license パラメータに自分のライセンスキーを渡します。

次に、search メソッドに artificial intelligence という文字列を渡す。

デフォルトでは、1ページ目の最初の10件が返され、それが繰り返され、各結果の url とテキストが画面に表示される。

Bing検索エンジンの場合も同様で、上のスクリプトでは Bing クラスを Google に置き換えるだけです。

それでは、Twitterで「人工知能」というテキストを含む最新の3つのツイートを検索してみましょう。

以下のスクリプトを実行してください。

from pattern.web import Twitter


twitter = Twitter()
index = None
for j in range(3):
    for tweet in twitter.search('artificial intelligence', start=index, count=3):
        print(tweet.text)
        index = tweet.id


上のスクリプトでは、まず pattern.web モジュールから Twitter クラスをインポートしています。

次に、Twitter クラスが返すツイートを繰り返し処理し、ツイートのテキストをコンソールに表示します。

上記のスクリプトを実行するために、ライセンスキーは必要ありません。

HTMLデータをプレーンテキストに変換する

URLクラスのdownloadメソッドは、HTML 形式のデータを返します。

しかし、テキストの意味解析、例えばセンチメント分類を行うには、HTML タグを含まないクリーンなデータが必要である。

データをクリーンアップするには、plaintextメソッドを使用する。

このメソッドは、download` メソッドによって返された HTML コンテンツをパラメータとして受け取り、クリーニングされたテキストを返します。

次のスクリプトを見てください。

from pattern.web import URL, plaintext


html_content = URL('https://stackabuse.com/python-for-nlp-introduction-to-the-textblob-library/').download()
cleaned_page = plaintext(html_content.decode('utf-8'))
print(cleaned_page)


出力には、ウェブページからクリーンアップされたテキストが表示されるはずです。

https://stackabuse.com/python-for-nlp-introduction-to-the-textblob-library/。

Python 3を使用している場合、データをバイトから文字列に変換するために decode('utf-8') メソッドを呼び出す必要があることを覚えておくことは重要です。

PDF 文書の解析

Pattern ライブラリは、PDF ドキュメントを解析するために使用できる PDF オブジェクトを含んでいます。

PDF (Portable Document Format) はクロスプラットフォームなファイルで、画像、テキスト、フォントをスタンドアロンなドキュメントに含んでいます。

それでは、PDFオブジェクトを使ってPDF文書をどのようにパースすることができるのか見てみましょう。

from pattern.web import URL, PDF


pdf_doc = URL('http://demo.clab.cs.cmu.edu/NLP/syllabus_f18.pdf').download()
print(PDF(pdf_doc.decode('utf-8')))


このスクリプトでは、download 関数を使ってドキュメントをダウンロードしています。

次に、ダウンロードした HTML ドキュメントを PDF クラスに渡し、最終的にそれをコンソールに出力しています。

キャッシュの削除

SearchEngine.search()URL.download()などのメソッドが返す結果は、デフォルトではローカルキャッシュに保存されます。

HTML文書をダウンロードした後にキャッシュをクリアするには、以下のようにキャッシュクラスのclear` メソッドを利用することができる。

from pattern.web import cache


cache.clear()


結論

PatternライブラリはPythonの自然言語処理ライブラリの中で最も有用なライブラリの1つです。

spaCyやNLTKほど有名ではありませんが、最上級や比較級の検索、事実や意見の検出などの機能があり、他のNLPライブラリとは一線を画しています。

今回は、Patternライブラリの自然言語処理への応用と、データマイニングやWebスクレイピングへの応用を検討しました。

トークン化、レムマター化、センチメント分析などの基本的な自然言語処理タスクの実行方法をパターン・ライブラリで確認した。

最後に、検索エンジンのクエリ、オンラインツイートのマイニング、HTML文書のクリーニングにPatternを使う方法を見た。

タイトルとURLをコピーしました