環境
- Windows 10
- Python 3.7.1
- scikit-learn 0.20.0
この記事でやろうとしていること
機械学習においてメジャーな次元削減法に、PCAがあります。
例えば、PCAで次元を減らしてからSVR(Support Vector Machine Regression)にかける、なんて使い方をするかと思います。
この場合、PCAのパラメーター(成分数: n_componentsとか)とSVRのパラメーター(CとかEpsilonとか)を決める必要があります。
scikit-learnのPipeline
とGridSearchCV
を使えば、これらのパラメーターをいっぺんにグリッドサーチしてくれます。
しかし、GridSearchCV
が検証してくれるのは、全パラメーターを組み合わせたパターンのみです。
PCAをかけないパターンも含めていっぺんに検証してほしいのです。
例えば、こんな例を見てください。
データ準備
例えば、ボストンの住宅価格のデータを使って回帰予測をします。
from sklearn.datasets import load_boston
housing = load_boston()
データの内容については、このあたりの記事をご覧ください。
train_test_split
でトレーニングデータとテストデータに分けます。
from sklearn.model_selction import train_test_split
X_train, X_test, y_train, y_test = train_test_split(housing.data, housing.target,
train_size=0.75, test_size=0.25)
例1) PCA -> SVRのPipelineで解析する
まず、Pipelineに含めるEstimatorを設定します。
from sklearn.svm import SVR
from sklearn.decomposition import PCA
from sklearn.pipeline import Pipeline
pl1 = Pipeline([['pca', PCA(random_state=2018)],
['svr', SVR(kernel='linear')]])
続いて、パラメーターの候補を与えます。
prms1 = {'pca__n_components': [0.1, 0.5, 0.9],
'svr__C': [0.1, 0.5, 1.],
'svr__epsilon': [0.05, 0.10, 0.20]}
PipelineとパラメーターをGrid Searchにかけます。
from sklearn.model_selection import GridSearchCV
gs1 = GridSearchCV(pl1, prms1, n_jobs=-1, return_train_score=True, cv=5, verbose=10)
gs1.fit(X_train, y_train)
テストデータで精度を検証します。
Pearsonの相関係数で.527。
ふむ、こんなものか。
例2) PCAなしでSVRをする
さっきはPCAをSVRに併用して.527の精度を出してましたが、PCAなしだとどうなるでしょう。
prms2 = {'C': [0.1, 0.5, 1.],
'epsilon': [0.05, 0.10, 0.20]}
gs2 = GridSearchCV(SVR(kernel='linear'), prms2, n_jobs=-1, return_train_score=True, cv=5, verbose=10)
gs2.fit(X_train, y_train)
こんなことがあるので、PCAをかけないパターンも一緒に検証してほしいのです。
都合のいいPCAクラス
GridSearchCV
は、パラメーターの全組み合わせしか検証してくれません。
逆に、PCA側がパラメーター次第で「なにもせず」データを返してくれればよいのです。
そんな都合のいいPCAクラスを作成します。
クラスを作る
scikit-learnは、PipelineやGridSearchCVに投げるクラスを自作する方法を用意してくれています。
このあたりの記事を参照しつつ、都合のいいクラスを作ってみました ↓
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.decomposition import PCA
class myPCA(BaseEstimator, TransformerMixin):
def __init__(self, n_components=0, random_state=None):
self.n_components = n_components
self.random_state = random_state
def fit(self, X, y=None):
if self.n_components==0:
return self
self.pca = PCA(n_components=self.n_components, random_state=self.random_state)
self.pca.fit(X)
return self
def transform(self, X, y=None):
if self.n_components==0:
return X
return self.pca.transform(X)
n_components
が0の場合は、PCAを行わず、データX
をそのまま返すようにしています。
0以外の時は、普通にscikit-learnのPCAをかけて返します。
クラスをインポートする
この都合のいいクラスmyPCA
を、別の.py
ファイルに書いて保存し、import
で呼び出します。
こうしないと、並列演算をしたときにエラーが出ます。
from mypca import myPCA
クラスを試す
先ほどの、例1と同じ設定でmyPCA
を試してみましょう。
prms3 = prms1
pl3 = Pipeline([['pca', myPCA(random_state=2018)],
['svr', SVR(kernel='linear')]])
gs3 = GridSearchCV(pl3, prms3, n_jobs=-1, return_train_score=True, cv=5, verbose=10)
gs3.fit(X_train, y_train)
では、n_components
の候補に0を加えてやってみます。
prms4 = {'pca__n_components': [0, 0.1, 0.5, 0.9],
'svr__C': [0.1, 0.5, 1.],
'svr__epsilon': [0.05, 0.10, 0.20]}
pl4 = pl3
gs4 = GridSearchCV(pl4, prms4, n_jobs=-1, return_train_score=True, cv=5, verbose=10)
gs4.fit(X_train, y_train)
例2(PCAなしでSVRのみ)の時と同じ結果が出ました。
PCAを使わないパターンも試して、最良の組み合わせを返してくれたわけです。
まとめ
自分でクラス関数を作ることによって、「そのEstimatorを使わないパターン」も含めてGridSearchCV
が検証してくれました。
今回はPCAでやりましたが、ほかの前処理についても同じように適用できるのではないかと思います。
注)
この記事には色々と間違いがあるかもしれないので、何かに使う場合はご注意ください。
もっと楽にやる方法があったら教えてください。
今回使ったコードのipynbファイル