Bag-of-Wordsを用いたセンチメント分析


AIって結局何なのかよく分からないので、とりあえず100日間勉強してみた Day41


経緯についてはこちらをご参照ください。



■本日の進捗

●センチメント分析を理解
●BoWを理解

■はじめに

引き続き「Pythonではじめる機械学習(オライリー・ジャパン)」で学んでいきます。

基本的な機械学習アルゴリズムとその前処理やデータ変換、その他ライブラリは前回までである程度網羅的に触ってみることができました。

今回からはテキストデータに対する機械学習に少し触れてみたいと思います。

■BoW

Bag-of-Words(BoW)とは、情報検索(information retrieval:IR)や自然言語処理(natural language processing:NLP)においてテキストデータを機械学習モデルが扱える形(つまり数値的)に表現する最も単純な手法です。

コーパス(corpus, NLPにおけるデータセットのこと)にあるドキュメント、テキストデータをトークン分割(tokenization)により単語(トークン)に分離し、出現するすべての単語でボキャブラリ構築(vocabulary building)してから、個々のドキュメントに単語が現れる回数をカウントし数値化(エンコード)します。

これはNLPの中でも機械学習ベースのアプローチとして知られ、まさに機械学習の特徴量抽出のような働きをします。この他に予め作成された辞書ベースのアプローチ手法や、RNN(recrrent neural network)やCNN(convolutional neural network)を用いた深層学習ベースのアプローチ手法もあります。

scikit-learnではCountVectorizerクラスでBoWが実装できます。

■インターネットレビューコーパス

まずはデータセットが必要ですが、参考文献の例に倣って映画レビューデータセットをダウンロードしてきます。今回用いるコーパスは、IMDb(Internet Movie Database)という実在する映画レビューサイトのコメントを集めたもので、こちらのLarge Movie Review Datasetからダウンロード可能です。

ダウンロードしたらそのまま展開するか、Powershellやターミナルのダウンロードファイルがあるディレクトリ上で下記のコマンドで解凍しておきます。

scikit-learnではload_files関数でデータの読み込みが可能です。

from sklearn.datasets import load_files

reviews_train = load_files("C:/Users/****/Documents/Python/aclImdb/train")
text_train, y_train = reviews_train.data, reviews_train.target
print("length of train data {}".format(len(text_train)))
print("sample train data :{}".format(text_train[18]))
print("sample train label :{}".format(y_train[18]))

reviews_test = load_files("C:/Users/****/Documents/Python/aclImdb/test")
text_test, y_test = reviews_test.data, reviews_test.target
print("length of test data {}".format(len(text_test)))
print("sample test data :{}".format(text_test[8]))
print("sample test label :{}".format(y_test[8]))

訓練データもテストデータもそれぞれ25000件のレビューで構成されています。サンプルデータを見てみると、訓練データ18には下記のように書いてあります。

黒澤は明らかに人道主義者だ。この映画は貧困にあえぐ人々を描いている。この映画は怒りなしに見れないだろう。嫌な気分になるけれど、それでも価値がある。具体化に満足しすぎている人は、この映画で2時間半を過ごすべきだ。

また、テストデータ8には下記のように書いてあります。

オーシャンズ12:他の2本とは比べ物にならないくらいくだらない、駄作。アートの横取り。10人の有名俳優が出演しているが、弱い台本とつまらないアイディアのせいで、20分しか見ていないがそう評価する。誤解しないでほしいが、もしかしたら気に入る人もいるかもしれない。だが、僕がオーシャンズ映画が好きなのは、強盗というテーマがあるからだ。もし『オーシャンズ12』が強盗がテーマでないなら、何のために見るのだろうか?
ソダーバーグ監督が自分の主な誤りに気づき、最初の作品よりはるかに優れた映画を作ることで挽回したことを嬉しく思う。それは称賛に値する。
スティーブン・ソダーバーグはあまりいい監督ではない。ヒット作「セックス・ライツ・アンド・ビデオテープ」を除けば、この監督のキャリアにこれほど素晴らしい出来事は他になかった。恥ずべきことだ。しかし、それは彼の責任に他ならない。

もうめちゃくちゃです。これぞネットコメントのお手本というべき酷評で、いくらレビューサイトと言えど、公に言っていい範疇を超えていると思うのですが、それは一旦置いておいて、このコメントはClass 0にラベリングされています。明らかにClass 0はネガティブコメントです。

