ナード戦隊データマン

データサイエンスを用いて悪と戦うぞ

tf-idfでテキストデータをスケール変換し映画レビューを分類する

映画レビューのようなテキストデータは、テキストの中の単語(トークン)を抽出し、さらにtheやaのような役に立たない単語を除外してボキャブラリを構築し、トークンの頻度をカウントして利用しなければなりません。ここでは、sklearnでそれらを簡単に行う方法を示します。

データのダウンロードと準備

$ wget http://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz
$ tar -xzvf aclImdb_v1.tar.gz
$ cd aclImdb
$ mkdir train_cp
$ mkdir test_cp
$ cp train/neg/ -r train_cp/
$ cp train/pos/ -r train_cp/
$ cp test/neg/ -r test_cp/
$ cp test/pos/ -r test_cp/

posは良い評価のレビュー、negは悪い評価のレビューです。熱心な研究者がデータを集め、分類してくれているので、wgetでダウンロードして使います。

コード

from sklearn.datasets import load_files

reviews_train = load_files("aclImdb/train_cp")
text_train, y_train = reviews_train.data, reviews_train.target
text_train = [doc.replace(b"<br />", b" ") for doc in text_train]

reviews_test = load_files("aclImdb/test_cp")
text_test, y_test = reviews_test.data, reviews_test.target
text_test = [doc.replace(b"<br />", b" ") for doc in text_test]

from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import make_pipeline
from sklearn.feature_extraction.text import TfidfVectorizer

pipe = make_pipeline(TfidfVectorizer(min_df=5, norm=None), LogisticRegression())
grid = GridSearchCV(pipe, {'logisticregression__C':[0.001, 0.01, 0.1, 1]}, cv=5)
grid.fit(text_train, y_train)
print(grid.score(text_test, y_test))
0.88504

コードの説明

このコードで重要なのは、TfidfVectorizerです。tf-idfとは、「特定の文書だけに頻繁に現れる単語ほど重要であると評価する」というスケール変換です。つまり、文書中の単語を特徴量としています(ただしテキストは英語)。theやaは重要度が低いと判断されます。

tfidfの評価式は以下で与えられます。

f:id:mathgeekjp:20170918201229p:plain

あとは、Pipelineとグリッドサーチを用いて、tfidfでスケール化されたデータを訓練データとし、ロジスティック回帰の正則化定数Cで最も精度の良いものを発見します。

学習された係数を見る

どんな単語に対してどのような係数が割当てられたのか見てみましょう。

%matplotlib inline
import mglearn
import numpy as np

vectorizer = grid.best_estimator_.named_steps["tfidfvectorizer"]
feature_names = np.array(vectorizer.get_feature_names())

mglearn.tools.visualize_coefficients(
    grid.best_estimator_.named_steps["logisticregression"].coef_,
    feature_names, n_top_features=40 
)

f:id:mathgeekjp:20170918201509p:plain

上記を見ると、worst, waste, awfulが含まれるレビューは悪い評価であるのに対し、great, excellent, perfectが含まれるレビューは良いものであると判断されています。直感的にもこの係数の値は的確であるように思えます。

参考

  1. Introduction to Machine Learning with Python - O'Reilly Media
  2. 4.2. Feature extraction — scikit-learn 0.19.0 documentation