본문 바로가기
[AI] - Machine Learning

# 5. Feature Scaling and Normalization (StandardScaler, MinMaxScaler)

by Bebsae 2021. 4. 16.

서로 다른 변수의 값 범위를 일정한 수준으로 맞추는 작업을 feature scailing이라고 한다.

그에 대표적인 방법으로는 표준화(Standardization)정규화(Normalization)이 있다.

 

1. Standardization (StandardScalar)
표준화는 가우시안 정규 분포를 따르도록 데이터의 피처를 변환하는 작업이다.
여기서 가우시안 정규 분포는 평균이 0, 분산이 1인 분포를 의미한다.
서포트 벡터 머신, 선형회귀, 로지스틱 회귀에서는 데이터가 가우시안 정규 분포를 따르는 것을 가정한다.

표준화는 보통 하나의 데이터 그룹에 대해 표준화를 할 때 사용한다.
특정 데이터가 데이터 그룹에서 어느 위치에 있는지 파악하기 위함.

$z_i = \frac{x_i - mean(x)} {stdev(x)}$
mean : 평균
stdev : 표준편차

2. Normalization (MinMaxScalar)
정규화는 서로 다른 데이터 그룹의 단위를 표준화하기 위한 작업이다.
예를 들어, 특정 데이터들의 키라는 피처 데이터 그룹몸무게라는 피처 데이터그룹이 있을 때
서로 다른 단위를 가진 키, 몸무게 피처 데이터 그룹을 0~1로 정규화하는 것을 의미한다.

키가 최소 160~180cm, 몸무게 50~80kg
>> : 0~1, 몸무게 0~1

$n_i = \frac{x_i-min(x)} {max(x)-min(x)}$

 

 

이번 포스트에서 다룰 import는 다음과 같다.

import pandas as pd

from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import MinMaxScaler
from sklearn.datasets import load_iris

 

우선 붓꽃 데이터를 불러와서 평균과 분산을 출력해보겠다.

iris = load_iris()
iris_feature_names = iris.feature_names
iris_data = iris.data
iris_df = pd.DataFrame(data=iris_data, columns=iris_feature_names)

print(iris_df, '\n')
print(iris_df.mean(), '\n')  # feature 들의 평균값
print(iris_df.var(), '\n')  # feature 들의 분산값

>>
     sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)
0                  5.1               3.5                1.4               0.2
1                  4.9               3.0                1.4               0.2
2                  4.7               3.2                1.3               0.2
3                  4.6               3.1                1.5               0.2
4                  5.0               3.6                1.4               0.2
..                 ...               ...                ...               ...
145                6.7               3.0                5.2               2.3
146                6.3               2.5                5.0               1.9
147                6.5               3.0                5.2               2.0
148                6.2               3.4                5.4               2.3
149                5.9               3.0                5.1               1.8

[150 rows x 4 columns] 

sepal length (cm)    5.843333
sepal width (cm)     3.057333
petal length (cm)    3.758000
petal width (cm)     1.199333
dtype: float64 

sepal length (cm)    0.685694
sepal width (cm)     0.189979
petal length (cm)    3.116278
petal width (cm)     0.581006
dtype: float64 

 

그 다음으로 StandardScaler를 통해 가우시안 정규 분포를 따르도록 스케일링을 진행하겠다.

standard_scaler = StandardScaler()
standard_scaler.fit(iris_df)
iris_scaled = standard_scaler.transform(iris_df)
iris_scaled_df = pd.DataFrame(data=iris_scaled, columns=iris_feature_names)

print(iris_scaled_df, '\n')
print(iris_scaled_df.mean(), '\n')  # 스케일링된 feature 들의 평균값
print(iris_scaled_df.var(), '\n')  # 스케일링된 feature 들의 분산값

>>
     sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)
0            -0.900681          1.019004          -1.340227         -1.315444
1            -1.143017         -0.131979          -1.340227         -1.315444
2            -1.385353          0.328414          -1.397064         -1.315444
3            -1.506521          0.098217          -1.283389         -1.315444
4            -1.021849          1.249201          -1.340227         -1.315444
..                 ...               ...                ...               ...
145           1.038005         -0.131979           0.819596          1.448832
146           0.553333         -1.282963           0.705921          0.922303
147           0.795669         -0.131979           0.819596          1.053935
148           0.432165          0.788808           0.933271          1.448832
149           0.068662         -0.131979           0.762758          0.790671

[150 rows x 4 columns] 

sepal length (cm)   -1.690315e-15
sepal width (cm)    -1.842970e-15
petal length (cm)   -1.698641e-15
petal width (cm)    -1.409243e-15
dtype: float64 

sepal length (cm)    1.006711
sepal width (cm)     1.006711
petal length (cm)    1.006711
petal width (cm)     1.006711
dtype: float64 

보다시피 값들이 대체로 0에 가까워진 것을 확인할 수 있다. 각 데이터(행)들의 피처(열)별로 가우시안 정규 분포를 따르도록 표준화를 진행하였으며 평균이 0에 수렴하고 분산은 1에 수렴하는 것을 확인할 수 있다.

 