一方訓練データサンプルの方はClass 1にラベリングされていて、一見「angry」や「bad」といったネガティブな単語が入っているものの良く見るとこの映画を評価しています。(映画に詳しい人はこのレビューだけで何の映画か分かりそうですが、あいにく…。)これは明らかにポジティブコメントで、機械学習モデルで正しく分類するには一定の精度が必要になりそうです。

■センチメント分析

センチメント分析とは、テキストデータから感情や意見を自動抽出し、ポジティブ、ネガティブ、(またはニュートラル)などの感情的極性を判定する分類タスクです。最初に言及したBag-of-Wordsなどの手法を用いることでテキストデータを数値化することができます。

from sklearn.datasets import load_files
from sklearn.feature_extraction.text import CountVectorizer

reviews_train = load_files("C:/Users/****/Documents/Python/aclImdb/train")
text_train, y_train = reviews_train.data, reviews_train.target

reviews_test = load_files("C:/Users/****/Documents/Python/aclImdb/test")
text_test, y_test = reviews_test.data, reviews_test.target

vectorizer = CountVectorizer()
vectorizer.fit(text_train)

vocabulary_list = list(vectorizer.vocabulary_.items())[:10]
print("first 10 vocabularies :{}".format(vocabulary_list))

レビュー内に出てきた単語をトークンに分割して、各トークンに数値が割り当てられています。

これをロジスティック回帰に学習させてみます。

import numpy as np
from sklearn.datasets import load_files
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression

reviews_train = load_files("C:/Users/****/Documents/Python/aclImdb/train")
text_train, y_train = reviews_train.data, reviews_train.target

reviews_test = load_files("C:/Users/****/Documents/Python/aclImdb/test")
text_test, y_test = reviews_test.data, reviews_test.target

vectorizer = CountVectorizer()
vectorizer.fit(text_train)

X_train = vectorizer.transform(text_train)
score = cross_val_score(LogisticRegression(max_iter=10000), X_train, y_train, cv=5, n_jobs=-1)
print("mean cv score :{:.2f}".format(np.mean(score)))

BoWで特徴量を数値化して学習しただけで、なかなかの精度で予測できる実力があるそうです。

■おわりに

これまでは極力scikit-learn標準データセットのみを用いてモデルを構築してきました(権利や適用範囲の確認に時間を取られるのは、初学者の学習としては本質的ではないと考えたため)が、今回初めて外部データセットを使ってみました。

やはり実在のデータを噛ませるのは楽しくて、いちいちデータや(裏で実施していた予測モデルの)結果をひとつひとつ確認してしまいます。若干ブラックボックス的で予測経緯は基本分からないですが、よく出てくる単語からここまで学習できるのかと感心したり、皮肉などのいかにも難しそうなレビューで外していたりと機械学習モデルの気持ちに寄り添えそうな気すらしてきます。

そしてデータのダウンロードから予測モデルの構築まで特に手間取ることもなくとても簡単にデータセットを使うことができました。これもひとえにAndrew Maas先生(と恐らく関係者各位)のご尽力の賜物なのでしょう。

Andrew Maas先生と世界中のレビュアーに敬意を表して。

■参考文献

  1. Andreas C. Muller, Sarah Guido. Pythonではじめる機械学習. 中田 秀基 訳. オライリー・ジャパン. 2017. 392p.
  2. ChatGPT. 4o mini. OpenAI. 2024. https://chatgpt.com/
  3. API Reference. scikit-learn.org. https://scikit-learn.org/stable/api/index.html
  4. Maas, Andrew L. and Daly, Raymond E. and Pham, Peter T. and Huang, Dan and Ng, Andrew Y. and Potts, Christopher, Learning Word Vectors for Sentiment Analysis, Proceedings of the 49th Annual Meeting of the Association for Computational Linguistics: Human Language Technologies, June, 2011, Portland, Oregon, USA, Association for Computational Linguistics, 142–150, http://www.aclweb.org/anthology/P11-1015
  5. Potts, Christopher. 2011. On the negativity of negation. In Nan Li and David Lutz, eds., Proceedings of Semantics and Linguistic Theory 20, 636-659.


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です