NLP、GridDB、Pythonを用いた映画レビューの分類

はじめに

このチュートリアルでは、NLPモデルを使って、感情分析に基づいて映画のレビューを分類します。これはアプリケーションベースのチュートリアルで、Allen NLPライブラリの事前学習済みLSTMモデルを使用します。チュートリアルの概要は以下の通りです。

  1. 環境を設定する
  2. データセットについて
  3. データの前処理を行う
  4. Allen NLPモデルをロードする
  5. 予測を立てる
  6. 結果を評価する

このJupyterファイルは、こちらのGitHub Pageにあります。

環境を設定する

このチュートリアルは、Jupyter Notebooks (Anaconda version 4.8.3)と Python version 3.8 を使用して、Windows 10 オペレーティングシステム上で行われます。コードを読み進める前に、以下のパッケージをインストールしてください。

  1. Pandas
  2. allennlp
  3. allennlp-models
  4. nltk
  5. scikit-learn

pipまたはcondaを用いて上記のパッケージをインストールすることができます。コマンドラインでpip install package-name または conda install package-name と入力してください。

PythonでGridDBのデータベースにアクセスするには、以下のパッケージが必要となります。

  1. GridDB C-client
  2. SWIG (Simplified Wrapper and Interface Generator)
  3. GridDB Python-client

データセットについて

Kaggleで公開されているIMDB感情分析データセットを使用します。このデータセットの形式は非常にシンプルで、次の2つの属性を持っています。

  1. Movie Review (string)
  2. Sentiment Label (int) – Binary

ラベル’0’は否定的な映画レビューを表し、’1’は肯定的な映画レビューを表します。今回は事前に学習されたモデルを使用するため、学習データセットと検証データセットをダウンロ ードする必要はありません。今回は、5000 個のインスタンスを持つテストデータセットのみを使用します。データセットをダウンロードしたら、同じ作業ディレクトリに置いてください。

それでは、Python 環境にデータセットをロードしてみましょう。

データを読み込む

GridDB では、python-client を使ってデータベースを直接呼び出し、pandas データフレームの形式でロードすることができるので、簡単にデータを扱うことができます。

import griddb_python as griddb
import pandas as pd

sql_statement = ('SELECT * FROM movie_review_test')
movie_review_test = pd.read_sql_query(sql_statement, cont)

cont変数には、データを保存しているコンテナ情報が入っています。Pandasを使ったGridDBへの読み書きについての詳しいチュートリアルをこちらのブログで公開しています。

また、CSVファイルがあれば、pandasのread_csv()関数を使うこともできます。どちらの場合も、結果は同じです。

import pandas as pd

movie_review_test = pd.read_csv("movie_review_test.csv")

最初の5つの行を印刷して、データを少し覗いてみましょう。

