머신러닝 알고리즘을 위한 특성 스케일링

데이터에 적용할 가장 중요한 변환 중 하나가 특성 스케일링(Feature Scaling)입니다. 

머신러닝 알고리즘은 입력 숫자 특성들의 스케일이 많이 다르면 잘 작동하지 않습니다. 주택 가격 데이터도 이에 해당합니다. 즉, 전체 방 개수의 범위는 6에서 39,320인 반면 중간 소득의 범위는 0에서 15까지 입니다. 타깃 값에 대한 스케일링은 일반적으로 불필요합니다.

모든 특성의 범위를 같도록 만들어주는 방법으로 min-max 스케일링(정규화, Normalization)표준화(Standardization)가 널리 사용됩니다.

min-max 스케일링은 매우 간단합니다. 0~1 범위에 들도록 값을 이동하고 스케일을 조정하면 됩니다. 데이터에서 최소값을 뺀 후 최대값과 최소값의 차이로 나누면 이렇게 할 수 있습니다. 사이킷런에는 이에 해당하는 MinMaxScaler 변환기를 제공합니다. 0~1사이를 원하지 않는다면 feature_range 매개변수로 범위를 조정할 수 있습니다.

표준화는 많이 다릅니다. 먼저 평균을 뺀 후 표준편차로 나누어 결과 분포의 분산이 1이 되도록 합니다. 범위의 상한과 하한이 없어 어떤 알고리즘에서는 문제가 될 수 있습니다. 그러나 표준화는 이상치에 영향을 덜 받습니다. 사이킷런에는 표준화를 위한 StandardScaler 변환기가 있습니다.

*모든 변환기에서 스케일링은 (테스트 세트가 포함된) 전체 데이터가 아닌 훈련 데이터에 대해서만 fit() 함수를 적용해야 합니다. 이후 훈련 세트와 테스트 세트에 대해 transform() 함수를 사용합니다.



1. 변환 파이프라인

머신 러닝을 하기 위한 수많은 변환 단계는 정확한 순서대로 실행되어야 합니다. 사이킷런에는 연속된 변환을 순서대로 처리할 수 있도록 도와주는 Pipeline 클래스가 있습니다.

Code

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

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

housing_num_tr = num_pipeline.fit_transform(housing_num)



Pipeline은 연속된 단계를 나타내는 이름/추정기 쌍의 목록을 입력으로 받습니다. 마지막 단계에는 변환기와 추정기를 모두 사용할 수 있고 그 외에는 모두 변환기여야 합니다. (즉, fit_transform() 함수를 가지고 있어야 합니다.)

파이프라인의 fit() 함수를 호출하면 모든 변환기의 fit_transform() 함수를 순서대로 호출하면서 한 단계의 출력을 다음 단계의 입력으로 전달합니다. 마지막 단계에서는 fit() 함수만 호출합니다.

파이프라인 객체는 마지막으로 추정기와 동일한 함수를 제공합니다. 이 예에서는 마지막 추정기가 변환기 StandardScaler이므로 파이프라인이 데이터에 대해 모든 변환을 순서대로 적용하는 transform() 함수를 가지고 있습니다. 

수치형 컬럼을 넘파이 배열로 추출하는 대신 판다스의 데이터프레임을 파이프라인에 직접 주입할 수 있다면 좋을 것 같습니다. 사이킷런이 판다스의 데이터프레임을 다룰 수는 없지만 이를 처리하는 변환기를 직접 만들 수 있습니다.

Code

from sklearn.base import BaseEstimator, TransformerMixin
class DataFrameSelector(BaseEstimator, TransformerMixin):
def __init__(self, attributes_names):
self.attributes_names = attributes_names

def fit(self, X, y = None):
return self

def transform(self, X):
return X[self.attributes_names].values

DataFrameSelector는 나머지는 버리고 필요한 특성을 선택하여 데이터프레임을 넘파이 배열로 바꾸는 식으로 데이터를 변환합니다. 이를 이용해 데이터프레임을 받아 수치형만 다루는 파이프라인을 손쉽게 만들 수 있습니다. 수치형 특성을 선택한 DataFrameSelector로 파이프라인을 시작해서 앞서 이야기한 다른 전처리 단계들을 나열합니다. 범주형 특성을 다루는 또 다른 파이프라인도 DataFrameSelector로 범주형 특성을 선택하고 CategoricalEncoder를 적용하면 간단합니다.

Code

from sklearn.pipeline import FeatureUnion
num_attribs = list(housing_num)
cat_attribs = ["ocean_proximity"]

num_pipeline = Pipeline([
('selector', DataFrameSelector(num_attribs)),
('imputer', Imputer(strategy="median")),
('attribs_adder', CombinedAttributesAdder()),
('std_scaler', StandardScaler()),
])

cat_pipeline = Pipeline([
('selector', DataFrameSelector(cat_attribs)),
('cat_encoder', CategoricalEncoder(encoding="onehot-dense"))
])

full_pipeline = FeatureUnion(transformer_list=[
("num_pipeline", num_pipeline),
("cat_pipeline", cat_pipeline),
])

housing_prepared = full_pipeline.fit_transform(housing)
print(housing_prepared)

전체 파이프라인을 간단하게 실행할 수 있습니다.



References

  • 오렐리앙 제롱, '핸즈온 머신러닝', 한빛미디어, 2018


+ Recent posts