LoginSignup

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 5 years have passed since last update.

【読書会】python機械学習プログラミング_6章

Last updated at Posted at 2017-01-20

モデルの評価とハイパーパラメータのチューニングのベストプラクティス

この章では、アルゴリズムをチューニングし、モデルの性能を評価することにより、機械学習のよいモデルを構築するベストプラクティスを学ぶ

この章でやることは
1,パイプラインを使う
2,交差検証を使ってモデルの性能を評価する
3,学習曲線と検証曲線を用いてアルゴリズを診断する
4,グリッドサーチを利用して機械学習モデルのチューニングを行う
5,様々な性能評価指標をしる

パイプラインによるワークフローの効率化

Pipelineというクラスを用いれば、任意の個数の変換ステップを含んだモデルを学習し、それを応用して新しいデータを予測できるようになる。

*メリットとかはよくわかっていませんが、処理をまとめられること、トレーニングとテストをまとめられるのが良いところ?なのかなと思っています。

下準備

1,データセットを読み込む

import pandas as pd

df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases'
                 '/breast-cancer-wisconsin/wdbc.data', header=None)

2,NumPy配列の形にする

from sklearn.preprocessing import LabelEncoder

X = df.loc[:, 2:].values
y = df.loc[:, 1].values
le = LabelEncoder()
y = le.fit_transform(y)

3,データセットをトレーニングデータセットとテストデータセットに分割する

if Version(sklearn_version) < '0.18':
    from sklearn.cross_validation import train_test_split
else:
    from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = \
    train_test_split(X, y, test_size=0.20, random_state=1)

パイプラインで変換器と推定器を結合する

この後に分析をかけるにあたって
・入力特徴量の尺度を揃える
・PCAを用いて30次元から2次元に圧縮
・最後にロジスティック回帰
を行う必要がある

ここでパイプラインを使う
トレーニングセットとテストセットの学習と変換を別々に行う代わりに
・standardscaler
・PCA
.logisticgegression
の三つのオブジェクトをパイプラインで結合できる

from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline

pipe_lr = Pipeline([('scl', StandardScaler()),
                    ('pca', PCA(n_components=2)),
                    ('clf', LogisticRegression(random_state=1))])

pipe_lr.fit(X_train, y_train)
print('Test Accuracy: %.3f' % pipe_lr.score(X_test, y_test))
y_pred = pipe_lr.predict(X_test)

pipelineのオブジェクトはタプルのリストを入れる
タプル1:任意の識別文字列
タプル2:scikitlearnの変換器または推定器

上のコードの様に入れることでスケーリング→主成分分析→ロジスティック回帰の流れを一発で行える

06_01.png

k分割交差検証を使ったモデルの性能の評価

機械学習において重要なステップは未知のデータを使って性能を評価することである

その際には、学習不足、過学習という状態に陥らないように入念に評価する必要がある

そこで交差検証法というのがでてくる
ここでは、ホールドアウト法、k分割交差検証法という交差検証法について説明する
これによって、汎化誤差、未知のデータに対するモデル性能を的確に推定できるようになる

ホールドアウト法

これは機械学習の汎化性能を評価するために従来より使用されている一般的なアプローチ

機械学習の性能をあげるためには、パラメータ設定のチューニングや比較を行うことも重要である。
このプロセスをモデル選択という
これは、チューニングパラメータの最適な値を選択する問題を指す
チューニングパラメータはハイパーパラメータとも呼ばれる


機械学習の手法には、必ずと言ってよいほどハイパーパラメータが存在します。 ハイパーパラメータとは、機械学習手法が持つパラメータの中で、データからの学習では決まらないパラメータのことを言います。 例えば、サポートベクターマシン (SVM) における、誤分類に対するペナルティの大きさを制御するパラメータや、ランダムフォレストにおける、個々の決定木に使う特徴量の数などです。 これらのパラメータは、データを学習する前にあらかじめ決めておかなければなりません。
https://book.mynavi.jp/manatee/detail/id=59393


モデル選択においてテストデータを繰り返し使用した場合それは過学習に陥る可能性が高くなる
それを解消するためのものが、ホールドアウト法

06_02.png

データをトレーニングデータセット、検証データセット、テストデータセットの三つに分割する
そしてパラメータ値のチューニング結果に満足したら、テストデータセットでのモデルの汎化誤差を評価する

