著者は、Write for DOnationsプログラムの一環として、寄付を受け取るためにOpen Internet/Free Speechファンドを選択しました。
前書き
今日生成される大量のデータはunstructuredであり、洞察を生成するための処理が必要です。 非構造化データの例としては、ニュース記事、ソーシャルメディアへの投稿、検索履歴などがあります。 自然言語を分析し、それを理解するプロセスは、自然言語処理(NLP)の分野に該当します。 感情分析は、テキストまたはテキストの一部を事前定義された感情に分類することを含む、一般的なNLPタスクです。 Pythonで一般的に使用されるNLPライブラリであるthe Natural Language Toolkit (NLTK)を使用して、テキストデータを分析します。
このチュートリアルでは、さまざまなデータクリーニング方法でNLP用にNLTKパッケージからサンプルツイートのデータセットを準備します。 データセットを処理する準備ができたら、事前に分類されたツイートでモデルをトレーニングし、モデルを使用してサンプルのツイートを否定的な感情と肯定的な感情に分類します。
この記事は、Pythonの基本(How To Code in Python 3 seriesを参照)、主にデータ構造、クラス、およびメソッドの使用に精通していることを前提としています。 このチュートリアルでは、NLPとnltk
の知識がないことを前提としていますが、それに関する知識が追加されています。
前提条件
-
このチュートリアルは、Pythonバージョン3.6.5に基づいています。 Python 3がインストールされていない場合は、install and setup a local programming environment for Python 3のガイドをご覧ください。
-
言語データの操作に精通していることをお勧めします。 NLTKを初めて使用する場合は、How To Work with Language Data in Python 3 using the Natural Language Toolkit (NLTK)ガイドを確認してください。
[[step-1 -—- installing-nltk-and-downloading-the-data]] ==ステップ1—NLTKのインストールとデータのダウンロード
このチュートリアルでは、すべてのNLPタスクにPythonのNLTKパッケージを使用します。 この手順では、NLTKをインストールし、モデルのトレーニングとテストに使用するサンプルツイートをダウンロードします。
まず、pip
パッケージマネージャーを使用したinstall the NLTK package:
pip install nltk==3.3
このチュートリアルでは、NLTKパッケージの一部であるサンプルツイートを使用します。 最初に、次のコマンドを実行してPythonインタラクティブセッションを開始します。
python3
次に、nltk
モジュールをPythonインタープリターにインポートします。
import nltk
NLTKパッケージからサンプルツイートをダウンロードします。
nltk.download('twitter_samples')
Pythonインタープリターからこのコマンドを実行すると、ツイートがダウンロードされ、ローカルに保存されます。 サンプルがダウンロードされると、使用できるようになります。
ネガティブツイートとポジティブツイートを使用して、後のチュートリアルで感情分析に関するモデルをトレーニングします。 感情のないツイートは、モデルのテストに使用されます。
独自のデータセットを使用する場合は、Twitter APIを使用して、特定の期間、ユーザー、またはハッシュタグからツイートを収集できます。
NLTKをインポートし、サンプルツイートをダウンロードしたので、exit()
と入力してインタラクティブセッションを終了します。 ツイートをインポートして、データの処理を開始する準備ができました。
[[step-2 -—- tokenizing-the-data]] ==ステップ2—データのトークン化
元の形式の言語はマシンで正確に処理できないため、マシンが理解しやすいように言語を処理する必要があります。 データを理解するための最初の部分は、tokenizationと呼ばれるプロセス、または文字列をtokensと呼ばれる小さな部分に分割することです。
トークンは、ユニットとして機能するテキスト内の文字のシーケンスです。 トークンの作成方法に基づいて、トークンは、単語、顔文字、ハッシュタグ、リンク、または個々の文字で構成されます。 言語をトークンに分割する基本的な方法は、空白と句読点に基づいてテキストを分割することです。
開始するには、スクリプトを保持する新しい.py
ファイルを作成します。 このチュートリアルでは、nlp_test.py
を使用します。
nano nlp_test.py
このファイルでは、最初にtwitter_samples
をインポートして、そのデータを操作できるようにします。
nlp_test.py
from nltk.corpus import twitter_samples
これにより、モデルをトレーニングおよびテストするためのさまざまなツイートを含む3つのデータセットがNLTKからインポートされます。
-
negative_tweets.json
:否定的な感情を持つ5000件のツイート -
positive_tweets.json
:肯定的な感情を持つ5000件のツイート -
tweets.20150430-223406.json
:感情のない20000ツイート
次に、positive_tweets
、negative_tweets
、およびtext
の変数を作成します。
nlp_test.py
from nltk.corpus import twitter_samples
positive_tweets = twitter_samples.strings('positive_tweets.json')
negative_tweets = twitter_samples.strings('negative_tweets.json')
text = twitter_samples.strings('tweets.20150430-223406.json')
twitter_samples
のstrings()
メソッドは、データセット内のすべてのツイートを文字列として出力します。 さまざまなツイートコレクションを変数として設定すると、処理とテストが容易になります。
NLTKでトークナイザーを使用する前に、追加のリソースpunkt
をダウンロードする必要があります。 punkt
モジュールは、単語や文をトークン化するのに役立つ事前トレーニング済みのモデルです。 たとえば、このモデルは、名前にピリオドが含まれている可能性があることを知っています(「S. Daityari」)および文中のこの期間の存在は、必ずしもそれを終了するわけではありません。 まず、Pythonインタラクティブセッションを開始します。
python3
セッションで次のコマンドを実行して、punkt
リソースをダウンロードします。
import nltk
nltk.download('punkt')
ダウンロードが完了すると、NLTKのトークナイザーを使用する準備が整います。 NLTKは、.tokenized()
メソッドを使用したツイートのデフォルトのトークナイザーを提供します。 行を追加して、positive_tweets.json
データセットをトークン化するオブジェクトを作成します。
nlp_test.py
from nltk.corpus import twitter_samples
positive_tweets = twitter_samples.strings('positive_tweets.json')
negative_tweets = twitter_samples.strings('negative_tweets.json')
text = twitter_samples.strings('tweets.20150430-223406.json')
tweet_tokens = twitter_samples.tokenized('positive_tweets.json')
スクリプトをテストして.tokenized
メソッドの動作を確認する場合は、強調表示されたコンテンツをnlp_test.py
スクリプトに追加します。 これにより、positive_tweets.json
データセットからの単一のツイートがトークン化されます。
nlp_test.py
from nltk.corpus import twitter_samples
positive_tweets = twitter_samples.strings('positive_tweets.json')
negative_tweets = twitter_samples.strings('negative_tweets.json')
text = twitter_samples.strings('tweets.20150430-223406.json')
tweet_tokens = twitter_samples.tokenized('positive_tweets.json')[0]
print(tweet_tokens[0])
ファイルを保存して閉じ、スクリプトを実行します。
python3 nlp_test.py
トークン化のプロセスは、空白の単純な分割ではないため、時間がかかります。 しばらくすると、次が表示されます。
Output['#FollowFriday',
'@France_Inte',
'@PKuchly57',
'@Milipol_Paris',
'for',
'being',
'top',
'engaged',
'members',
'in',
'my',
'community',
'this',
'week',
':)']
ここで、.tokenized()
メソッドは、@
や_
などの特殊文字を返します。 これらの文字は、このチュートリアルの後半の正規表現で削除されます。
.tokenized()
メソッドがどのように機能するかを確認したので、最後の行をコメントアウトするか削除して、行の先頭に#
を追加して、トークン化されたツイートをスクリプトから出力してください。
nlp_test.py
from nltk.corpus import twitter_samples
positive_tweets = twitter_samples.strings('positive_tweets.json')
negative_tweets = twitter_samples.strings('negative_tweets.json')
text = twitter_samples.strings('tweets.20150430-223406.json')
tweet_tokens = twitter_samples.tokenized('positive_tweets.json')[0]
#print(tweet_tokens[0])
これで、データをトークン化するようにスクリプトが構成されました。 次のステップでは、スクリプトを更新してデータを正規化します。
[[step-3 -—- normalizing-the-data]] ==ステップ3—データの正規化
単語にはさまざまな形式があります。たとえば、「ran」、「runs」、「running」は、同じ動詞「run」のさまざまな形式です。 分析の要件に応じて、これらすべてのバージョンを同じ形式の「実行」に変換する必要がある場合があります。 NLPのNormalizationは、単語を正規の形式に変換するプロセスです。
正規化は、同じ意味で異なる形式の単語をグループ化するのに役立ちます。 正規化しないと、「ran」、「runs」、および「running」は異なる単語として扱われますが、同じ単語として扱われたい場合もあります。 このセクションでは、正規化の2つの一般的な手法であるstemmingとlemmatizationについて説明します。
ステミングは、単語から接辞を削除するプロセスです。 ステミングは、単純な動詞形式のみで機能し、単語の終わりを削除するヒューリスティックなプロセスです。
このチュートリアルでは、語彙のコンテキストとテキスト内の単語のmorphological analysisで単語を正規化する、レンマ化のプロセスを使用します。 補題アルゴリズムは、単語の構造とそのコンテキストを分析して、正規化された形式に変換します。 したがって、速度が犠牲になります。 ステミングと補題化の比較は、最終的には速度と精度のトレードオフに帰着します。
見出し語化の使用に進む前に、Pythonインタラクティブセッションに次を入力して必要なリソースをダウンロードします。
python3
セッションで次のコマンドを実行して、リソースをダウンロードします。
import nltk
nltk.download('wordnet')
nltk.download('averaged_perceptron_tagger')
wordnet
は、スクリプトがベースワードを決定するのに役立つ英語の語彙データベースです。 文中の単語のコンテキストを判別するには、averaged_perceptron_tagger
リソースが必要です。
ダウンロードしたら、レンマタイザーを使用する準備がほぼ整います。 lemmatizerを実行する前に、テキスト内の各単語のコンテキストを決定する必要があります。 これは、文内の単語の相対位置を評価するタグ付けアルゴリズムによって実現されます。 Pythonセッションで、pos_tag
関数をインポートし、タグを取得するための引数としてトークンのリストを提供します。 Pythonでこれを試してみましょう。
from nltk.tag import pos_tag
from nltk.corpus import twitter_samples
tweet_tokens = twitter_samples.tokenized('positive_tweets.json')
print(pos_tag(tweet_tokens[0]))
これがpos_tag
関数の出力です。
Output[('#FollowFriday', 'JJ'),
('@France_Inte', 'NNP'),
('@PKuchly57', 'NNP'),
('@Milipol_Paris', 'NNP'),
('for', 'IN'),
('being', 'VBG'),
('top', 'JJ'),
('engaged', 'VBN'),
('members', 'NNS'),
('in', 'IN'),
('my', 'PRP$'),
('community', 'NN'),
('this', 'DT'),
('week', 'NN'),
(':)', 'NN')]
タグのリストから、最も一般的なアイテムとその意味のリストを以下に示します。
-
NNP
:名詞、固有名詞、単数 -
NN
:名詞、一般、単数、または質量 -
IN
:前置詞または接続詞、従属 -
VBG
:動詞、動名詞、または現在分詞 -
VBN
:動詞、過去分詞
これがfull list of the datasetです。
一般に、タグがNN
で始まる場合、その単語は名詞であり、VB
で始まる場合、その単語は動詞です。 タグを確認した後、exit()
と入力してPythonセッションを終了します。
これを文を正規化する関数に組み込むには、最初にテキスト内の各トークンのタグを生成してから、タグを使用して各単語をレンマライズする必要があります。
文をレンマ化する次の関数を使用して、nlp_test.py
ファイルを更新します。
nlp_test.py
...
from nltk.tag import pos_tag
from nltk.stem.wordnet import WordNetLemmatizer
def lemmatize_sentence(tokens):
lemmatizer = WordNetLemmatizer()
lemmatized_sentence = []
for word, tag in pos_tag(tokens):
if tag.startswith('NN'):
pos = 'n'
elif tag.startswith('VB'):
pos = 'v'
else:
pos = 'a'
lemmatized_sentence.append(lemmatizer.lemmatize(word, pos))
return lemmatized_sentence
print(lemmatize_sentence(tweet_tokens[0]))
このコードは、WordNetLemmatizer
クラスをインポートし、それを変数lemmatizer
に初期化します。
関数lemmatize_sentence
は、最初にツイートの各トークンの位置タグを取得します。 if
ステートメント内で、タグがNN
で始まる場合、トークンは名詞として割り当てられます。 同様に、タグがVB
で始まる場合、トークンは動詞として割り当てられます。
ファイルを保存して閉じ、スクリプトを実行します。
python3 nlp_test.py
これが出力です:
Output['#FollowFriday',
'@France_Inte',
'@PKuchly57',
'@Milipol_Paris',
'for',
'be',
'top',
'engage',
'member',
'in',
'my',
'community',
'this',
'week',
':)']
動詞being
がそのルート形式be
に変わり、名詞members
がmember
に変わることに気付くでしょう。 続行する前に、スクリプトのサンプルツイートを出力する最後の行をコメントアウトします。
単語を正規化する関数が正常に作成されたので、次はノイズを除去する準備が整いました。
[[step-4 -—- removing-noise-from-the-data]] ==ステップ4—データからのノイズの除去
この手順では、データセットからノイズを削除します。 Noiseは、データに意味や情報を追加しないテキストの部分です。
ノイズは各プロジェクトに固有であるため、あるプロジェクトでノイズを構成するものは、別のプロジェクトではない場合があります。 たとえば、言語で最も一般的な単語はstop wordsと呼ばれます。 ストップワードの例には、「is」、「the」、および「a」があります。 特定のユースケースで言語の包含が正当化されない限り、一般的に言語を処理する場合は関係ありません。
このチュートリアルでは、use regular expressions in Pythonを使用して、次のアイテムを検索および削除します。
-
Hyperlinks-TwitterのすべてのハイパーリンクはURL短縮サービスt.coに変換されます。 したがって、それらをテキスト処理に保持しても、分析に値が追加されることはありません。
-
Twitter handles in replies-これらのTwitterユーザー名の前には
@
記号が付いていますが、これは意味を伝えません。 -
Punctuation and special characters-これらはテキストデータにコンテキストを提供することがよくありますが、このコンテキストは処理が難しいことがよくあります。 簡単にするために、すべての句読点と特殊文字をツイートから削除します。
ハイパーリンクを削除するには、最初にhttp://
またはhttps://
で始まり、その後に文字、数字、または特殊文字が続くURLに一致する部分文字列を検索する必要があります。 パターンが一致すると、.sub()
メソッドはそれを空の文字列に置き換えます。
remove_noise()
関数内で単語形式を正規化するため、スクリプトからlemmatize_sentence()
関数をコメントアウトできます。
次のコードをnlp_test.py
ファイルに追加して、データセットからノイズを削除します。
nlp_test.py
...
import re, string
def remove_noise(tweet_tokens, stop_words = ()):
cleaned_tokens = []
for token, tag in pos_tag(tweet_tokens):
token = re.sub('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+#]|[!*\(\),]|'\
'(?:%[0-9a-fA-F][0-9a-fA-F]))+','', token)
token = re.sub("(@[A-Za-z0-9_]+)","", token)
if tag.startswith("NN"):
pos = 'n'
elif tag.startswith('VB'):
pos = 'v'
else:
pos = 'a'
lemmatizer = WordNetLemmatizer()
token = lemmatizer.lemmatize(token, pos)
if len(token) > 0 and token not in string.punctuation and token.lower() not in stop_words:
cleaned_tokens.append(token.lower())
return cleaned_tokens
このコードは、ノイズを除去し、前のセクションで説明した正規化とレンマ化を組み込んだremove_noise()
関数を作成します。 このコードは、ツイートトークンとストップワードのタプルという2つの引数を取ります。
次に、コードはループを使用して、データセットからノイズを削除します。 ハイパーリンクを削除するには、コードは最初にhttp://
またはhttps://
で始まり、その後に文字、数字、または特殊文字が続くURLに一致する部分文字列を検索します。 パターンが一致すると、.sub()
メソッドはそれを空の文字列または''
に置き換えます。
同様に、@
の言及を削除するために、コードは正規表現を使用してテキストの関連部分を置き換えます。 このコードは、re
ライブラリを使用して@
記号を検索し、続いて数字、文字、または_
を検索し、それらを空の文字列に置き換えます。
最後に、ライブラリstring
を使用して句読点を削除できます。
これに加えて、NLTKの組み込みのストップワードセットを使用してストップワードを削除します。これは、別途ダウンロードする必要があります。
Pythonインタラクティブセッションから次のコマンドを実行して、このリソースをダウンロードします。
nltk.download('stopwords')
リソースがダウンロードされたら、対話型セッションを終了します。
.words()
メソッドを使用して、英語のストップワードのリストを取得できます。 関数をテストするために、サンプルのツイートで実行してみましょう。 nlp_test.py
ファイルの最後に次の行を追加します。
nlp_test.py
...
from nltk.corpus import stopwords
stop_words = stopwords.words('english')
print(remove_noise(tweet_tokens[0], stop_words))
ファイルを保存して閉じた後、スクリプトを再度実行して、次のような出力を受け取ります。
Output['#followfriday', 'top', 'engage', 'member', 'community', 'week', ':)']
この関数は、すべての@
の言及を削除し、単語を停止し、単語を小文字に変換することに注意してください。
次のステップのモデリング演習に進む前に、remove_noise()
関数を使用して、ポジティブツイートとネガティブツイートをクリーンアップします。 行をコメントアウトして、サンプルツイートにremove_noise()
の出力を出力し、nlp_test.py
スクリプトに以下を追加します。
nlp_test.py
...
from nltk.corpus import stopwords
stop_words = stopwords.words('english')
#print(remove_noise(tweet_tokens[0], stop_words))
positive_tweet_tokens = twitter_samples.tokenized('positive_tweets.json')
negative_tweet_tokens = twitter_samples.tokenized('negative_tweets.json')
positive_cleaned_tokens_list = []
negative_cleaned_tokens_list = []
for tokens in positive_tweet_tokens:
positive_cleaned_tokens_list.append(remove_noise(tokens, stop_words))
for tokens in negative_tweet_tokens:
negative_cleaned_tokens_list.append(remove_noise(tokens, stop_words))
サンプルのツイートをクリーンアップするコードを追加したので、元のトークンをサンプルのツイートのクリーンアップされたトークンと比較することができます。 これをテストする場合は、ファイルに次のコードを追加して、リスト内の500番目のツイートの両方のバージョンを比較します。
nlp_test.py
...
print(positive_tweet_tokens[500])
print(positive_cleaned_tokens_list[500])
ファイルを保存して閉じ、スクリプトを実行します。 出力から、句読点とリンクが削除され、単語が小文字に変換されていることがわかります。
Output['Dang', 'that', 'is', 'some', 'rad', '@AbzuGame', '#fanart', '!', ':D', 'https://t.co/bI8k8tb9ht']
['dang', 'rad', '#fanart', ':d']
テキストの前処理中に発生する可能性のある特定の問題があります。 たとえば、スペースのない単語(「iLoveYou」)は1つとして扱われ、そのような単語を分離することは困難です。 さらに、「Hi」、「Hii」、および「Hiiiii」は、問題に取り組むための特定の何かを記述しない限り、スクリプトによって異なる方法で処理されます。 特定のデータのノイズ除去プロセスを微調整することは一般的です。
remove_noise()
関数の動作を確認したので、コメントアウトするか、スクリプトから最後の2行を削除して、さらに追加できるようにしてください。
nlp_test.py
...
#print(positive_tweet_tokens[500])
#print(positive_cleaned_tokens_list[500])
このステップでは、分析をより効果的にするためにデータからノイズを除去しました。 次のステップでは、データを分析して、サンプルデータセットで最も一般的な単語を見つけます。
[[step-5 -—- determining-word-density]] ==ステップ5—単語密度の決定
テキストデータの分析の最も基本的な形式は、単語の頻度を取り出すことです。 単一のツイートは、単語の分布を見つけるにはエンティティが小さすぎるため、単語の頻度の分析は、すべての肯定的なツイートで行われます。
次のスニペットは、get_all_words
という名前のa generator functionを定義します。これは、ツイートのリストを引数として取り、結合されたすべてのツイートトークン内の単語のリストを提供します。 次のコードをnlp_test.py
ファイルに追加します。
nlp_test.py
...
def get_all_words(cleaned_tokens_list):
for tokens in cleaned_tokens_list:
for token in tokens:
yield token
all_pos_words = get_all_words(positive_cleaned_tokens_list)
ツイートのサンプルに含まれるすべての単語をまとめたので、NLTKのFreqDist
クラスを使用して、最も一般的な単語を見つけることができます。 次のコードをnlp_test.py
ファイルに追加します。
nlp_test.py
from nltk import FreqDist
freq_dist_pos = FreqDist(all_pos_words)
print(freq_dist_pos.most_common(10))
.most_common()
メソッドは、データで最も頻繁に出現する単語をリストします。 これらの変更を行った後、ファイルを保存して閉じます。
ここでファイルを実行すると、データ内で最も一般的な用語が見つかります。
Output[(':)', 3691),
(':-)', 701),
(':d', 658),
('thanks', 388),
('follow', 357),
('love', 333),
('...', 290),
('good', 283),
('get', 263),
('thank', 253)]
このデータから、絵文字エンティティが肯定的なツイートの最も一般的な部分を形成していることがわかります。 次の手順に進む前に、上位10個のトークンを出力するスクリプトの最後の行をコメント化してください。
要約すると、nltk
からツイートを抽出し、トークン化、正規化、およびモデルで使用するためのツイートのクリーンアップを行いました。 最後に、データ内のトークンの頻度も調べて、上位10個のトークンの頻度を確認しました。
次のステップでは、センチメント分析用のデータを準備します。
[[step-6 -—- preparing-data-for-the-model]] ==ステップ6—モデルのデータを準備する
感情分析は、執筆中のトピックに関する著者の態度を特定するプロセスです。 モデルをトレーニングするためのトレーニングデータセットを作成します。 これは、教師付き学習機械学習プロセスであり、各データセットをトレーニングの「センチメント」に関連付ける必要があります。 このチュートリアルでは、モデルで「ポジティブ」と「ネガティブ」の感情を使用します。
感情分析を使用して、テキストをさまざまな感情に分類できます。 トレーニングデータセットの単純さと可用性のために、このチュートリアルでは、ポジティブとネガティブの2つのカテゴリのみでモデルをトレーニングできます。
モデルは、ルールと方程式を使用したシステムの記述です。 それは、身長を考慮して、人の体重を予測する方程式と同じくらい簡単かもしれません。 作成するセンチメント分析モデルは、ツイートをポジティブまたはネガティブな感情に関連付けます。 データセットを2つの部分に分割する必要があります。 最初の部分の目的はモデルを構築することですが、次の部分ではモデルのパフォーマンスをテストします。
データ準備ステップでは、トークンを辞書形式に変換して感情分析用のデータを準備し、トレーニングとテストの目的でデータを分割します。
トークンを辞書に変換する
最初に、モデルにフィードするデータを準備します。 NLTKのNaive Bayes classifierを使用して、モデリングの演習を実行します。 モデルには、ツイート内の単語のリストだけでなく、単語をキーとして、True
を値として持つPython辞書が必要であることに注意してください。 次の関数は、生成されたデータの形式を変更するジェネレーター関数を作成します。
次のコードを追加して、ツイートをクリーンアップされたトークンのリストから、キーをトークンとして、True
を値として持つ辞書に変換します。 対応する辞書はpositive_tokens_for_model
とnegative_tokens_for_model
に格納されます。
nlp_test.py
...
def get_tweets_for_model(cleaned_tokens_list):
for tweet_tokens in cleaned_tokens_list:
yield dict([token, True] for token in tweet_tokens)
positive_tokens_for_model = get_tweets_for_model(positive_cleaned_tokens_list)
negative_tokens_for_model = get_tweets_for_model(negative_cleaned_tokens_list)
モデルのトレーニングとテストのためのデータセットの分割
次に、NaiveBayesClassifier
クラスをトレーニングするためのデータを準備する必要があります。 ファイルに次のコードを追加して、データを準備します。
nlp_test.py
...
import random
positive_dataset = [(tweet_dict, "Positive")
for tweet_dict in positive_tokens_for_model]
negative_dataset = [(tweet_dict, "Negative")
for tweet_dict in negative_tokens_for_model]
dataset = positive_dataset + negative_dataset
random.shuffle(dataset)
train_data = dataset[:7000]
test_data = dataset[7000:]
このコードは、各ツイートにPositive
またはNegative
のラベルを付けます。 次に、ポジティブツイートとネガティブツイートを結合してdataset
を作成します。
デフォルトでは、データにはすべての肯定的なツイートに続いてすべての否定的なツイートが順番に含まれています。 モデルをトレーニングする場合、バイアスを含まないデータのサンプルを提供する必要があります。 バイアスを回避するために、random
の.shuffle()
メソッドを使用してデータをランダムに配置するコードを追加しました。
最後に、コードはシャッフルされたデータをそれぞれトレーニングとテストのために70:30の比率に分割します。 ツイートの数は10000なので、シャッフルされたデータセットの最初の7000個のツイートをモデルのトレーニングに使用し、最後の3000個をモデルのテストに使用できます。
この手順では、クリーンアップされたトークンを辞書形式に変換し、データセットをランダムにシャッフルし、トレーニングデータとテストデータに分割しました。
[[step-7 --- building-and-testing-the-model]] ==ステップ7—モデルの構築とテスト
最後に、NaiveBayesClassifier
クラスを使用してモデルを構築できます。 .train()
メソッドを使用してモデルをトレーニングし、.accuracy()
メソッドを使用してテストデータでモデルをテストします。
nlp_test.py
...
from nltk import classify
from nltk import NaiveBayesClassifier
classifier = NaiveBayesClassifier.train(train_data)
print("Accuracy is:", classify.accuracy(classifier, test_data))
print(classifier.show_most_informative_features(10))
コードを追加した後、ファイルを保存して閉じ、実行します。 コードの出力は次のようになります。
OutputAccuracy is: 0.9956666666666667
Most Informative Features
:( = True Negati : Positi = 2085.6 : 1.0
:) = True Positi : Negati = 986.0 : 1.0
welcome = True Positi : Negati = 37.2 : 1.0
arrive = True Positi : Negati = 31.3 : 1.0
sad = True Negati : Positi = 25.9 : 1.0
follower = True Positi : Negati = 21.1 : 1.0
bam = True Positi : Negati = 20.7 : 1.0
glad = True Positi : Negati = 18.1 : 1.0
x15 = True Negati : Positi = 15.9 : 1.0
community = True Positi : Negati = 14.1 : 1.0
精度は、モデルが感情を正しく予測できたテストデータセット内のツイートの割合として定義されます。 テストセットの99.5%の精度はかなり良いです。
最も有益な機能を示す表では、出力のすべての行に、トレーニングデータセットの正と負のタグ付きツイートでのトークンの発生率が表示されます。 データの最初の行は、トークン:(
を含むすべてのツイートで、ネガティブツイートとポジティブツイートの比率が2085.6
対1
であることを示しています。 興味深いことに、ポジティブデータセットには:(
のトークンが1つあったようです。 テキストの上位2つの識別項目が顔文字であることがわかります。 さらに、sad
などの単語は否定的な感情につながりますが、welcome
やglad
は肯定的な感情に関連付けられています。
次に、モデルがTwitterからのランダムなツイートでどのように機能するかを確認できます。 このコードをファイルに追加します。
nlp_test.py
...
from nltk.tokenize import word_tokenize
custom_tweet = "I ordered just once from TerribleCo, they screwed up, never used the app again."
custom_tokens = remove_noise(word_tokenize(custom_tweet))
print(classifier.classify(dict([token, True] for token in custom_tokens)))
このコードを使用すると、custom_tweet
変数に関連付けられた文字列を更新して、カスタムツイートをテストできます。 これらの変更を行った後、ファイルを保存して閉じます。
スクリプトを実行して、カスタムテキストを分析します。 例のカスタムテキストの出力は次のとおりです。
Output'Negative'
肯定的なツイートを正しく特徴付けているかどうかも確認できます。
nlp_test.py
...
custom_tweet = 'Congrats #SportStar on your 7th best goal from last season winning goal of the year :) #Baller #Topbin #oneofmanyworldies'
これが出力です:
Output'Positive'
ポジティブな感情とネガティブな感情の両方をテストしたので、変数を更新して、皮肉のようなより複雑な感情をテストします。
nlp_test.py
...
custom_tweet = 'Thank you for sending my baggage to CityX and flying me to CityY at the same time. Brilliant service. #thanksGenericAirline'
これが出力です:
Output'Positive'
モデルはこの例を陽性と分類しました。 これは、トレーニングデータが、皮肉なツイートを否定的なものとして分類するほど包括的ではなかったためです。 モデルで皮肉を予測する場合は、それに応じてトレーニングするために十分な量のトレーニングデータを提供する必要があります。
このステップでは、モデルを構築してテストしました。 また、特定の例で皮肉を検出しないなど、その制限のいくつかを検討しました。 完成したコードには、チュートリアルの後に残ったアーティファクトが残っているため、次のステップでは、コードをPythonのベストプラクティスに合わせる手順を説明します。
[[step-8 -—- cleaning-up-the-code-optional]] ==ステップ8—コードのクリーンアップ(オプション)
チュートリアルは完了していますが、プログラミングのベストプラクティスに従うように、nlp_test.py
ファイルのコードを再編成することをお勧めします。 ベストプラクティスに従って、コードは次の基準を満たす必要があります。
-
すべてのインポートはファイルの先頭にある必要があります。 同じライブラリからのインポートは、1つのステートメントにグループ化する必要があります。
-
すべての関数は、インポート後に定義する必要があります。
-
ファイル内のすべてのステートメントは、
if __name__ == "__main__":
条件で格納する必要があります。 これにより、ファイルの機能を別のファイルにインポートしている場合にステートメントが実行されないことが保証されます。
また、新しいremove_noise
関数によってレンマ化が完了するため、チュートリアルに従ってコメントアウトされたコードとlemmatize_sentence
関数を削除します。
nlp_test.py
のクリーンアップされたバージョンは次のとおりです。
from nltk.stem.wordnet import WordNetLemmatizer
from nltk.corpus import twitter_samples, stopwords
from nltk.tag import pos_tag
from nltk.tokenize import word_tokenize
from nltk import FreqDist, classify, NaiveBayesClassifier
import re, string, random
def remove_noise(tweet_tokens, stop_words = ()):
cleaned_tokens = []
for token, tag in pos_tag(tweet_tokens):
token = re.sub('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+#]|[!*\(\),]|'\
'(?:%[0-9a-fA-F][0-9a-fA-F]))+','', token)
token = re.sub("(@[A-Za-z0-9_]+)","", token)
if tag.startswith("NN"):
pos = 'n'
elif tag.startswith('VB'):
pos = 'v'
else:
pos = 'a'
lemmatizer = WordNetLemmatizer()
token = lemmatizer.lemmatize(token, pos)
if len(token) > 0 and token not in string.punctuation and token.lower() not in stop_words:
cleaned_tokens.append(token.lower())
return cleaned_tokens
def get_all_words(cleaned_tokens_list):
for tokens in cleaned_tokens_list:
for token in tokens:
yield token
def get_tweets_for_model(cleaned_tokens_list):
for tweet_tokens in cleaned_tokens_list:
yield dict([token, True] for token in tweet_tokens)
if __name__ == "__main__":
positive_tweets = twitter_samples.strings('positive_tweets.json')
negative_tweets = twitter_samples.strings('negative_tweets.json')
text = twitter_samples.strings('tweets.20150430-223406.json')
tweet_tokens = twitter_samples.tokenized('positive_tweets.json')[0]
stop_words = stopwords.words('english')
positive_tweet_tokens = twitter_samples.tokenized('positive_tweets.json')
negative_tweet_tokens = twitter_samples.tokenized('negative_tweets.json')
positive_cleaned_tokens_list = []
negative_cleaned_tokens_list = []
for tokens in positive_tweet_tokens:
positive_cleaned_tokens_list.append(remove_noise(tokens, stop_words))
for tokens in negative_tweet_tokens:
negative_cleaned_tokens_list.append(remove_noise(tokens, stop_words))
all_pos_words = get_all_words(positive_cleaned_tokens_list)
freq_dist_pos = FreqDist(all_pos_words)
print(freq_dist_pos.most_common(10))
positive_tokens_for_model = get_tweets_for_model(positive_cleaned_tokens_list)
negative_tokens_for_model = get_tweets_for_model(negative_cleaned_tokens_list)
positive_dataset = [(tweet_dict, "Positive")
for tweet_dict in positive_tokens_for_model]
negative_dataset = [(tweet_dict, "Negative")
for tweet_dict in negative_tokens_for_model]
dataset = positive_dataset + negative_dataset
random.shuffle(dataset)
train_data = dataset[:7000]
test_data = dataset[7000:]
classifier = NaiveBayesClassifier.train(train_data)
print("Accuracy is:", classify.accuracy(classifier, test_data))
print(classifier.show_most_informative_features(10))
custom_tweet = "I ordered just once from TerribleCo, they screwed up, never used the app again."
custom_tokens = remove_noise(word_tokenize(custom_tweet))
print(custom_tweet, classifier.classify(dict([token, True] for token in custom_tokens)))
結論
このチュートリアルでは、Python 3のnltk
ライブラリを使用した基本的な感情分析モデルを紹介しました。 まず、ツイートのトークン化、単語の正規化、およびノイズの除去により、ツイートの前処理を実行しました。 次に、データ内で頻繁に発生するアイテムを視覚化しました。 最後に、ツイートを特定の感情に関連付けるモデルを構築しました。
教師あり学習モデルは、トレーニングデータと同じくらい優れています。 モデルをさらに強化するには、興奮や怒りなどのカテゴリを追加することを検討できます。 このチュートリアルでは、初歩的なモデルを作成することで表面をスクラッチしただけです。 感情分析を実行する際に注意しなければならないa detailed guide on various considerationsは次のとおりです。