Python
scikit-learn

scikit-learnのtutorialをやってみた(Working With Text Data: Exercise 2)

Exercise 1に引き続き2もやったのでメモ書きしておきます。

環境

  • macOS High Sierra(10.13.4)
  • Python 3.5.2

資料

内容

今回の課題では映画のレビュー結果を学習し、ポジティブな意見かネガティブな意見かを判定するモデルを作成します。grid-searchを使い、unigramとbigramの比較も行います。前回同様exercise_02_sentiment.pyの空所を埋めていきます。

TASK: Build a vectorizer / classifier pipeline that filters out tokens that are too rare or too frequent

データの前処理、特徴量の抽出、学習を一度に実行するためのpiplelineのインスタンスを作成します。

exercise_02_sentiment.py
from sklearn.feature_extraction.text import TfidfTransformer
# Pipelineのインスタンスを作成
text_clf=Pipeline([
    ('vect',TfidfVectorizer(max_df=0.99, min_df=0.01)),
    ('tfidf',TfidfTransformer()),
    ('clf', LinearSVC())])

min_dfでほとんど出てこない特徴量、max_dfで逆に多すぎる特徴量のフィルタリングをおこなっています。決め打ちでmin_dfを0.01、max_dfを0.99にしました。

TASK: Build a grid search to find out whether unigrams or bigrams are more useful.

grid-searchを実行するインスタンスを作成します。コードは以下です。

exercise_02_sentiment.py
# GridSearchCVのインスタンスを作成
parameters={'vect__ngram_range':[(1,1),(2,2)]}
gs_clf=GridSearchCV(text_clf, parameters, n_jobs=-1, cv=5, verbose=1)

parametersという変数にgrid_searchで検証するパラメータのリストを設定しています。今回の課題ではunigramかbigramの比較をするので、ngram_rangeに(1, 1), (2 , 2)の二つのパラメータを指定しています。cvはN-folds cross validationのNを決めるパラメータです。上記なら5-folds cross validationになります。verboseはgrid-search実行時の出力内容を制御するパラメータで、1にしておくと実行時間等が見れて良い感じです。

TASK: print the cross-validated scores for the each parameters set explored by the grid search

作成したgrid-searchのインスタンスを使ってパラメータの検証を行います。

exercise_02_sentiment.py
# grid-searchを実行
gs_clf=gs_clf.fit(docs_train, y_train)

# 得られたパラメータを表示
print(gs_clf.best_score_)
for param_name in sorted(parameters.keys()):
    print("%s: %r" % (param_name, gs_clf.best_params_[param_name]))

TASK: Predict the outcome on the testing set and store it in a variable named y_predicted

grid-searchで得られたパラメータを使ってモデルを学習し直し、テストデータでモデルを検証します。

exercise_02_sentiment.py
# モデルの再構築
text_clf=Pipeline([
    ('vect',TfidfVectorizer(max_df=0.99, min_df=0.01,
    ngram_range=gs_clf.best_params_['vect__ngram_range'])),
    ('tfidf',TfidfTransformer()),
    ('clf', LinearSVC())])
# モデルの学習
text_clf.fit(docs_train, y_train)
# テストデータで検証
y_predicted=text_clf.predict(docs_test)

gs_clf(GridSearchCVのインスタンス)のbest_params_にgrid-searchの結果得られた最良のパラメータがdictとして格納されているので、パラメータ名をkeyにして取り出してngram_rangeに設定します。

実行結果

まずはunigramとbigramの比較結果です。unigramが選ばれています。

MBP:TEST_scikit-learn user$ ipython
Python 3.5.2 (default, Sep 10 2016, 22:20:29)
(snip)
In [1]: %run exercise_02_sentiment.py text_analytics/data/movie_reviews/txt_sentoken/
n_samples: 2000
Fitting 5 folds for each of 2 candidates, totalling 10 fits
[Parallel(n_jobs=-1)]: Done  10 out of  10 | elapsed:   15.1s finished
0.827333333333
vect__ngram_range: (1, 1)
             precision    recall  f1-score   support

    neg       0.86      0.81      0.83       252
    pos       0.82      0.86      0.84       248


avg / total       0.84      0.84      0.84       500

[[204  48]
 [ 34 214]]

せっかくなので他のパラメータについてもgrid-searchをかけてみました。変更した箇所は以下です。
exercise_02_sentiment.py
# TASK: Build a vectorizer / classifier pipeline that filters out tokens that are too rare or too frequent
text_clf=Pipeline([
    ('vect',TfidfVectorizer()),
    ('tfidf',TfidfTransformer()),
    ('clf', LinearSVC())])
# TASK: Build a grid search to find out whether unigrams or bigrams are more useful.
parameters={'vect__min_df': [0.0, 1], 
    'vect__max_df': [0.1, 1.0], 
    'vect__ngram_range': [(1,1) ,(1, 2),
    (2,2)], 'clf__C':[0.5, 1.0]}

# TASK: Predict the outcome on the testing set and store it in a variable named y_predicted
text_clf=Pipeline([
    ('vect',TfidfVectorizer(
    max_df=gs_clf.best_params_['vect__max_df'],
    min_df=gs_clf.best_params_['vect__min_df'],
    ngram_range=gs_clf.best_params_['vect__ngram_range'])),
    ('tfidf',TfidfTransformer()),
    ('clf', LinearSVC(C=gs_clf.best_params_['clf__C']))])

結果は以下です。

n_samples: 2000
Fitting 5 folds for each of 24 candidates, totalling 120 fits
[Parallel(n_jobs=-1)]: Done  42 tasks      | elapsed:  1.7min
[Parallel(n_jobs=-1)]: Done 120 out of 120 | elapsed:  4.9min finished
0.822666666667
clf_C: 1.0
vectmax_df: 1.0
vectmin_df: 0.0
vect_ngram_range: (1, 2)
             precision    recall  f1-score   support

    neg       0.86      0.80      0.83       254
    pos       0.81      0.86      0.83       246


avg / total       0.83      0.83      0.83       500

[[204  50]
 [ 34 212]]

ngram_rangeが(1,2)の場合、unigramとbigramのmixになります。結局bigramの方が微妙に良いという結果に。

おまけ

max_df等を調整せずに特徴量をbigramにしたモデルが一番精度が良かった。

n_samples: 2000
             precision    recall  f1-score   support

    neg       0.86      0.82      0.84       248
    pos       0.83      0.87      0.85       252


avg / total       0.85      0.85      0.85       500

[[204  44]
 [ 32 220]]

まとめ

bigramの方が良いだろうと予想していたのですがこれくらいだと誤差の範囲なんでしょうか、この辺りがよくわからないです。scikit-learnの使い方に少し慣れてきたので、手法やモデルの知識もつけて扱えるようになりたいです。

次の課題

そのうち