1.はじめに
pipelineは、色々な前処理を連結して行う時にコードが簡潔に書けて便利なのですが、今回は、沢山のパイプライン(pipeline)を1つにまとめて、一気に片付ける方法がとても便利だったので、備忘録として残します。
2.下準備
デモ用のデータセットを、kaggleの HR Analytics からダウンロードします。
カレントディレクトリーに、inputフォルダー、outputフォルダー、modelフォルダーを用意し、ダウンロードしたデータセット HR_comma_sep.csv は、inputフォルダーに保存します。
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()
データセットの行をシャッフル後、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)
ここでは、8本のパイプラインそれぞれについて、学習、指標計算(accuracy, logloss)、Submit保存(予測確率)、モデル保存を行っています。同じ様な処理をまとめて一気にやりたい時にも、pipelineは超便利ですね。
ちなみに、kaggleの場合、y_valid
は秘密(というか、それを当てるのがkaggle)なので、valid_acc
、valid_loss
は計算出来ませんが、今回は分かっているので、付け加えています。^^