sklearn.pipelineを試す
sklearn.pipelineとは
sklearn.pipeline とは、前処理用のScaler(変換器)や機械学習モデルを一括で処理するためのオブジェクトを生成する。
これをおこなうことで、管理が容易になったり処理コード部分を簡潔に書くことができる。
実装
データはボストン住宅価格を使用する。
from sklearn.datasets import load_boston dsn = load_boston() X = pd.DataFrame(dsn.data, columns=dsn.feature_names) y = pd.DataFrame(dsn.target, columns=['target'])

Scaler
Scalerは以下の2つを用いる
StandardScaler
StandardScalerはデータの標準化をおこなう。
from sklearn.preprocessing import StandardScaler scaler = StandardScaler() scaler.fit_transform(X)
このとき、返り値はArrayとなる。

自作Scaler
以下で自作のScalerを作成する。自作Scalerという概念の詳細はそのうち記事に書くので省略。
ここで作成するAddCrim2は特徴量CRIMの値を引数value倍(初期値は2)したCRIM2という特徴量を追加するシンプルな内容(例示用なのでこの処理自体に意味はないです)。
from sklearn.base import BaseEstimator, TransformerMixin class AddCrim2(BaseEstimator, TransformerMixin): def __init__(self,value=2): self.value = value def fit(self, X, y=None): return self def transform(self, X): # 直接の書き換えが起きないようにcopy _X = X.copy() # 新たな特徴量の作成 _X['CRIM2'] = _X['CRIM'] * self.value return _X tranceformer = AddCrim2(value=3) tranceformer.fit_transform(X)

pipeline
ここから本題のpiplineについて。
処理としては、
- 前述の自作Scaler
AddCrim2で変換をおこなう - 前述の
sklearnのScalerStandardScalerで変換をおこなう - 機械学習モデル
LinearRegressionで線形回帰をおこなう
といった流れとなる。
piplineを使わないと以下のような処理となる。
from sklearn.linear_model import LinearRegression # AddCrim2でXを変換(特徴量追加) scaler1 = AddCrim2(value=4) X2 = scaler1.fit_transform(X) # StandardScalerでX2,yを変換 scaler2 = StandardScaler() X3 = scaler2.fit_transform(X2) y3 = scaler2.fit_transform(y) # LinearRegressionをX3, y3で学習 model = LinearRegression() model.fit(X3, y3) # LinearRegressionの学習結果をX3,yで評価 model.score(X3, y3) # => 0.7406426641094093
pipelineを使用する場合、任意の処理名と処理クラスのセットのタプルを各処理の順番で渡す。ちなみに辞書ではないのは、明確に処理に順番があるため。
また、各処理の引数はset_params関数を用いて、引数を 処理名__引数名で指定することができる(_は2つなので注意)。
pipelineオブジェクトが作成できたらfitでデータを渡す。
from sklearn.pipeline import Pipeline add_scale_ols_pipeline = Pipeline([ ('add', AddCrim2()), ('scaler', StandardScaler()), ('ols', LinearRegression()) ]) add_scale_ols_pipeline.set_params(add__value=4) # AddCrim2の引数valueを4に設定 add_scale_ols_pipeline.fit(X, y)
なお、pipelineオブジェクトを作成した際に、各処理の引数がどう設定されているかが表示されるので引数指定の際に参考になる。

このpipelineオブジェクトでの変換・学習結果の評価もおこなえる。
scale_ols_add_pipeline.score(X, y)
# =>0.7406426641094095
pipelineの注意点
各処理で設定する引数
AddCrim2のコードは特徴量に関するコードなのでXだけを処理する。しかし、引数としてy = Noneとわざわざ設定している理由について。
先程のpipelineコードではLinearRegression用にfit時にX, yを渡している。そのため、各fit処理でX, yが引数として渡される。
そのため、AddCrim2でのfitでも共通してX, yともに渡す必要があるので、引数yへの処理が記載されていないと引数が多くてエラーとなる。そのため、yについての引数についても書きつつ、使用自体はしないのでy = Noneと指示している。
# オリジナル class AddCrim2(BaseEstimator, TransformerMixin): def __init__(self,value=2): self.value = value def fit(self, X, y=None): return self def transform(self, X): # 直接の書き換えが起きないようにcopy _X = X.copy() # 新たな特徴量の作成 _X['CRIM2'] = _X['CRIM'] * self.value return _X # 引数1つ class AddCrim2_2(BaseEstimator, TransformerMixin): def __init__(self,value=2): self.value = value def fit(self, X): return self def transform(self, X): # yへの引数なし # 直接の書き換えが起きないようにcopy _X = X.copy() # 新たな特徴量の作成 _X['CRIM2'] = _X['CRIM'] * self.value return _X scale_ols_add_pipeline = Pipeline([ ('add', AddCrim2_2()), # 引数1つ ('scaler', StandardScaler()), ('ols', LinearRegression()) ]) scale_ols_add_pipeline.set_params(add__value=4) scale_ols_add_pipeline.fit(X,y) # AddCrim2_2, StandardScaler, LinearRegressionそれぞれのfitが走る。AddCrim2_2のfitはfit(X, y)として走るがfit(X)と定義されているので引数エラーとなる

各処理の順番
前述のように、指定された順番で処理が走る。つまり、前工程で処理したデータを次工程に渡す。このとき、前工程でどのような形で返ってくるか、次工程でどのような形でデータを受け取るかを意識しておく必要がある。
今回の例示コードのAddCrim2は、前述のように データフレームを受け取って処理する 。また、StandardScalerは受け取る形はデータフレームでもArrayでも良いが 処理結果はArrayで返ってくる。
そのため、処理順がAddCrim2 → StandardScalerは良いが、逆の StandardScaler → AddCrim2 ではエラーが起きる(仮にエラーが出なくても、標準化してから特徴量を掛け算して加えるという処理はわけがわからないので例示用として考えてください)。
scale_ols_add_pipeline = Pipeline([
('scaler', StandardScaler()), # 順番入れ替える
('add', AddCrim2()),
('ols', LinearRegression())
])
scale_ols_add_pipeline.set_params(add__value=4)
scale_ols_add_pipeline.fit(X,y)

参考
pipelineについてよくまとまっている、また、pipelineの簡素版であるmake_pipelineの紹介もおこなっている。