더 이상 tistory 블로그를 운영하지 않습니다. glanceyes.github.io에서 새롭게 시작합니다.

새소식

Data Science/데이터 기본

데이터 전처리의 피처 스케일링(Feature Scaling)

  • -

 

머신러닝의 데이터 전처리에서의 피처 스케일링(Feature Scaling)

 

Feature Scaling이란?

 

  • 서로 다른 변수의 값 범위를 일정한 수준으로 맞추는 작업이다.



Feature Scaling을 하는 이유는?

 

  1. 변수 값의 범위 또는 단위가 달라서 발생 가능한 문제를 예방할 수 있다.
  2. 머신러닝 모델이 특정 데이터의 편향성을 갖는 걸 방지할 수 있다.
  3. 데이터 범위 크기에 따라 모델이 학습하는 데 있어서 bias가 달라질 수 있으므로 하나의 범위 크기로 통일해주는 작업이 필요할 수 있다.



Feature Scaling의 종류

 

  • 표준화
  • 정규화



표준화(Standardization)란?

 

  • 서로 다른 범위의 변수들을 평균이 0이고 분산이 1인 가우시안 정규 분포를 가진 값으로 변환하는 작업이다.



표준화를 하는 이유는?

 

  • 머신러닝에서 사용하는 Support Vector Machine, Linear Regression, Logistic Regression 모델은 데이터가 가우시안 분포를 가지고 있다고 가정하여 구현되어 있어서 사전에 학습 데이터에 관해 표준화를 적용하는 것이 모델의 예측 성능 향상에 중요하다.



표준화와 이상치

 

  • 일반적으로 데이터에 값이 너무 크거나 작은 이상치가 있어도 이를 표준화 하는 데는 문제 없다. 그래서 표준화를 진행한 후 이상치를 찾아서 제거하는 게 바람직하다.




예) 이상치 있는 데이터의 표준화 전과 후 평균과 분산 비교

from sklearn.preprocessing 
import StandardScaler 
import numpy as np 
standard_scaler = StandardScaler() 

# 샘플 데이터를 생성한다. 
x = np.arange(10) 

# outlier(이상치)를 생성한다. 
x[9] = 1000 

# 표준화 전 이상치가 있는 데이터의 평균과 분산 print('표준화 전 이상치가 있는 데이터') 
print('평균: ', x.mean(), '\n분산: ', x.std()) 

# 데이터에 관해 표준화를 진행한다. 
scaled = standard_scaler.fit_transform(x.reshape(-1, 1)) 

print('\n표준화 후 데이터') 
print('평균: ', scaled.mean(), '\n분산: ', scaled.std()) 
print('평균 근삿값: ', round(scaled.mean(), 2), '\n분산 근삿값: ', round(scaled.std(), 2))
출력 결과 

표준화 전 이상치가 있는 데이터 
평균: 103.6 
분산: 298.8100399919654 

표준화 후 데이터 
평균: 4.4408920985006264e-17 
분산: 1.0 
평균 근삿값: 0.0 
분산 근삿값: 1.0



정규화(Normalization)란?

 

  • 서로 다른 범위의 변수들의 크기를 통일하기 위해 이를 변환하는 작업이다. 일반적으로 [0, 1] 범위의 분포로 조정한다.



정규화와 이상치

 

  • 데이터에서 이상치를 추가하기 전과 후 데이터를 정규화했을 때의 결과는 큰 차이를 보인다. 그래서 일반적으로 이상치를 제거한 후 정규화를 하는 것이 바람직하다. 위의 표준화에서의 내용과 종합하면 데이터를 표준화하고 그 결과에서 이상치를 제거한 후 정규화를 진행하는 것이 바람직하다.




예) 이상치 추가 전과 후의 정규화 결과 비교

from sklearn.preprocessing 
import MinMaxScaler 
import numpy as np 

# 이상치 추가 전 데이터의 평균과 분산 
y = np.arange(10) 
scaled = MinMaxScaler().fit_transform(y.reshape(-1, 1)) 
print('이상치 추가 전의 데이터') 
print('평균: ', scaled.mean(), '\n분산: ', scaled.std()) 