しかし、これには問題がある
それはデータをどう分割するかによって性能の評価に影響が及ぶ事である
つまり、データのサンプリングによって評価が変わってしまう

k分割交差検証法

トレーニングデータセットのk個のサブセットに対してホールドアウト法をk回繰り返す。

k分割交差検証では、非復元抽出を用いて、トレーニングデータセットをランダムにk個に分割する
そのうちk-1個をモデルのトレーニングに使用し、1個をテストに使用する。
この手順をk回繰り返すことで、k個のモデルと性能評価を取得する

06_03.png

一般にk分割交差検証法はモデルのチューニングに使用される

k分割交差検証で標準的に用いられるkの値は10であり、だいたいはそれで大丈夫なようです。

層化k分割交差検証

k分割交差検証を少し改良したもの
クラスの比率が均等でなかったとしてもバイアスとバリアンスが改善される
層化交差検証では各サブセットでのクラスの比率が維持される


import numpy as np

if Version(sklearn_version) < '0.18':
    from sklearn.cross_validation import StratifiedKFold
else:
    from sklearn.model_selection import StratifiedKFold


if Version(sklearn_version) < '0.18':
    kfold = StratifiedKFold(y=y_train, 
                            n_folds=10,
                            random_state=1)
else:
    kfold = StratifiedKFold(n_splits=10,
                            random_state=1).split(X_train, y_train)

scores = []
for k, (train, test) in enumerate(kfold):
    pipe_lr.fit(X_train[train], y_train[train])
    score = pipe_lr.score(X_train[test], y_train[test])
    scores.append(score)
    print('Fold: %s, Class dist.: %s, Acc: %.3f' % (k+1,
          np.bincount(y_train[train]), score))

