데이터 변환기, Pipeline 만들기

 

계층적 샘플링 (Stratified Sampling)

데이터 셋이 충분히 크다면 일반 훈련 데이터 셋을 무작위로 샘플링 하여도 큰 문제가 발생하지 않는다. 하지만 그렇지 않으면 데이터 편향이 생길 가능성이 크다. 예를들어 여론 설문조사 기관

dsbook.tistory.com

계층적 샘플링에서 다루었던 housing 데이터들을 가지고 예를 들어보자. 현재 이 데이터들은 StratifiedShuffleSplit 객체에 의해 훈련 세트와 테스트 세트로 나뉘어진 상태이며, 훈련 세트는 다시 housing으로 초기화하였고, 훈련 세트의 레이블은 housing_label로 초기화하였다. 이 데이터들을 전처리하기 위한 파이프라인을 만들어보자.

housing = strat_train_set.drop("median_house_value", axis=1)
housing_label = strat_train_set["median_house_value"].copy()

 

우선, 파이프라인을 만들기 앞서 수치형 feature과 범주형 feature를 각각 housing_num과 housing_cat을 구분한 후. 각각의 데이터들을 다른 방식으로 전처리해주어야 한다. 수치형 데이터로 이루어진 housing_num은 다음과 같은 방법으로 전처리 과정을 따를 것이다.

  1. SimpleImputer를 통해 NaN으로 비워져있는 값 채우기
  2. 예측이 좀 더 잘 될 수 있도록 하는 새로운 특성(feature) 만들기
  3. ★ StandardScaler로 특성 스케일링

SimpleImputer과 StandardScaler는 각각 sklearn.impute와 sklearn.preprocessing API에 들어있는 클래스들이므로 따로 우리가 만들어줄 필요는 없지만 새로운 특성을 만들기 위한 변환기는 따로 제작해주어야 한다.

from sklearn.base import BaseEstimator, TransformerMixin

# column index
rooms_ix, bedrooms_ix, population_ix, households_ix = 3, 4, 5, 6

# rooms_per_household, population_per_household, bedrooms_per_room 새로운 특성 추가
class CombinedAttributesAdder(BaseEstimator, TransformerMixin):
    def __init__(self, add_bedrooms_per_room = True): # no *args or **kargs
        self.add_bedrooms_per_room = add_bedrooms_per_room
    def fit(self, X, y=None):
        return self  # nothing else to do
    def transform(self, X):
        rooms_per_household = X[:, rooms_ix] / X[:, households_ix]
        population_per_household = X[:, population_ix] / X[:, households_ix]
        if self.add_bedrooms_per_room:
            bedrooms_per_room = X[:, bedrooms_ix] / X[:, rooms_ix]
            return np.c_[X, rooms_per_household, population_per_household,
                         bedrooms_per_room]
        else:
            return np.c_[X, rooms_per_household, population_per_household]

커스텀 변환기를 만들 때 BaseEstimator를 상속해주는 이유는 하이퍼파라미터 튜닝에 필요한 두 메서드(get_params(), set_params())를 얻기 위함이고, TransformerMixin을 상속해주는 이유는 fit_transform() 메서드를 상속받아 따로 fit_transform() 메서드를 정의하지 않기 위함이다.

이렇게 정의한 변환기들을 바탕으로 수치형 데이터들을 전처리하기 위한 파이프라인을 만들어보자.

from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler

num_pipeline = Pipeline([
    ('imputer', SimpleImputer(strategy="median")),
    ('attribs_adder', CombinedAttributesAdder()),
    ('std_scaler', StandardScaler()),    
])

 

이후 범주형 데이터로 이루어진 housing_cat은 OneHotEncoder를 통해 범주형 데이터를 수치형 데이터로 변환시켜야 한다. 하지만 이 과정을 수치형 데이터와 따로 처리하지 않고 하나의 변환기 안에서 각 열마다 적절한 변환을 적용하여 모든 열을 처리할 수 있다면 더 편하게 작업할 수 있을 것이다. 사이킷런에서는 ColumnTransformer를 통해 이런 기능을 제공한다.

from sklearn.compose import ColumnTransformer

housing_num = housing.drop("ocean_proximity", axis=1)
num_attribs = list(housing_num)		# 수치형 데이터로 이루어진 열들
cat_attribs = ["ocean_proximity"]	# 범주형 데이터로 이루어진 열

full_pipeline = ColumnTransformer([
    ("num", num_pipeline, num_attribs),
    ("cat", cat_pipeline, cat_attribs),
])

 

정리해보면, 수치형 데이터들은 num_pipeline의 과정을 통해 데이터가 변환되고, 범주형 데이터는 OneHotEncoder을 통해 데이터가 변환되며 이 모든 과정은 ColumnTransformer 객체로 합쳐져 한 번에 처리된다.

housing_prepared = full_pipeline.fit_transform(housing)

그리고 housing 데이터를 fit_transform() 메서드를 통해 full_pipeline에서 변환시킨다.

 


특성 스케일링, Feature Scaling

