3
5

More than 3 years have passed since last update.

沢山のパイプライン(pipeline)を1つにまとめて、一気に片付ける方法

Posted at

1.はじめに

 pipelineは、色々な前処理を連結して行う時にコードが簡潔に書けて便利なのですが、今回は、沢山のパイプライン(pipeline)を1つにまとめて、一気に片付ける方法がとても便利だったので、備忘録として残します。

2.下準備

 デモ用のデータセットを、kaggleの HR Analytics からダウンロードします。

 カレントディレクトリーに、inputフォルダー、outputフォルダー、modelフォルダーを用意し、ダウンロードしたデータセット HR_comma_sep.csv は、inputフォルダーに保存します。

スクリーンショット 2020-01-31 09.54.03.png

 HR_comma_sep.csv は、9項目の特徴量を元にその人が退社するかどうかを予測(left列)するデータセットで、全部で14,999行あります。

 kaggleのコンペの様に、この内10,000行をtrain、残り4,999行をtestとし、trainで学習モデルを作って、testの結果を予測する場面を想定します。

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split

# -------------  データセットの作成 ------------------
# データセットを読み込む
df = pd.read_csv('./input/HR_comma_sep.csv')

# 行をシャッフルし、インデックスをリセットし、IDを付加
df = df.sample(frac=1, random_state=1)  
df = df.reset_index(drop=True) 
df = df.reset_index()  
df = df.rename(columns={'index':'ID'})

# 行数でtrain, testに分割
train = df[0:10000]
valid = df[10000:]

# カテゴリー変数をワンホットエンコーディング
df_train = pd.get_dummies(train)
df_valid = pd.get_dummies(valid)

# 正解ラベルと特徴量に分割
y = df_train['left']
X = df_train.drop(['ID','left'], axis=1)
y_valid = df_valid['left']
X_valid = df_valid.drop(['ID','left'], axis=1)

# train とtestに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=1)    

print('X_train.shape = ', X_train.shape)
print('y_train.shape =  ', y_train.shape)
print('X_test.shape = ', X_test.shape)
print('y_test.shape = ', y_test.shape)
print('X_valid.shape = ', X_valid.shape)
print('y_valid.shape = ', y_valid.shape)
print()

スクリーンショット 2020-01-30 21.02.47.png
 データセットの行をシャッフル後、trainとtestに分割し、それぞれカテゴリー変数はワンホットエンコーディングして、正解ラベル(y, y_valid)と特徴量(X, X_valid)に分離します。

 さらに、学習モデルを作るための、X, yは、train_test_splitで、学習用(X_train, y_train)と評価用(X_test, y_test)に分割します。これで、下準備は完了です。

3.パイプラインの設定

 今回は、前処理付き学習モデルのパイプラインを8本用意して、1つの大きなパイプラインにまとめます。こうすることで、8本のパイプラインを順次動かすことが出来ます。

# -------- パイプラインの設定 -------- 
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.pipeline import Pipeline

pipelines = {    
    'KNN':
        Pipeline([('scl',StandardScaler()),
                  ('est',KNeighborsClassifier())]), 
    'Logistic':
        Pipeline([('scl',StandardScaler()),
                  ('est',LogisticRegression(solver='lbfgs', random_state=1))]), 
    'SVM':
        Pipeline([('scl',StandardScaler()),
                  ('est',SVC(C=1.0, kernel='linear', class_weight='balanced', random_state=1, probability=True))]),
    'K-SVM':
        Pipeline([('scl',StandardScaler()),
                  ('est',SVC(C=1.0, kernel='rbf', class_weight='balanced', random_state=1, probability=True))]),
    'Tree':
        Pipeline([('scl',StandardScaler()),
                  ('est',DecisionTreeClassifier(random_state=1))]),
    'RandomF':
        Pipeline([('scl',StandardScaler()),
                  ('est',RandomForestClassifier(n_estimators=100, random_state=1))]), 
    'GBoost':
        Pipeline([('scl',StandardScaler()),
                  ('est',GradientBoostingClassifier(random_state=1))]),    
    'MLP':
        Pipeline([('scl',StandardScaler()),
                  ('est',MLPClassifier(hidden_layer_sizes=(3,3),
                                       max_iter=1000,
                                       random_state=1))]), 
    }

5.パイプラインの処理

 後は、for pipe_name, pipeline in pipelines.items():とすると、各パイプラインの先頭にある文字列(例えば、'KNN')が pipe_name に、各パイプラインのインスタンスがpipelineに順次入ります。つまり、

pipeline.fit(X_train, y_train) で学習モデル作成
pipeline.predict(X_test) で学習モデルでの予測
pickle.dump(pipeline, open(file_name, 'wb')) で学習モデルの保存

こんな感じで使えて、非常に便利です。

# ------- パイプラインの処理 ------
from sklearn.metrics import accuracy_score
from sklearn.metrics import log_loss
import pickle

scores = {}
for pipe_name, pipeline in pipelines.items():

    # 学習
    pipeline.fit(X_train, y_train)

    # 指標計算
    scores[(pipe_name,'test_log')] = log_loss(y_test, pipeline.predict_proba(X_test))
    scores[(pipe_name,'valid_log')] = log_loss(y_valid, pipeline.predict_proba(X_valid))
    scores[(pipe_name,'test_acc')] = accuracy_score(y_test, pipeline.predict(X_test))
    scores[(pipe_name,'valid_acc')] = accuracy_score(y_valid, pipeline.predict(X_valid))

    # Submit保存(outputフォルダー) 
    ID=df_valid['ID']
    preds = pipeline.predict_proba(X_valid)  # 予測確率
    submission = pd.DataFrame({'ID': ID, 'left':preds[:, 1]})  
    submission.to_csv('./output/'+pipe_name+'.csv', index=False) 

    # モデル保存(modelフォルダー)
    file_name = './model/'+pipe_name+'.pkl'
    pickle.dump(pipeline, open(file_name, 'wb'))

# 指標の表示
df = pd.Series(scores).unstack()
df = df.sort_values('test_acc', ascending=False)
print(df)

スクリーンショット 2020-01-30 21.09.49.png
 ここでは、8本のパイプラインそれぞれについて、学習、指標計算(accuracy, logloss)、Submit保存(予測確率)、モデル保存を行っています。同じ様な処理をまとめて一気にやりたい時にも、pipelineは超便利ですね。

 ちなみに、kaggleの場合、y_validは秘密(というか、それを当てるのがkaggle)なので、valid_accvalid_lossは計算出来ませんが、今回は分かっているので、付け加えています。^^

3
5
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
3
5