그 다음으로는 MinMaxScaler를 통해 데이터들의 최대 1, 최소 0을 분포로 갖도록 스케일링을 진행하겠다.

min_max_scaler = MinMaxScaler()
min_max_scaler.fit(iris_df)
iris_normalized = min_max_scaler.transform(iris_df)
iris_normalized_df = pd.DataFrame(data=iris_normalized, columns=iris_feature_names)

print(iris_normalized_df, '\n')
print(iris_normalized_df.min(), '\n')  # 정규화된 feature 들의 최소값
print(iris_normalized_df.max(), '\n')  # 정규화된 feature 들의 최대값
print(iris_normalized_df.mean(), '\n')  # 정규화된 feature 들의 평균값
print(iris_normalized_df.var(), '\n')  # 정규화된 feature 들의 분산값

>>
     sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)
0             0.222222          0.625000           0.067797          0.041667
1             0.166667          0.416667           0.067797          0.041667
2             0.111111          0.500000           0.050847          0.041667
3             0.083333          0.458333           0.084746          0.041667
4             0.194444          0.666667           0.067797          0.041667
..                 ...               ...                ...               ...
145           0.666667          0.416667           0.711864          0.916667
146           0.555556          0.208333           0.677966          0.750000
147           0.611111          0.416667           0.711864          0.791667
148           0.527778          0.583333           0.745763          0.916667
149           0.444444          0.416667           0.694915          0.708333

[150 rows x 4 columns] 

sepal length (cm)    0.0
sepal width (cm)     0.0
petal length (cm)    0.0
petal width (cm)     0.0
dtype: float64 

sepal length (cm)    1.0
sepal width (cm)     1.0
petal length (cm)    1.0
petal width (cm)     1.0
dtype: float64 

sepal length (cm)    0.428704
sepal width (cm)     0.440556
petal length (cm)    0.467458
petal width (cm)     0.458056
dtype: float64 

sepal length (cm)    0.052908
sepal width (cm)     0.032983
petal length (cm)    0.089522
petal width (cm)     0.100869
dtype: float64 

각 데이터들의 피처들은 공통적으로 최소, 최대가 0.0, 1.0임을 확인할 수 있다. 하지만 각각 피처안에서도 분포가 다르기 때문에 평균, 분산이 다르게 나타나는 것을 확인할 수 있다.

 

그리고 주의할 점이 있다. 학습 데이터 세트와 테스트 데이터 세트를 스케일링할 시, 학습 데이터 세트의 fit() 메소드 결과를 토대로 테스트 데이터 세트를 transform()을 진행해야 한다. 그 이유는 fit()과 transform() 메소드의 역할을 알면 이해할 수 있다.

 

* fit() : 데이터 변환을 위한 기준 정보 설정 (ex - 데이터 세트의 최소/최대값 설정 ..)

* transform() : 설정된 기준 정보를 토대로 데이터를 변환한다.

 

만약 테스트 데이터 세트의 fit() 결과를 토대로 transform()을 진행하면 학습 데이터 세트와는 완전히 별개의 데이터가 되어 버린다. (학습 데이터 세트의 최소. 최대값과 테스트 데이터 세트의 최소, 최대값은 다르므로)

 

train_array = np.arange(0, 11).reshape(-1, 1)  # 2차원으로 reshape
test_array = np.arange(0, 6).reshape(-1, 1)

scaler = MinMaxScaler()
scaler.fit(train_array)
train_scaled = scaler.transform(train_array)
"""
ValueError: Expected 2D array, got 1D array instead:
array=[ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9. 10.].
"""

print('원본 train set : ', train_array.reshape(-1))
print('스케일 train set : ', train_scaled.reshape(-1))

>>
원본 train set :  [ 0  1  2  3  4  5  6  7  8  9 10]
스케일 train set :  [0.  0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]

우선 학습 데이터 세트를 MinMaxScaler를 통해 fit(), trasform()를 진행한 상태이다. train_array의 최소는 0, 최대는 10이기 때문에 모든 값이 1/10으로 스케일링된다.

 

scaler.fit(test_array)
test_scaled = scaler.transform(test_array)

print('원본 test set : ', test_array.reshape(-1))
print('스케일 test set : ', test_scaled.reshape(-1))

>>
원본 test set :  [0 1 2 3 4 5]
스케일 test set :  [0.  0.2 0.4 0.6 0.8 1. ]

테스트 데이터 세트를 fit()할 경우 최소값이 0, 최대값이 5이기 때문에 모든 값이 1/5로 스케일링된다. 

 

scaler.fit(train_array)
test_scaled = scaler.transform(test_array)

print('원본 test set : ', test_array.reshape(-1))
print('스케일 test set : ', test_scaled.reshape(-1))

>>
원본 test set :  [0 1 2 3 4 5]
스케일 test set :  [0.  0.1 0.2 0.3 0.4 0.5]

테스트 데이터 세트 또한 1/10으로 올바르게 스케일링된 것을 확인할 수 있다.

 

하지만 이것보다 더 좋은 방법은 전체 데이터에 대해서 fit()을 수행한 결과로 학습 데이터에 대한 transform(), 테스트 데이터 세트에 대한 transform()을 수행하는 것이 깔끔하다.

댓글