이때 중요한 것이 특성 스케일링 과정이다. 머신러닝 알고리즘에서는 입력 숫자 특성들의 스케일(숫자의 범위)가 많이 다르면 잘 작동하지 않는다. 따라서 모든 수치형 데이터 값들을 비슷한 수준으로 맞춰주는 과정을 특성 스케일링 (feature scaling) 과정이라고 한다. 대표적으로 0~1 범위에 들도록 값을 이동하고 스케일을 조정하는 (정규화, normalization) MinMaxScaler와 평균과 분산을 각각 0과 1로 만들어주는 (표준화, standardization) StandardScaler가 있다. 

 


fit, transform, fit_transform() 메서드

 

fit & transform 과 fit_transform의 차이가 무엇인가요? - 인프런

질문 - fit & transform 과 fit_transform의 차이가 무엇인가요? 안녕하세요, 정말 좋은 강의 잘 듣고 있습니다!! sklearn 의 클래스를 사용하다보면 fit, transform이 많이 언급되는데요. 어쩔때는 fit 하고나서

www.inflearn.com

 

fit, transform and fit_transform | Data Science and Machine Learning

Technical advice from other data scientists | Questions & Answers

www.kaggle.com

fit 메서드는 사이킷런 모듈에서 다양한 역할을 수행한다. 우선 첫째로 지도 학습에서 특정 레이블을 예측하기 전에 데이터를 학습하기 위해 fit 메서드를 사용한다. K-근접 이웃(KNN), 서포트 벡터 머신(SVM), 로지스틱 회귀(Logistic Regression), 나이브 베이즈(Naive Bayes) 등이 이 fit 메서드를 사용해 데이터를 학습시킨 후, predict 메서드를 통해 결과를 예측한다.

  • fit() → predict() 

또한 데이터 전처리과정에서 사이킷런을 통해 데이터를 변환하는 대부분의 로직에서는 fit과 transform을 쌍으로 사용한다. StandardScaler, MinMaxScaler, OneHotEncoder와 같은 Scaler들과 TfidfVectorize와 같은 NLP Vectorizers 등이 fit()과 transform() 메서드를 같이 이용한다. 첫 번째의 경우 fit과 predict의 역할이 명백하게 나뉘어 있어 구분하여 사용하기 편하지만 fit과 transform의 경우에는 fit을 사용하지 않고 transform만 사용하는 경우가 있어 헷갈리는 경우가 있다.

  • fit() → transform()

 

앞서 데이터 전처리 과정에 대해서 배웠으므로 fit과 transform 메서드를 알아보자. fit 메서드는 학습 데이터 세트에서 변환을 위한 기반을 설정하는 단계라고 볼 수 있다. 예를들어서 StandardScaler 객체를 생성한 후 fit() 메서드에 학습 데이터 세트를 인자로 넣어주게 되면, StandardScaler 객체는 학습 데이터 세트의 변환을 위한 여러가지 기반을 설정한 후 그 값을 내부에 저장한다. transform 메서드는 fit 메서드에서 저장한 설정값들을 기반으로 데이터를 변환하는 메서드이다. 여기서 fit_transform() 메서드는 fit() 메서드와 transform() 메서드의 동작을 연속적으로 수행하기 위한 메서드이다. 

fit과 transform 그리고 fit_transform의 기능은 동일하지만 언제 사용하는지는 다르다. 특히 데이터 전처리하기 전 학습 데이터 세트를 훈련 데이터와 테스트 데이터로 나눈 경우에는 더더욱 그렇다. 우리는 먼저 학습 데이터를 계층적 샘플링 과정을 통해 훈련 데이터와 테스트 데이터로 나눈 후, 훈련 데이터에만 pipeline 과정을 통해 데이터를 전처리하였다. 이때, fit_transform() 메서드를 사용했는데, 이 메서드를 사용함으로써 StandardScaler과 같은 메서드들에는 훈련 데이터 변환을 위한 여러가지 설정값들이 내부에 저장되어 있다.

하지만 테스트 세트의 경우 훈련 세트에서 학습한 내용을 바탕으로 예측을 적용해야 한다. 따라서 테스트 세트에서 fit_transform() 메서드, 혹은 fit() 메서드를 통해 데이터를 변환시켜버리면 기존의 훈련 데이터에서 저장했던 내부의 값들, 기준들을 모두 무시하고 새로운 값들을 저장하여 적용하게 된다. 따라서 이 경우에는 fit_transform() 메서드를 적용해서는 안되며 훈련 데이터 세트에 적용한 값들을 기반으로 transform() 메서드만 사용해 변환시켜야 하는 것이다.

이 과정은 특히 특성 스케일링 과정에서 유의해야 한다. fit_transform() 메서드와 transform() 메서드가 헷갈리다면, 차라리 훈련 데이터 세트에서만 fit() 메서드를 적용시킨 후 훈련 세트와 테스트 세트에 transform() 메서드를 적용시키는 것도 하나의 방법이 될 수 있다.

728x90
반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 라이프코리아트위터 공유하기
  • shared
  • 카카오스토리 공유하기