[ Python ]sklearn Pipeline으로 전처리하고 dict에 저장 후 재사용

2019. 8. 6. 00:03분석 Python/Scikit Learn (싸이킷런)

728x90

 

내가 모델링을 할 때 다음과 같은 실수를 한 적이 있다.

예를 들어 어떤 데이터를 열심히 전처리를 하고 나서, 모델링을 한다고 했을 때, 보통 모델만 저장하고 전 처리할 때 값을 저장을 안 한 경우가 있다. 

 

그래서 다른 새로운 데이터가 들어왔을 때 다시 전처리 함수를 적용하는 경우가 있는데, 이러면 train과 test로 학습시킨 게 어긋나기 때문에 문제가 생긴다.

 

그리고 만약 어떤 칼럼에서 특정 값이 train에는 없는데, test에만 있어도 문제가 발생할 것이다. 

하지만 이때 test에 있는 것을 가져와야 하는지... 어차피 다 0으로 처리되니까 상관없을 것 같기도 하지만 고민이 되는 사항이다. 

 

아무튼 이번 포스팅은 전처리를 하고 나서 저장해서 어떻게 쓸 수 있을지에 대해 고민해본 것을 써보려고 한다.

 

 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn import preprocessing 

data_file = "./../../Data/Churn.csv"
data = pd.read_csv(data_file)
data["churn"] = data["churn"].astype(str)
data.drop(["phone number"] , axis = 1 , inplace= True)

data.head(2)

요런 데이터가 있다고 하자.

 

이런 것이 있다고 했을 때 일반적으로 object로 나온 부분은 category이기 때문에 onehot 처리를 해줄 것이고, int나 float는 숫자니 표준화를 해줄 것이다.

 

데이터를 잘 봐야 하는데, area code 같은 경우에는 int지만 category이므로 같이 onehot을 해줘야 한다.

 

churn 같은 경우에는 타깃이니 내버려두자. 

 

 

 

 

 

 

 

 

 

 

 

 

 

원래는 각각 따로따로 처리해서 진행을 하였다. 

따로따로 처리하면 장점은 변수 하나하나당 섬세하게 작업을 할 수 있다는 장점이 있을 것 같다.

category_col = ["state","international plan" , "voice mail plan" ,'area code' ]
numeric_col = list(set(data.columns.tolist()).difference(col))
onehot_encoder_dict = {}
for i in category_col :
    print(i)
    onehot_encoder = preprocessing.OneHotEncoder()
    label = onehot_encoder.fit(data[i].values.reshape(-1,1))
    colnames = i + "_"+ onehot_encoder.categories_[0].astype(str).astype(object)
    dt = pd.DataFrame(label.transform(data[i].values.reshape(-1,1)).toarray(), columns = colnames)
    data.drop([i],axis = 1 , inplace = True)
    data = pd.concat([data , dt], axis = 1 )
    onehot_encoder_dict[i] = onehot_encoder
numeric_col.remove("churn")
from sklearn.preprocessing import StandardScaler
numeric_process_dict = {}
for i in numeric_col :
    print(i)
    scaler = StandardScaler()
    scaler.fit(data[i].values.reshape(-1,1))
    numeric_process_dict[i] = scaler
    data[i] = scaler.transform(data[i].values.reshape(-1,1))

이런 식으로 처리를 해주면 된다 여기서 중요한 것은 모델을 만든 후에 dict에 넣은 것이다

이유는 나중에 다시 또 쓰기 위해서! 

그래서 위에 같이 전처리를 진행해주면 되는데, 찾아보니 동시에 한꺼번에 하는 것도 있어서 공유한다.

한꺼번에 하면 좋은 점이 많은데, 단점으로는 인풋의 순서를 잘 기억해야 한다는 점이다.

꼬이는 순간 다른 데이터가 되어버린다!

from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder

numeric_features = numeric_col 
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())])

categorical_features = category_col
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
    ('onehot', OneHotEncoder(handle_unknown='ignore'))])

preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),
        ('cat', categorical_transformer, categorical_features)])
clf = Pipeline(steps=[('preprocessor', preprocessor),])

data_file = "./../../Data/Churn.csv"
data = pd.read_csv(data_file)
data["churn"] = data["churn"].astype(str)
data.drop(["phone number" ,"churn"] , axis = 1 , inplace= True)
clf.fit(data)
Allprocess2= {}
Allprocess2["Numeric_Onehot"] = clf

test2 = pd.DataFrame(data.iloc[0,:]).T
clf = Allprocess2["Numeric_Onehot"]
pd.DataFrame(clf.transform(test2).toarray())

 

 

 

 

말하고자 했던 것은 모델뿐만 아니라 encoding 값을 만든 함수도 따로 저장을 해서 새로운 데이터에 적용을 하는 것이 올바른 적용 방법이다

 

728x90