print('\nCV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores)))

kfoldイテレータは、k個のサブセットをforループで処理するために使用する

ここまで来てfor文か、、、
と思ったらやはりありました

scikit-learnにはk分割交差検証の性能指標を算出する関数も実装されている

f Version(sklearn_version) < '0.18':
    from sklearn.cross_validation import cross_val_score
else:
    from sklearn.model_selection import cross_val_score

scores = cross_val_score(estimator=pipe_lr,
                         X=X_train,
                         y=y_train,
                         cv=10,
                         n_jobs=1)
print('CV accuracy scores: %s' % scores)
print('CV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores)))

cross_val_score関数は分散処理も行えるそうです

3,学習曲線と検証曲線を用いてアルゴリズを診断する

学習曲線

06_04.png

左上:バイアスが高い
右上:バリアンスが高い

ってのがわかる

これをpythonで出すには

import matplotlib.pyplot as plt

if Version(sklearn_version) < '0.18':
    from sklearn.learning_curve import learning_curve
else:
    from sklearn.model_selection import learning_curve



pipe_lr = Pipeline([('scl', StandardScaler()),
                    ('clf', LogisticRegression(penalty='l2', random_state=0))])

train_sizes, train_scores, test_scores =\
                learning_curve(estimator=pipe_lr,
                               X=X_train,
                               y=y_train,
                               train_sizes=np.linspace(0.1, 1.0, 10),
                               cv=10,
                               n_jobs=1)

train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

plt.plot(train_sizes, train_mean,
         color='blue', marker='o',
         markersize=5, label='training accuracy')

plt.fill_between(train_sizes,
                 train_mean + train_std,
                 train_mean - train_std,
                 alpha=0.15, color='blue')

plt.plot(train_sizes, test_mean,
         color='green', linestyle='--',
         marker='s', markersize=5,
         label='validation accuracy')

plt.fill_between(train_sizes,
                 test_mean + test_std,
                 test_mean - test_std,
                 alpha=0.15, color='green')

plt.grid()
plt.xlabel('Number of training samples')
plt.ylabel('Accuracy')
plt.legend(loc='lower right')
plt.ylim([0.8, 1.0])
plt.tight_layout()
# plt.savefig('./figures/learning_curve.png', dpi=300)
plt.show()

検証曲線

06_06.png

横軸にハイパーパラメータの値をとってそれによる変化を見る
図をみると0.1くらいがちょうど良い

これをコードで書くと

if Version(sklearn_version) < '0.18':
    from sklearn.learning_curve import validation_curve
else:
    from sklearn.model_selection import validation_curve



param_range = [0.001, 0.01, 0.1, 1.0, 10.0, 100.0]
train_scores, test_scores = validation_curve(
                estimator=pipe_lr, 
                X=X_train, 
                y=y_train, 
                param_name='clf__C', 
                param_range=param_range,
                cv=10)

train_mean = np.mean(train_scores, axis=1)
train_std = np.std(train_scores, axis=1)
test_mean = np.mean(test_scores, axis=1)
test_std = np.std(test_scores, axis=1)

plt.plot(param_range, train_mean, 
         color='blue', marker='o', 
         markersize=5, label='training accuracy')

plt.fill_between(param_range, train_mean + train_std,
                 train_mean - train_std, alpha=0.15,
                 color='blue')

plt.plot(param_range, test_mean, 
         color='green', linestyle='--', 
         marker='s', markersize=5, 
         label='validation accuracy')

plt.fill_between(param_range, 
                 test_mean + test_std,
                 test_mean - test_std, 
                 alpha=0.15, color='green')

plt.grid()
plt.xscale('log')
plt.legend(loc='lower right')
plt.xlabel('Parameter C')
plt.ylabel('Accuracy')
plt.ylim([0.8, 1.0])
plt.tight_layout()
# plt.savefig('./figures/validation_curve.png', dpi=300)
plt.show()

4,グリッドサーチを利用して機械学習モデルのチューニングを行う

ここでは、グリッドサーチというハイパーパラメータの最適化手法を取り上げる
グリッドサーチは、ハイパーパラメータの値の「最適な」組み合わせを見つけ出すことにより、モデルの性能をさらに改善するのに役立つ

実際のコード

rom sklearn.svm import SVC
if Version(sklearn_version) < '0.18':
    from sklearn.grid_search import GridSearchCV
else:
    from sklearn.model_selection import GridSearchCV

pipe_svc = Pipeline([('scl', StandardScaler()),
            ('clf', SVC(random_state=1))])

param_range = [0.0001, 0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0]

param_grid = [{'clf__C': param_range, 
               'clf__kernel': ['linear']},
                 {'clf__C': param_range, 
                  'clf__gamma': param_range, 
                  'clf__kernel': ['rbf']}]

gs = GridSearchCV(estimator=pipe_svc, 
                  param_grid=param_grid, 
                  scoring='accuracy', 
                  cv=10,
                  n_jobs=-1)
gs = gs.fit(X_train, y_train)
print(gs.best_score_)
print(gs.best_params_)

このコードではサポートベクトルマシンのトレーニングとチューニングを行なっている

入力したパラメータをしらみつぶしに評価する

もっとも性能が良いモデルのパラメータを取得するにはbest_param_属性を使用する

入れ子式交差検証によるアルゴリズムの選択

様々な機械学習アルゴリズムの中からどれかを選択したい
そんな時にモデル毎の性能を比較するのに使用する

06_07.png

外側のループではk分割交差検証を使って、トレーニングセットとテストサブセットにデータを分割し、性能を評価する。
内側のループでは、トレーニングサブセットに対してk分割交差検証を行う事で、モデルを選択する。

gs = GridSearchCV(estimator=pipe_svc,
                  param_grid=param_grid,
                  scoring='accuracy',
                  cv=2)

# Note: Optionally, you could use cv=2 
# in the GridSearchCV above to produce
# the 5 x 2 nested CV that is shown in the figure.

scores = cross_val_score(gs, X_train, y_train, scoring='accuracy', cv=5)
print('CV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores)))

決定木でもやってみる

from sklearn.tree import DecisionTreeClassifier

gs = GridSearchCV(estimator=DecisionTreeClassifier(random_state=0),
                  param_grid=[{'max_depth': [1, 2, 3, 4, 5, 6, 7, None]}],
                  scoring='accuracy',
                  cv=2)
scores = cross_val_score(gs, X_train, y_train, scoring='accuracy', cv=5)
print('CV accuracy: %.3f +/- %.3f' % (np.mean(scores), np.std(scores)))

すると、
SVM:CV accuracy: 0.965 +/- 0.025
決定木:CV accuracy: 0.921 +/- 0.029
となり、SVMの方が適している事がわかる

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up