「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_) # そのときのパラメータ