読者です 読者をやめる 読者になる 読者になる

下校時刻

メモ、読んだ本のまとめメモ、考えたことのメモ、その他のメモなどが書いてあるブログです(twitter: @hoture6)

「Python Machine Learning」Ch. 6(scikit-learnのtips)まとめ

Ch. 6はscikit-learnを使った機械学習におけるいろいろなベストプラクティスの話です。cross validationやパラメータチューニングについての話題が多い。


Pipeline

Pipelineによって、前処理(標準化やPCAなど)から学習までの流れをまとめて管理することができる。

Pipelineはタプルのリストを入力として受け取る。各タプルは2つの要素からなり、1つめは処理の名前を表す文字列、2つめはscikit-learnのtransformer(StandardScalerなど)やestimator(LogisticRegressionなど)である。最後のタプルがestimator、それ以前のタプルがtransformerを表す。例えば、以下のコードのような使い方をする。

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_tr, Y_tr)
pipe_lr.predict(X_ts) # 予測
pipe_lr.score(X_ts, Y_ts) # 正解率

cross-validation

cross-validationの手法には、主にholdout cross-validationとk-fold cross-validationがある。

holdout cross-validation

holdout法では通常、データをトレーニングセットとテストセットに分ける。トレーニングセットを学習に使い、テストセットで予測の性能を評価する。ただし、モデル選択における最適なパラメータの探索などにおいては、何回もテストセットを使うことによりテストセットがトレーニングセットの一部のようになってしまって、過学習に陥る。そこで、トレーニングセットの一部をバリデーションセットとして使うとよい。

ある一つのアルゴリズムについて、トレーニングセットとバリデーションセットを使ってパラメータの最適化を測る。また別のアルゴリズムについても同様にパラメータを最適化し、それぞれの正解率をテストセットで評価することにより、性能を比較することができるのである。

holdout法の欠点としては、トレーニングセットとバリデーションセットをどのように分割するかによって性能が大きく変わりうることが挙げられる。この点を解決するのがk-fold cross-validationである。

k-fold cross-validation

k-fold法では、トレーニングセットをk個に分割する。このうちの1個をバリデーションセットとして、k-1個をトレーニングセットとして使って性能を評価するということをk回行い、その性能の平均を最終的な性能として用いる。これにより、holdout法のように性能がデータの分割に強く依存するということがなくなる。

典型的なkの値は10だが、データセットが小さいときはkを大きくし、データセットが大きいときはkを小さくすることが多い。

以下のように実装する。

from sklearn.cross_validation import StratifiedKFold

kfold = StratifiedKFold(y=y_tr, n_folds=10, random_state=1)
scores = []
for k, (train, test) in enumerate(kfold):
    pipe_lr.fit(X_tr[train], y_tr[train])
    score = pipe_lr.score(X_tr[test], y_tr[test])
    scores.append(score)

print(np.mean(scores))

上のコードではkfold法を露わに使ったけど、実はそんなことをしなくても良い。以下のコードで自動的にkfold法をやってくれる。

from sklearn.cross_validation import cross_val_score

scores = cross_val_score(estimator=pipe_lr, X=X_tr, y=y_tr, cv=10, n_jobs=1)

learning/validation curve

機械学習アルゴリズムの評価をする上で以下の曲線を書くと便利。

  • learning curve:トレーニングサンプル数 vs パフォーマンス
  • validation curve:ハイパーパラメータ vs パフォーマンス

learning curve

learning curveは、bias/varianceのバランスを見るのに使える。biasが高い場合はサンプル数が増えていってパフォーマンスが飽和したところでも、パフォーマンスが低くなってしまう。一方、varianceが高い場合は、サンプル数が増えても、トレーニングセットに対するカーブとバリデーションセットに対するカーブの値の差が大きくなる。

from sklearn.learning_curve import learning_curve

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

# トレーニングサンプル数を0.1倍から1倍(そのまま)まで10段階で変えつつ性能を評価
train_sizes, train_scores, test_scores = learning_curve(estimator=pipe_lr, X=X_tr, y=y_tr, train_sizes=np.linspace(0.1, 1.0, 10, cv=10)) 

validation curve

validation curveはover/underfittingを評価するのに使える。例えば、ロジスティック回帰における正則化のパラメータCを変えながらパフォーマンスを見ることで、どこらへんでちょうど良いバランスになるのかが見られる。また、Pipelineの中のestimatorの引数へのパラメータの代入の方法に注意。

from sklearn.learning_curve import validation_curve

param_range = [0.001, 0.01, 0.1, 1, 10, 100]

# Cを0.001から100まで変えつつ性能を評価
train_scores, test_scores = validation_curve(estimator=pipe_lr, X=X_tr, y=y_tr, param_name='clf__C', param_range=param_range, cv=10)) 

grid search

パラメータのチューニングを行う際にはgrid searchが便利。試したいパラメータのリストを渡すことで、最適なパラメータやその際のパフォーマンスを計算してくれる。

from sklearn.grid_search import GridSearchCV

pipe_svc = Pipeline([('scl', StandardScaler()), ('clf', SVC(random_state=1))])
param_range = [0.001, 0.01, 0.1, 1, 10, 100]
param_grid = [{'clf__C': param_range, 'clf__kernel': ['rbf']}, {'clf__C': param_range, 'clf__kernel': ['linear']}]

gs = GridSearchCV(estimator=pipe_svc, param_grid=param_grid, scoreing='accuracy', cv=10)
gs = gs.fit(X_tr, Y_tr)

print(gs.best_score_) # 最高のパフォーマンス
print(gs.best_params_) # そのときのパラメータ