# 이상치 추가 후 데이터의 평균과 분산 
y[9] = 1000 
scaled = MinMaxScaler().fit_transform(y.reshape(-1, 1)) 
print('\n이상치 추가 후의 데이터') 
print('평균: ', scaled.mean(), '\n분산: ', scaled.std())
출력 결과 이상치 추가 전의 데이터 
평균: 0.5 
분산: 0.31914236925211265 

이상치 추가 후의 데이터 
평균: 0.1036 
분산: 0.2988100399919655



StandardScaler란?

 

  • Sklrean(사이킷런)에서 제공하는 표준화를 위한 클래스이며, 개별 변수를 평균이 0이고 분산이 1인 가우시안 정규 분포를 가질 수 있도록 값을 변환해준다.



StandardScaler 사용 시 주의할 점

 

  • Scaler에 의해 변환된 데이터는 ndarray 형식이므로 데이터 관리의 용이성을 위해 DataFrame 형식으로의 명시적 변환이 필요할 수 있다.



StandardScaler 사용법

 

  1. 데이터셋을 불러온다.
  2. StandardScaler를 선언하여 StandardScaler() 메소드를 사용한다.
  3. StandardScaler의 fit() 메소드로 데이터 분포가 정규분포를 만족하도록 모델을 학습시킨다.
  4. StandardScaler의 transform() 메소드로 정규 분포에 따르는 데이터 값을 ndarray 형식으로 받는다.
  5. 반환 받은 ndarray 데이터를 pandas의 DataFrame() 메소드를 사용하여 DataFrame 형식으로 변환한다.




예) iris 데이터셋을 통한 StandardScaler 사용 예제

from sklearn.datasets 
import load_iris 
import pandas as pd 
import numpy as np 

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('feature 분산') 
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

 

from sklearn.preprocessing 
import StandardScaler 

scaler=StandardScaler() 
# fit()에 매개변수로 전달할 데이터 프레임은 2차원 이상의 값이어야 한다. 
scaler.fit(iris_df) 

iris_scaled=scaler.transform(iris_df) 
# iris_scaled가 배열 형태이므로 데이터 프레임으로 변환해주는 작업이 필요하다. 
iris_df_scaled=pd.DataFrame(data=iris_scaled, columns=iris.feature_names) 

print('feature 평균') 
print(iris_df_scaled.mean()) 

print('feature 분산') 
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 

각 feature의 평균과 분산이 각각 완전한 0과 1은 아니지만, 0과 1에 매우 가까운 값으로 변환되었음을 알 수 있다.



MinMaxScaler란?

 

  • Sklrean(사이킷런)에서 제공하는 정규화를 위한 클래스이며, 데이터 값을 0과 1 사이의 범위 값으로 변환한다. 음수 값이 있으면 -1에서 1 사이의 범위 값으로 변환한다.




MinMaxScaler 사용 시 주의할 점

 

  • Scaler에 의해 변환된 데이터는 ndarray 형식이므로 데이터 관리의 용이성을 위해 DataFrame 형식으로의 명시적 변환이 필요할 수 있다.



MinMaxScaler 사용법

 

  1. 데이터셋을 불러온다.
  2. MinMaxScaler를 선언하여 MinMaxScaler() 메소드를 사용한다.
  3. MinMaxScaler의 fit() 메소드로 데이터 범위가 [0, 1]이 되도록 최솟값은 0, 최댓값은 1을 갖도록 모델을 학습시킨다.
  4. MinMaxScaler의 transform() 메소드로 [0, 1] 범위에 따르도록 데이터 값을 ndarray 형식으로 받는다.
  5. 반환 받은 ndarray 데이터를 pandas의 DataFrame() 메소드를 사용하여 DataFrame 형식으로 변환한다.




예) iris 데이터셋을 통한 MinMaxScaler 사용 예제

from sklearn.preprocessing 
import MinMaxScaler 

scaler=MinMaxScaler() 
scaler.fit(iris_df) 

iris_scaled=scaler.transform(iris_df) 
# Scaler에 의해 변환된 데이터는 ndarray이므로 DataFrame으로 변환 필요 
iris_df_scaled=pd.DataFrame(data=iris_scaled, columns=iris.feature_names) 

print('feature 최솟값') 
print(iris_df_scaled.min()) 
print('fearure 최댓값') 
print(iris_df_scaled.max())
출력 결과 

feature 최솟값 
sepal length (cm) 0.0 
sepal width (cm) 0.0 
petal length (cm) 0.0 
petal width (cm) 0.0 
dtype: float64 

