머신러닝

피처 스케일링과 정규화(StandardScaler, MinMaxScaler), 스케일링 변환시 유의점

Y0un9Ki 2024. 2. 23. 16:53

서로 다른 변수의 값 범위를 일정한 수준으로 맞추는 작업을 피처 스케일링(feature scaling)이라고 한다.

대표적인 방법으로 표준화(Standardization)정규화(Normalization)가 있다.

 

피처 스케일링과 정규화는 기본적으로 무조건 사용한다. ML알고리즘 상관없이 무조건 디폴트 값으로 사용해야 한다.

 

  • RBF 커널을 이용하는 서포트 벡터 머신, 선형 회귀, 로지스틱 회귀는 데이터가 가우시안 분포를 가지고 있다고 가정을 하고 구현했기 때문에 사전에 표준화를 적용하는 것은 예측 성능 향상에 중요한 요소가 될 수 있다.
  • 만약 트리 계열 알고리즘이라면 굳이 스케일링과 정규화가 필요가 없긴하다. 왜냐하면 변수를 모두 동일한 크기 단위로 0~1까지 변환한다 한들 크기나 비율이 변하지 않기 때문에 즉 분할 기준이 변하지 않기 때문이다.

 

하지만 웬만하면 피처 스케일링과 정규화는 디폴트로 꼭 사용해야 한다라고 알고 있자 중요하다!! 

 

표준화

표준화는 데이터의 피처 각각이 평균이 0이고 분산이 1인 가우시안 정규 분포를 가진 값으로 변환하는 것을 의미한다.

표준화를 통해 변환될 피처 𝑥의 새로운 i번째 데이터를 𝓧i_new라고 한다면 이값은 원래 값에서 피처 𝑥의 평균을 뺀 값을 피처 𝑥의 표준편차로 나눈 값으로 계산할 수 있다.

표준화를 통해 변환될 피처 𝑥의 새로운 i번째 데이터

 

정규화

일반적으로 정규화는 서로 다른 피처의 크기를 통일하기 위해 크기를 최소 0부터 ~ 최대 1로 변환해주는 개념이다.

 

이것은 예를 들면 더 알기 편한대 피처 A는 거리를 나타내느 변수로서 값이 0 ~ 100KM로 주어지고 피처 B는 금액을 나타내는 속성으로 값이 0 ~ 100,000,000,000원으로 주어진다면 이 변수를 모두 동일한 크기 단위로 비교하기 위해 값을 모두 최소 0 ~ 최대 1의 값으로 변환하는 것.

 

개별 데이터의 크기를 모두 똑같은 단위로 변경하는 것이다.

 

정규화를 통해 새로운 데이터 𝓧i_new는 원래 값에서 피처 𝑥의 최솟값을 뺀 값을 피처 𝑥의 최댓값과  최솟값의 차이로 나눈 값으로 변환 된다.

정규화를 통해 새로운 데이터 𝓧i_new

 

그런데 사이컷런의 전처리에서 제공하는 Normalizer 모듈과 일반적인 정규화는 약간의 차이가 있다.(큰 개념은 동일) 사이킷런의 Normalizer모듈은 선형대수에서의 정규화 개념이 적용됐으며, 개별 벡터의 크기를 맞추기 위해 변환하는 것을 의미한다.

개별 벡터를 모든 피처 벡터의 크기로 나눠준다. 세 개의 피처 𝑥, 𝑦, 𝑧가 있다고 하면 새로운 데이터 𝑥i_new는 원래 값에서 세 개의 피처의 i번째 피처 값에 해당하는 크기를 합한 값으로 나눠 준다. ==> 내가 알고있는 정규화의 정의이다.!!! 모든 피처 벡터를 더한 크기를 구해서 그 값을 나눠준다. 

사이킷런의 Normalizer 모듈 통한 새로운 데이터(선형대수의 정규화)

 

이 책에서는 혼선을 방지하기 위해 일반적인 의미의 표준화와 정규화를 피처 스케일링으로 통칭하고 선형대수 개념의 정규화를 벡터 정규화로 지칭했다.

 

여기서 사이킷런에서 제공하는 대표적인 피처 스케일링 클래스인 StandardScaler와 MinMaxScaler를 알아보자!!!

 

 

StandardScaler