movie_review_test.head()
text label
0 I always wrote this series off as being a comp… 0
1 1st watched 12/7/2002 – 3 out of 10(Dir-Steve … 0
2 This movie was so poorly written and directed … 0
3 The most interesting thing about Miryang (Secr… 1
4 when i first read about “berlin am meer” i did… 0
len(movie_review_test)
5000

データの前処理を行う

データの前処理は、機械学習モデルから予想外の動作が生じないようにするための重要なステップです。Null値や欠損値を適切に処理しないと、全体的な結果がおかしくなる可能性があります。ここでは、データにNull値が含まれていないかどうかを確認します。

movie_review_test.isna().sum()
text     0
label    0
dtype: int64

幸いなことに、今回のテストデータセットには、Nullまたはミスの値はありませんでした。しかし、Null値が発生した場合は、次に進む前にその値を削除するか、置き換えることを検討してください。

句読点やストップワードを削除する

句読点やストップワードは、テキストの総語数制限を増やしてしまいます。これらはモデルの学習には寄与せず、主にノイズとして機能します。そのため、学習ステップの前にこれらを除去することが重要です。今回のケースでは、トレーニングステップはありませんが、提供される入 力が有効で適切であることを確認したいと考えます。このステップは、トレーニングデータセットに対しても拡張することができます。

様々なライブラリがストップワードのリストを提供しています。今回は nltk ライブラリを使用します。ストップワードのリストは、パッケージによって異なることに注意してください。他のライブラリ(例えばspacy)を使用している場合は、若干異なる結果になるかもしれません。

from nltk.corpus import stopwords
import nltk
stop = stopwords.words('english')
len(stop)
179
type(stop)
list

これで179のストップワードのリストができました。このリストには、独自の単語を追加することもできます。実際に、いくつかの単語をストップワードのリストに追加してみましょう。

extra_words = ['Yeah', 'Okay']
for word in extra_words:
    if word not in stop:
        stop.append(word)
len(stop)
181

あるいは、extend()を使って、リストのすべての項目を追加することもできます。for ループ内のif条件は、同じ単語を2回追加しないようにしています。

movie_review_test['text'] = movie_review_test['text'].apply(lambda words: ' '.join(word for word in words.split() if word not in stop))
movie_review_test.head()
text label
0 I always wrote series complete stink-fest Jim … 0
1 1st watched 12/7/2002 – 3 10(Dir-Steve Purcell… 0
2 This movie poorly written directed I fell asle… 0
3 The interesting thing Miryang (Secret Sunshine… 1
4 first read “berlin meer” expect much. thought … 0

ご覧の通り、’I’や’we’などの人称代名詞が削除されています。続けて、句読点も削除してみましょう。

movie_review_test['text'] = movie_review_test['text'].str.lower()
movie_review_test['text'] = movie_review_test['text'].str.replace('[^\w\s]','')
movie_review_test.head()
text label
0 i always wrote series complete stinkfest jim b… 0
1 1st watched 1272002 3 10dirsteve purcell typi… 0
2 this movie poorly written directed i fell asle… 0
3 the interesting thing miryang secret sunshine … 1
4 first read berlin meer expect much thought rig… 0

これでデータの準備ができたので、次はモデルをロードして、予測を始めます。

Allen NLPモデルをロードする

Allen NLPは、様々な問題文を対象とした多くの機械学習モデルを提供しています。ここでは、映画レビューのデータセットに、GLoVE-LSTM binary classifierを使用します。公式ドキュメントによると、このモデルはStanford Sentiment Treebankで87%の全体的な精度を達成しています。このモデルのlive demoがallennlp社の公式サイトで公開されています。

では、早速、Predictorをロードしてみましょう。

from allennlp.predictors.predictor import Predictor
import allennlp_models.tagging
predictor = Predictor.from_path("https://storage.googleapis.com/allennlp-public-models/basic_stanford_sentiment_treebank-2020.06.09.tar.gz")
error loading _jsonnet (this is expected on Windows), treating C:\Users\SHRIPR~2\AppData\Local\Temp\tmpfjmtd8u3\config.json as plain json

なお、これらのモデルは重いので、GPU を搭載したシステムをお使いの場合は、上記のpredictor関数に引数predictorを渡してください。

Predictorが正常に動作しているかどうかを確認するために、サンプルのテキストレビューを渡して、どのような出力が得られるかを見てみましょう。

sample_review = "This movie was so great. I laughed and cried, a lot!"
predictor.predict(sample_review)
'0'

ご覧のように、予測モデルは5つのキー、logitsprobstoken_idslabeltokens を持つ辞書を返します。サンプルのレビューがポジティブなものであることがわかっているので、モデルは正しくlabel '1'を返したと言えます。

ラベルに加えて、probsリストは、各ラベルの信頼度スコアまたは確率も示しており、ここでは0または1となっています。probsリストの最初の項目、つまりラベル’1’の確率は0.98(または98%)で、これはモデルがレビューが肯定的であることを98%確信していたことを意味します。

Predictorが正常に動作していることがわかったので、今度は予測をしてみましょう。

予測を立てる

ここでは、映画のレビューを受け取り、そのラベルを整数で返すpredict関数を定義します。元 のラベルはint型であることに注意してください。同じデータ型であれば、実際の値と予測値 の比較が容易になります。

def predict_review(movie_review):
    return (int(predictor.predict(movie_review)['label']))
movie_review_test['predicted_label'] = movie_review_test['text'].apply(predict_review)
movie_review_test.head()
text label predicted_label
0 I always wrote this series off as being a comp… 0 1
1 1st watched 12/7/2002 – 3 out of 10(Dir-Steve … 0 0
2 This movie was so poorly written and directed … 0 0
3 The most interesting thing about Miryang (Secr… 1 1
4 when i first read about “berlin am meer” i did… 0 1

あとは、モデルの精度を計算するだけです。予測セルは CPU で実行していたため、5000インスタンスの実行に6分かかりましたが、これらのモデルは重い可能性があります。このコードを大規模なデータに利用する場合は、GPUの使用を検討してください。

結果を評価する

Allen NLPには、評価のための独自の指標があります。ここでは簡単にするために、scikit- learnライブラリを使用します。Allen NLPの評価基準についての詳細はこちらを参照してください。

from sklearn.metrics import accuracy_score
actual = movie_review_test['label']
predicted = movie_review_test['predicted_label']
accuracy = accuracy_score(actual, predicted)
accuracy
0.7208

今回のモデルは、テストデータセットで 72%の総合的な精度を示しました。これはなかなか良い結果であると言えるでしょう。予測結果は、pd.to_csv(file_path)を使って CSVファイルに保存できます。それでは、実際にコードを試してみてください。

Happy coding!

ブログの内容について疑問や質問がある場合は Q&A サイトである Stack Overflow に質問を投稿しましょう。 GridDB 開発者やエンジニアから速やかな回答が得られるようにするためにも "griddb" タグをつけることをお忘れなく。 https://stackoverflow.com/questions/ask?tags=griddb

Leave a Reply

Your email address will not be published. Required fields are marked *