fearure 최댓값 
sepal length (cm) 1.0 
sepal width (cm) 1.0 
petal length (cm) 1.0 
petal width (cm) 1.0 
dtype: float64 

각 feature의 최솟값과 최댓값이 각각 0과 1로 변환되었다.



Feature Scaling 시 유의사항

 

  • 학습 데이터로 fit(), transform()을 적용한 후 테스트 데이터에서 다시 fit()을 수행하지 않고 학습 데이터로 fit()을 적용한 결과를 이용해서 transform()을 적용해야 한다.

 

  • 머신러닝 모델이 학습 데이터(train set)에 관해서만 학습을 해야 하는데, 오직 평가에서만 사용해야 할 테스트 데이터(test set)를 머신러닝 모델이 학습을 하게 되면 모델의 성능을 제대로 평가할 수 없다. 테스트 데이터로 다시 새로운 스케일링 기준을 만들어서 학습해 버리면 스케일링 기준 정보가 학습 데이터에서의 기준과 달라질 수 있어서다.

 

  • 정리하면, 머신러닝 모델은 학습 데이터를 기반으로 학습이 되므로 반드시 테스트 데이터는 학습 데이터의 스케일링 기준을 따라야 한다. 따라서 테스트 데이터에는 다시 fit()을 적용해서는 안 된다.




예) 학습 데이터와 테스트 데이터에 모두 fit()을 적용했을 때 발생하는 문제

# 0에서 10까지의 정수를 train array에 저장 
train_array=np.arange(0,11).reshape(-1, 1) 

# 0에서 5까지의 정수를 test array에 저장 
test_array=np.arange(0,6).reshape(-1, 1) 
# reshape(-1, 정수): 열의 값을 정수로 지정해주면 변환될 배열의 행의 수는 알아서 지정된다는 의미이다. 

scaler=MinMaxScaler() 
scaler.fit(train_array) 
train_scaled=scaler.transform(train_array) 

print('train_array:', np.round(train_array.reshape(-1),2)) 
print('scaling 이후의 train_array:', np.round(train_scaled.reshape(-1),2)) 

# 테스트 데이터에 관해 fit()을 적용했을 때의 값 
scaler.fit(test_array) 
test_scaled=scaler.transform(test_array) 

print('test_array:', np.round(test_array.reshape(-1),2)) 
print('scaling 이후의 test_array:', np.round(test_scaled.reshape(-1),2))
출력 결과 

train_array: [ 0 1 2 3 4 5 6 7 8 9 10] 
scaling 이후의 train_array: [0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ] 

test_array: [0 1 2 3 4 5] 
scaling 이후의 test_array: [0. 0.2 0.4 0.6 0.8 1. ] 

학습 데이터와 테스트 데이터의 스케일링이 부합하지 않는다. 
fit()을 테스트 데이터에 적용하면 학습 데이터와는 또 다른 새로운 스케일링 기준 정보가 만들어진다. 
그래서 테스트 데이터에는 다시 fit()을 적용하지 않아야 한다. 
학습 데이터로 fit()을 수행한 결과를 이용해 transform()을 적용해야 한다.

 

# train data에만 fit()을 적용하는 올바른 방법 
scaler=MinMaxScaler() 
scaler.fit(train_array) 
train_scaled=scaler.transform(train_array) 

print('train_array:', np.round(train_array.reshape(-1),2))
print('scaling 이후의 train_array:', np.round(train_scaled.reshape(-1),2)) 

test_scaled=scaler.transform(test_array) 
print('test_array:', np.round(test_array.reshape(-1),2)) 
print('scaling 이후의 test_array:', np.round(test_scaled.reshape(-1),2))
출력 결과 

train_array: [ 0 1 2 3 4 5 6 7 8 9 10] 
scaling 이후의 train_array: [0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ] 

test_array: [0 1 2 3 4 5] 
scaling 이후의 test_array: [0. 0.1 0.2 0.3 0.4 0.5] 

처음부터 학습 데이터와 테스트 데이터로 분리하기 전에 우선 전체 데이터 세트에 관해서 스케일링을 적용하고 이를 분리하는 방법도 있다.

 

Contents

글 주소를 복사했습니다

부족한 글 끝까지 읽어주셔서 감사합니다.
보충할 내용이 있으면 언제든지 댓글 남겨주세요.