StandardScaler는 표준화를 쉽게 지원하기 위한 클래스이다. 개별 피처를 평균이 0이고, 분산이 1인 값으로 변환해준다.

사이킷런에서 구현한 RBF 커널을 이용하는 서포트 벡터 머신, 선형 회귀, 로지스틱 회귀 데이터가 가우시안 분포를 가지고 있다고 가정을 하고 구현했기 때문에 사전에 표준화를 적용하는 것은 예측 성능 향상에 중요한 요소가 될 수 있다.

그러나 이상치가 있다면 평균과 표준편차에 영향을 미쳐 변환된 데이터의 확산은 매우 달라지게 된다.

따라서 이상치가 있는 경우 균형 잡힌 척도를 보장할 수 없다.

 

이제 코드로 StandardScaler가 어떻게 데이터 값을 변환하는지 데이터 세트로 확인해 보자

 

from sklearn.datasets import load_iris
import pandas as pd
# 붓꽃 데이터 셋을 로딩하고 DataFrame으로 변환합니다. 
iris = load_iris()
iris_data = iris.data
iris_df = pd.DataFrame(data=iris_data, columns=iris.feature_names)

print('feature 들의 평균 값')
print(iris_df.mean())
print('\nfeature 들의 분산 값')
print(iris_df.var())
feature 들의 평균 값
sepal length (cm)    5.843333
sepal width (cm)     3.057333
petal length (cm)    3.758000
petal width (cm)     1.199333
dtype: float64

feature 들의 분산 값
sepal length (cm)    0.685694
sepal width (cm)     0.189979
petal length (cm)    3.116278
petal width (cm)     0.581006
dtype: float64

 

이 결과값을 가지고 이제 StandardScaler를 이용해 각 피처를 한번에 표준화 해보겠다.

from sklearn.preprocessing import StandardScaler

# 객체 생성
scaler = StandardScaler()

scaler.fit(iris_df)
iris_scaled = scaler.transform(iris_df)
iris_df_scaled = pd.DataFrame(data=iris_scaled, columns=iris.feature_names)
# iris_df_scaled
print('feature 들의 평균 값')
print(iris_df_scaled.mean())
print('\nfeature 들의 분산 값')
print(iris_df_scaled.var())
feature 들의 평균 값
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

feature 들의 분산 값
sepal length (cm)    1.006711
sepal width (cm)     1.006711
petal length (cm)    1.006711
petal width (cm)     1.006711
dtype: float64

 

sepal length을 예시로 들어보면

sepal length의 각각 피처 데이터들의 값을 하나하나씩 빼와서 전체 sepal length 피처 데이터의 평균 값(5.843333)으로 빼준 값을 전체 sepal length 피처 데이터의 표준편차로 나눠주고 표준화된 각각 sepal length의 모든 피처 값들의 평균을 낸것이다.

그 값이 -1.690315e-15이 나오게 된다.

 

모든 칼럼 값의 평균이 0에 아주 가까운 값으로, 그리고 분산은 1에 아주 가까운 값으로 가우시안 분포를 따르게끔 변환된것을 볼 수 있다.

 

 

MinMaxScaler

MinMaxScaler는 데이터값을 0과 1 사이의 범위 값으로 변환한다. 정규화의 의미를 가지고 있다.(음수 값이 있으면 -1에서 1값으로 변환한다.) @@

데이터의 분포가 가우시안 분포가 아닐 경우에 Min, Max Scale을 적용해 볼 수 있다.

다만 이상치가 있는 경우 변환된 값이 매우 좁은 범위로 압축될 수 있다.

즉, MinMaxScaler 역시 아웃라이어의 존재에 매우 민감하다.

 

 

코드를 통해 예제를 한번 보자

 

from sklearn.preprocessing import MinMaxScaler
from sklearn.datasets import load_iris
import pandas as pd

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

scaler = MinMaxScaler()

scaler.fit(iris_df)
iris_scaled = scaler.transform(iris_df)

iris_df_scaled = pd.DataFrame(data=iris_scaled, columns=iris.feature_names)
# iris_df_scaled
print('feature들의 최솟 값')
print(iris_df_scaled.min())
print('\nfeature들의 최닷 값')
print(iris_df_scaled.max())
print('\nfeature 들의 평균 값')
print(iris_df_scaled.mean())
print('\nfeature 들의 분산 값')
print(iris_df_scaled.var())
feature 들의 최솟 값
sepal length (cm)    0.0
sepal width (cm)     0.0
petal length (cm)    0.0
petal width (cm)     0.0
dtype: float64

