NLP for Pythonの連載は今回で5回目です。
前回は、PythonのspaCyライブラリを使って品詞タグ付けと名前付き固有表現認識を行う方法を説明しました。
今回は、Scikit-Learnライブラリを用いて、Twitterのデータからセンチメント分析を行う方法を紹介します。
センチメント分析とは、テキストや画像などのデータを用いて、ほとんど全てのものに関して、何かに対する意見や感情を分析することを指します。
センチメント分析は、企業の意思決定に役立ちます。
例えば、ある商品に対する世論があまり良くない場合、企業は損失を避けるために、その商品を変更したり、生産を完全に停止したりすることができる。
国民感情の情報源は、インタビュー、世論調査、アンケートなど多数あります。
しかし、より多くの人々がソーシャルメディアに参加するようになり、FacebookやTwitterのようなウェブサイトは、国民感情を解析することができるようになりました。
この記事では、テキストデータのセンチメント分析を行う方法について説明します。
問題定義
6つの米国の航空会社に関するツイートが与えられたとき、そのツイートが航空会社に関するポジティブ、ネガティブ、またはニュートラルな感情を含んでいるかどうかを予測するタスクである。
これは典型的な教師あり学習のタスクであり、テキスト文字列が与えられると、テキスト文字列を予め定義されたカテゴリに分類する必要がある。
解決方法
この問題を解決するために、典型的な機械学習パイプラインに従います。
まず、必要なライブラリとデータセットをインポートする。
次に、データセットに何らかの傾向があるかどうかを調べるために、探索的データ分析を行う。
次に、テキストの前処理を行い、テキストデータを機械学習アルゴリズムで利用可能な数値データに変換する。
最後に、機械学習アルゴリズムを使用して、センチメント分析モデルの学習とテストを行います。
必要なライブラリのインポート
いつものように、まずは必要なライブラリをインポートします。
import numpy as np
import pandas as pd
import re
import nltk
import matplotlib.pyplot as plt
%matplotlib inline
注:この記事のスクリプトはすべてJupyter Notebookを使って実行されています。
データセットのインポート
この記事で使用するデータセットは、Githubのリンクから自由に入手することができます。
データセットをインポートするために、以下のようにPandasの read_csv
関数を使用します。
data_source_url = "https://raw.githubusercontent.com/kolaveridi/kaggle-Twitter-US-Airline-Sentiment-/master/Tweets.csv"
airline_tweets = pd.read_csv(data_source_url)
まずは head()
メソッドを使って、データセットがどのように見えるか見てみましょう。
airline_tweets.head()
出力はこのようになります。
データ分析
何か傾向を見つけることができるかどうか、データセットを少し調べてみましょう。
しかし、その前に、プロットをよりよく見るために、デフォルトのプロットサイズを変更します。
以下のスクリプトを実行してください。
plot_size = plt.rcParams["figure.figsize"]
print(plot_size[0])
print(plot_size[1])
plot_size[0] = 8
plot_size[1] = 6
plt.rcParams["figure.figsize"] = plot_size
まず、各航空会社のツイート数を見てみましょう。
円グラフでプロットしてみましょう。
airline_tweets.airline.value_counts().plot(kind='pie', autopct='%1.0f%%')
出力では、各航空会社の公開ツイートのパーセンテージを見ることができます。
ユナイテッド航空が最も多く、26%、次いでUSエアウェイズ(20%)となっています。
それでは、全ツイートにおける感情の分布を見てみましょう。
以下のスクリプトを実行してください。
airline_tweets.airline_sentiment.value_counts().plot(kind='pie', autopct='%1.0f%%', colors=["red", "yellow", "green"])
上のスクリプトの出力は以下のようになります。
出力から、ネガティブなツイートが63%と最も多く、次いでニュートラルなツイートが21%、そしてポジティブなツイートが16%であることがわかります。
次に、航空会社ごとのセンチメントの分布を見てみましょう。
airline_sentiment = airline_tweets.groupby(['airline', 'airline_sentiment']).airline_sentiment.count().unstack()
airline_sentiment.plot(kind='bar')
出力はこのようになります。
出力から明らかなように、ほぼすべての航空会社で、ネガティブなツイートが大半を占め、次いでニュートラル、ポジティブのツイートが続いています。
Virgin Americaは、3つの心情の比率がある程度似ている唯一の航空会社でしょう。
最後に、Seabornライブラリを使用して、3つのセンチメントカテゴリに属するツイートの平均信頼度を表示してみましょう。
以下のスクリプトを実行します。
import seaborn as sns
sns.barplot(x='airline_sentiment', y='airline_sentiment_confidence' , data=airline_tweets)
上のスクリプトの出力は以下のようになる。
この出力から、ネガティブなツイートの信頼度がポジティブやニュートラルなツイートと比べて高いことがわかります。
探索的なデータ分析が終わったら、次はデータに対して何らかの前処理を行い、以下のように数値データをテキストデータに変換します。
データ洗浄
ツイートには多くの俗語や句読点が含まれている。
機械学習モデルの学習に使用する前に、ツイートをクリーニングする必要がある。
しかし、ツイートをクリーニングする前に、データセットを特徴セットとラベルセットに分割してみよう。
特徴セットはツイートのみで構成される。
データセットを見てみると、11列目にツイートのテキストが入っている。
pandasのカラムはゼロベースインデックスを採用しており、最初のカラムを0番目のカラムと呼ぶので、このカラムのインデックスは10であることに注意しましょう。
ラベルセットは予測すべきツイートのセンチメントから構成されます。
ツイートのセンチメントは2列目(インデックス1)にあります。
特徴量とラベルセットを作成するために、pandasのデータフレームから iloc
メソッドを使用することができます。
以下のスクリプトを実行します。
features = airline_tweets.iloc[:, 10].values
labels = airline_tweets.iloc[:, 1].values
データを特徴量と学習セットに分割したら、データをきれいにするために前処理を行います。
そのために、正規表現を使用します。
正規表現については、こちらの記事を参照してください。
processed_features = []
for sentence in range(0, len(features)):
# Remove all the special characters
processed_feature = re.sub(r'W', ' ', str(features[sentence]))
# remove all single characters
processed_feature= re.sub(r's+[a-zA-Z]s+', ' ', processed_feature)
# Remove single characters from the start
processed_feature = re.sub(r'^[a-zA-Z]s+', ' ', processed_feature)
# Substituting multiple spaces with single space
processed_feature = re.sub(r's+', ' ', processed_feature, flags=re.I)
# Removing prefixed 'b'
processed_feature = re.sub(r'^bs+', '', processed_feature)
# Converting to Lowercase
processed_feature = processed_feature.lower()
processed_features.append(processed_feature)
上のスクリプトでは、まずツイートから特殊文字をすべて削除しています。
正規表現「re.sub(r’ W’, ‘ ‘, str(features[sentence]))` 」がこれにあたります。
次に、特殊文字を除去した結果残った1文字を正規表現 re.sub(r's+[a-zA-Z]s+', ' ', processed_feature)
で全て除去する。
例えば、Jack's
から特殊文字'
を取り除き、スペースに置き換えると、Jack s
が残ります。
ここで、s
は意味を持たないので、すべての一文字をスペースに置き換えて削除します。
しかし、すべての一文字をスペースに置き換えると、複数のスペースが生成されます。
そこで、正規表現 re.sub(r's+', ' ', processed_feature, flags=re.I)
を用いて、複数のスペースを全て半角のスペースに置き換えます。
さらに、文字列がbytes形式の場合、文字列の後ろにb
という文字が付加されます。
上記のスクリプトでは、正規表現 re.sub(r'^barette+', '', processed_feature)
を用いてこれを除去しています。
最後に、lower()
関数を用いて小文字に変換しています。
文字列を数値で表現する
統計的アルゴリズムは、機械学習モデルを学習するために数学を使う。
しかし、数学は数字しか扱えない。
統計的アルゴリズムをテキストに適用するためには、まずテキストを数値に変換しなければならない。
そのために、Bag of Words、TF-IDF、Word2Vecの3つのアプローチが存在する。
ここでは、Bag of WordsとTF-IDFの方式について説明する。
ーーバッグオブワーズ
Bag of Words方式は、テキストを数値に変換する最も簡単な方法である。
例えば、3つの文書があるとする。
- Doc1 = “私はサッカーが好きです”
- Doc2 = “サッカーはいいゲームだ”
- Doc3 = “ラグビーよりサッカーが好き”
bag of wordsのアプローチでは、まず、すべてのユニークな単語の語彙を作成することである。
上記の3つの文書では、語彙は次のようになる。
Vocab = [I, like, to, play, football, it, is, a, good, game, prefer, over, rugby]
次に、この語彙を用いて各文書を特徴ベクトルに変換する。
各特徴ベクトルの長さは、語彙の長さと同じである。
文書中の単語の出現頻度は、語彙中の実際の単語を置き換える。
語彙中の単語が対応する文書にない場合、文書特徴ベクトルはその場所を0とする。
例えば、Doc1の場合、特徴ベクトルは以下のようになる。
[1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
TF-IDF
Bag of wordsアプローチでは、各単語は同じ重みを持っている。
TF-IDFの考え方は、すべての文書にあまり出現せず、個々の文書に多く出現する単語は、分類により貢献するというものである。
TF-IDFは、2つの用語の組み合わせである。
用語頻度と逆文書頻度である。
これらは次のように計算される。
TF = (Frequency of a word in the document)/(Total words in the document)
IDF = Log((Total number of docs)/(Number of docs containing the word))
Scikit-Learn ライブラリを利用した TF-IDF
幸運なことに、PythonのScikit-Learnライブラリには TfidfVectorizer
クラスがあり、これを用いてテキスト特徴をTF-IDF特徴ベクトルに変換することができます。
以下のスクリプトはこれを実行する。
from nltk.corpus import stopwords
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer (max_features=2500, min_df=7, max_df=0.8, stop_words=stopwords.words('english'))
processed_features = vectorizer.fit_transform(processed_features).toarray()
上記のコードでは、max_features
を2500と定義しています。
これは、最も頻出する2500個の単語だけを用いてbag of words特徴ベクトルを作成することを意味します。
出現頻度の低い単語はあまり分類に有用ではありません。
同様に、max_df
は、文書の最大80%に出現する単語のみを使用することを指定する。
すべての文書に出現する単語は一般的すぎるため、分類にはあまり役立たない。
同様に、min-df
は7に設定されており、少なくとも7つの文書に出現する単語を含むことを示す。
データをトレーニングセットとテストセットに分ける
前節では、データを数値形式に変換した。
アルゴリズムを学習する前の最後のステップとして、データをトレーニングセットとテストセットに分割する必要がある。
トレーニングセットはアルゴリズムを学習するために使用され、テストセットは機械学習モデルの性能を評価するために使用される。
以下のコードを実行する。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(processed_features, labels, test_size=0.2, random_state=0)
上記のコードでは、sklearn.model_selection
モジュールの train_test_split
クラスを用いて、データをトレーニングセットとテストセットに分割しています。
このメソッドでは、最初のパラメータとして特徴量セット、2番目のパラメータとしてラベルセット、そして test_size
パラメータに値を指定します。
ここでは、test_size
に0.2という値を指定しました。
これは、データセットが80%と20%の2つのデータセットに分割されることを意味します。
80%のデータセットをトレーニングに、20%のデータセットをテストに使用することになります。
モデルのトレーニング
データをトレーニングセットとテストセットに分割したら、トレーニングデータから機械学習アルゴリズムを使用して学習することができる。
どのような機械学習アルゴリズムを用いてもよい。
しかし、ここでは正規化されていないデータを扱うことができる Random Forest アルゴリズムを使用する。
sklearn.ensembleモジュールには
RandomForestClassifierクラスがあり、ランダムフォレストアルゴリズムを用いて機械学習モデルを学習させることができます。
そのためには、RandomForestClassifierクラスの
fit` メソッドを呼び出して、学習用の特徴量とラベルをパラメータとして渡す必要があります。
以下のスクリプトを見てください。
from sklearn.ensemble import RandomForestClassifier
text_classifier = RandomForestClassifier(n_estimators=200, random_state=0)
text_classifier.fit(X_train, y_train)
予測の作成とモデルの評価
モデルの学習が完了したら、最後のステップはモデルに対する予測を行うことです。
そのためには、学習に使用した RandomForestClassifier
クラスのオブジェクトに対して predict
メソッドを呼び出す必要があります。
次のスクリプトを見てください。
predictions = text_classifier.predict(X_test)
最後に、機械学習モデルの性能を評価するために、混乱度、F1値、精度などの分類尺度を利用することができます。
これらのメトリクスの値を求めるには、sklearn.metrics
ライブラリの classification_report
, confusion_matrix
, accuracy_score
ユーティリティを使用します。
以下のスクリプトを見てください。
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
print(confusion_matrix(y_test,predictions))
print(classification_report(y_test,predictions))
print(accuracy_score(y_test, predictions))
上のスクリプトの出力は以下のようなものである。
[[1724 101 45]
[ 329 237 48]
[ 142 58 244]]
precision recall f1-score support
negative 0.79 0.92 0.85 1870
neutral 0.60 0.39 0.47 614
positive 0.72 0.55 0.62 444
micro avg 0.75 0.75 0.75 2928
macro avg 0.70 0.62 0.65 2928
weighted avg 0.74 0.75 0.73 2928
0.7530737704918032
この出力から、我々のアルゴリズムは75.30%の精度を達成したことがわかります。
結論
感情分析は、特定のトピックに関する全体的な世論を決定するのに役立つため、最も一般的に実行される自然言語処理タスクの1つです。
この記事では、様々なPythonライブラリがセンチメント分析の実行にどのように貢献するかを見てきました。
米国の航空会社6社に関する一般人のツイートを分析し、約75%の精度を達成しました。
ロジスティック回帰、SVM、KNNなど他の機械学習アルゴリズムを使ってみて、より良い結果が得られるかどうか試してみることをお勧めします。
次回は、Scikit-Learnでトピックモデリングを行う方法を紹介します。
これは、ドキュメントをグループにクラスタリングすることによって、大量のテキストデータを分析する教師なし手法です。