LoginSignup
11
12

More than 5 years have passed since last update.

グリッドサーチでPCAをするか決める

Last updated at Posted at 2018-12-12

環境

  • 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のPipelineGridSearchCVを使えば、これらのパラメーターをいっぺんにグリッドサーチしてくれます。

しかし、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)

テストデータで精度を検証します。
スクリーンショット (30).png
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)

テストデータで精度検証
スクリーンショット (29).png
PCA使わない方が精度がいい!

こんなことがあるので、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)

スクリーンショット (32).png
ちゃんとさっきと同じ結果が出ました。

では、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)

スクリーンショット (33).png

例2(PCAなしでSVRのみ)の時と同じ結果が出ました。
PCAを使わないパターンも試して、最良の組み合わせを返してくれたわけです。

まとめ

自分でクラス関数を作ることによって、「そのEstimatorを使わないパターン」も含めてGridSearchCVが検証してくれました。
今回はPCAでやりましたが、ほかの前処理についても同じように適用できるのではないかと思います。

注)
この記事には色々と間違いがあるかもしれないので、何かに使う場合はご注意ください。
もっと楽にやる方法があったら教えてください。
 
今回使ったコードのipynbファイル

11
12
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
11
12