feature 들의 최닷 값
sepal length (cm)    1.0
sepal width (cm)     1.0
petal length (cm)    1.0
petal width (cm)     1.0
dtype: float64

feature 들의 평균 값
sepal length (cm)    0.428704
sepal width (cm)     0.440556
petal length (cm)    0.467458
petal width (cm)     0.458056
dtype: float64

feature 들의 분산 값
sepal length (cm)    0.052908
sepal width (cm)     0.032983
petal length (cm)    0.089522
petal width (cm)     0.100869

 

모든 피처에 0에서 1 사이의 값으로 변환되는 스케일링이 적용됐음을 알 수 있다.

 

학습 데이터와 테스트 데이터의 스케일링 변환 시 유의점

StandardScaler나 MinMaxScaler와 같은 Scaler 객체를 이용해 데이터의 스케일링 변환시 fit( ), transform( ), fit_transfrom( ) 메서드를 이용한다.

  • Scaler 클래스의 fit( ), transform( )은 2차원 이상 데이터만 가능하다. (reshape() 메서드로 2차원 이상 배열로 만들어주기!!!)
  • fit( ): 데이터 변환을 위한 스케일링 기준 정보(데이터 세트의 최댓값/최솟값 설정 등)를 설정한다.
  • transform( ): fit( )에서 설정된 정보를 이용해 데이터를 변환한다.
  • fit_transform( ): fit( )과 transform( )을 한 번에 적용하는 기능을 수행한다.

학습 데이터 세트와 테스트 데이터 세트에 이 fit( )과 transform( )을 적용할 때 주의가 필요하다.

Scaler 객체를 이용해 학습 데이터 세트로 fit( )과 transform( )을 적용하면 테스트 데이터 세트로는 다시 fit( )을 수행하지 않고 학습데이터 세트로 fit( )을 수행한 결과를 이용해 테스트 데이터 세트는 transform( ) 만 진행해야 한다.

 

이것이 무슨 소리이냐 하면 예를 들어보자

 

학습데이터는 0부터 10까지 배열을 만들고, 테스트 데이터는 0부터 5까지 배열을 만들고 서로 각각 MinMaxScaler를 진행을 해보자.

 

위에서 썻듯이 fit( ) 매서드는 데이터 변환을 위한 기준 정보 설정 즉 데이터 세트의 최댓값/최솟값을 설정하는 과정인데 여기서 학습 데이터와 테스트 데이터의 최댓값과 최솟값은 서로 다르다.

 

학습 데이터의 fit( )에서는 최솟값은 0 최댓값은 10 으로 1/10 스케일링 기준(Scale 객체)이 적용되는데,

반면에 테스트 데이터의 fit( )에서는 최솟값은 0 최댓값은 5로 1/5 스케일링 기준(Scale 객체)이 적용된다.

 

즉 학습 데이터에서는 원본 데이터 1이 0.1로 스케일링 변환되고, 10이 1로 스케일링 변환이 되는 반면에, 테스트 데이터는 1이 0.1로 5가 1로 스케일링 변환이 된다. 이러하면 나중에 예측을 진행 할 때 서로 스케일링 기준 정보가 안 맞기 때문에 올바른 예측 결과가 나오지 않을 수 있다.

 

올바른 스케일링 변환 방법(매우 중요!!!) 

  • 학습 데이터와 테스트 데이터를 서로 각각 스케일링을 해야 한다면 학습 데이터로 fit( )을 해서 스케일링 기준(Scaler 객체)을 잡아주고 테스트 데이터는 학습 데이터로 fit( )한 스케일링 기준을 가지고 와서 스케일링 하기(transform( ) 진행해주기).
  • 학습 데이터와 테스트 데이터를 나누기 전에 전체 데이터에 스케일링을 진행 후 학습 데이터와 테스트 데이터로 나눠주기

 

위의 유의 사항은 앞으로 배울 사이킷런 기반의 PCA(주성분 분석)와 같은 차원 축소 변환이나 텍스트의 피처 벡터화 변환 작업 적용되기 때문에 꼭 기억하기!!!! @+@