1. Data Scaling
보통의 경우 각 컬럼이 가지는 값의 범위는 다양합니다.
대부분의 분석 알고리즘은 컬럼 간 데이터의 범위가 크게 차이날 경우
값의 범위가 작은 컬럼에 비해 범위가 큰 컬럼이 목표 변수를 예측하는 데
큰 영향을 준다고 판단합니다.
따라서 컬럼 간 데이터 범위가 많이 차이날 경우 스케일링을 통해 모든 컬럼 값의 범위를
동일하게 만들어줄 필요가 있습니다.
가장 일반적인 스케일링 방법에는 표준화와 정규화가 있습니다.
표준화는 각 컬럼의 평균을 0, 분산이 1인 표준정규분포로 만드는 방법이며,
정규화는 각 컬럼들의 값이 특정 범위(주로 0~1) 안에 들어가도록 스케일링 하는 방법입니다.
파이썬으로 표준화와 정규화를 구현해 보겠습니다.
먼저 iris 데이터 셋을 불러옵니다.
import pandas as pd
from sklearn.datasets import load_iris
load_iris = load_iris()
iris = pd.DataFrame(load_iris.data,columns=load_iris.feature_names)
iris['class'] = load_iris.target
iris['class'] = iris['class'].map({0:'setosa',1:'versicolor',
2:'virginica'})
iris
sklearn의 load_iris 모듈을 통해 iris 데이터 셋을 불러올 수 있습니다.
불러온 데이터 셋을 데이터 프레임으로 변경 후 target을 class 컬럼으로 추가합니다.
target값이 0이면 setosa, 1은 versicolor, 2는 virginica로 변경합니다.
다음 train test split을 통해 train 데이터와 test 데이터로 나눠줍니다.
from sklearn.model_selection import train_test_split
import numpy as np
x_train,x_test,y_train,y_test = train_test_split(iris.drop(columns='class'),iris['class'],test_size = 0.3
,stratify=iris['class'],random_state=1004)
print(x_train.shape,x_test.shape)
print(y_train.shape,y_test.shape)
stratify는 class 분포 비율을 맞춰주겠다는 매개변수입니다.
다음 sklearn의 StandardScaler를 통해 입력 변수를 표준화 스케일링 해줍니다.
from sklearn.preprocessing import StandardScaler
std_scaler = StandardScaler()
std_scaler.fit(x_train)
x_train_sc = std_scaler.transform(x_train)
x_test_sc = std_scaler.transform(x_test)
다음 스케일링한 데이터의 최소, 최대, 평균, 표준편차를 구합니다.
print(x_train_sc.min())
print(x_train_sc.max())
print(x_train_sc.mean())
print(x_train_sc.std())
print(x_test_sc.min())
print(x_test_sc.max())
print(x_test_sc.mean())
print(x_test_sc.std())
데이터의 평균을 보면 0의 근사치인 것을 확인할 수 있습니다.
다음 MinMaxSclaer를 통해 정규화를 진행합니다.
MinMaxScaler는 컬럼들을 0과 1 사이의 값으로 스케일링 하는 방식이며,
최소값이 0이고 최대값이 1입니다.
from sklearn.preprocessing import MinMaxScaler
mms = MinMaxScaler()
mms.fit(x_train)
x_train_mms = mms.transform(x_train)
x_test_mms = mms.transform(x_test)
print(x_train_mms.min())
print(x_train_mms.max())
print(x_train_mms.mean())
print(x_train_mms.std())
print(x_test_mms.min())
print(x_test_mms.max())
print(x_test_mms.mean())
print(x_test_mms.std())
최대값과 최솟값이 0,1이 나오는 모습을 볼 수 있습니다.
정규화 방식 중 위 방식 외에도 MaxAbsScaler 방식이 있습니다.
최대 절대값이 1이 되도록 스케일링하는 정규화 방식으로 모든 값은
-1과 1 사이에서 표현됩니다.
데이터가 양수인 경우 MinMaxScaler와 값이 동일한 결과가 나옵니다.
from sklearn.preprocessing import MaxAbsScaler
mas = MaxAbsScaler()
mas.fit(x_train)
x_train_mas = mas.transform(x_train)
x_test_mas = mas.transform(x_test)
print(x_train_mas.min())
print(x_train_mas.max())
print(x_train_mas.mean())
print(x_train_mas.std())
print(x_test_mas.min())
print(x_test_mas.max())
print(x_test_mas.mean())
print(x_test_mas.std())
위 스케일링 방식은 모두 이상치에 민감하게 반응합니다.
따라서 이상치를 정제 후 진행해야 하지만 이는 매우 불편한 방법입니다.
때문에 RobustScaler를 사용합니다.
RobustScaler는 평균과 분산 대신 중앙값과 사분위 값을 활용하는 방식으로,
중앙값을 0으로 설정하고 사분위 범위를 사용해 이상치의 영향을 최소화합니다.
from sklearn.preprocessing import RobustScaler
rs = RobustScaler()
rs.fit(x_train)
x_train_rs = rs.transform(x_train)
x_test_rs = rs.transform(x_test)
print(x_train_rs.min())
print(x_train_rs.max())
print(x_train_rs.mean())
print(x_train_rs.std())
print(x_test_rs.min())
print(x_test_rs.max())
print(x_test_rs.mean())
print(x_test_rs.std())
만약 스케일링한 값을 원본으로 변경하고 싶다면 다음과 같이 변경합니다.
x_original = std_scaler.inverse_transform(x_train_sc)
pd.DataFrame(x_original).head()
2. Data Sampling
정상을 정확하게 분류하는 것과 이상을 정확하게 분류하는 것 중
일반적으로 이상을 정확히 분류하는 것이 중요합니다.
보통 이상 데이터가 목표값이 되는 경우가 많기 때문입니다.
예를 들어 암환자 분류 문제에서 암 검진 대상자 중 실제로 암에 걸린 사람을
찾아내는 것이 중요한 문제입니다.
때문에 아래 샘플링을 통해 이 문제를 해결합니다.
먼저 UnderSampling은 다수의 범주를 가진 데이터를 샘플링 해 소수 범주를 가진
수준으로 데이터를 감소시킵니다.
데이터 불균형으로 인한 문제를 피할 수 있지만 전체 데이터 수가 급격히 줄어들어
학습 성능을 떨어뜨리는 결과를 초래할 수 있습니다.
!pip install imblearn
from sklearn.datasets import make_classification
from collections import Counter
x, y = make_classification(n_samples=2000,
n_features=6,weights=[0.95],flip_y=0)
# n_samples(표본수), n_features(컬럼수), weights(범주의 가중치)
# flip_y(무작위로 할당된 표본수, 숫자가 커질수록 분류 어려움)
Counter(y)
make_classification으로 샘플 데이터를 생성합니다.
y의 개수는 0: 1900개, 1: 100개로 두개의 범주가 크게 차이납니다.
from imblearn.under_sampling import RandomUnderSampler
# sampling_strategy -> majority 지정
# 다수 범주 데이터 샘플링해 소수 범주 데이터수와 동일하게 함
under_sample = RandomUnderSampler(sampling_strategy='majority')
x_under,y_under = under_sample.fit_resample(x,y)
Counter(y_under)
UnderSampling 진행 시 1900개였던 0이 100개가 되는 모습을 확인할 수 있습니다.
이 외에도 smapling_strategty를 통해 비율을 맞춰 샘플링할 수 있습니다.
# sampling_strategy -> 0~1 숫자 값 지정
# 소수 범주 데이터 수와 다수 범주 데이터 수가 해당 비율이 되도로 조정
under_sample2 = RandomUnderSampler(sampling_strategy=0.4)
x_under2, y_under2 = under_sample2.fit_resample(x,y)
Counter(y_under2)
다음 OverSampling은 소수의 범주를 지닌 데이터 세트를 다수의 범주를 지닌
데이터 세트의 수만큼 증식해 학습에 사용하기 위한 충분한 양의 데이터를
확보하는 기법입니다.
데이터의 손실이 없어 일반적으로 언더 샘플링보다 성능이 유리합니다.
from imblearn.over_sampling import RandomOverSampler
# 소수 범주 데이터 늘리는 방법(sampling_strategy=비율)
over_sample = RandomOverSampler(sampling_strategy=0.5)
# fit_resample(분포저장 및 리샘플)
x_over, y_over = over_sample.fit_resample(x,y)
Counter(y_over)
over_sample2 = RandomOverSampler(sampling_strategy='minority')
x_over2, y_over2 = over_sample2.fit_resample(x,y)
Counter(y_over2)
RandomOverSampling은 소수 범주를 지닌 데이터 세트를 단순 복제해 다수 범주와
비율을 맞추는 방법입니다.
데이터를 단순 복제하기 때문에 분포는 변하지 않지만 그 수가 늘어나 같은 비율로
가중치를 받을 수 있습니다.
과적합의 위험성이 있지만 불균형 문제를 처리하지 않는 것보다 유효하기 때문에
종종 사용됩니다.
또한 OverSampling 기법 중 SMOTE(Synthetic Minority Over-sampling Technique)를 사용하기도 합니다.
SMOTE는 소수의 범주를 지닌 데이터 세트의 관측 값에 대해 K개의 최근접 이웃을
찾고 관측 값과 이웃으로 선택된 값 사이 임의의 새로운 데이터를 생성하는 방법으로
샘플 수를 늘립니다.
from imblearn.over_sampling import SMOTE
smote_sample = SMOTE(sampling_strategy='minority')
x_sm, y_sm = smote_sample.fit_resample(x,y)
Counter(y_sm)
위에서 구한 모든 샘플링을 시각화합니다.
import matplotlib.pyplot as plt
import seaborn as sns
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(10,10))
sns.scatterplot(x = x[:,1],y = x[:,2],hue=y,ax=axes[0][0],alpha=0.5)
sns.scatterplot(x = x_under[:,1],y = x_under[:,2],hue=y_under,ax=axes[0][1],alpha=0.5)
sns.scatterplot(x = x_over[:,1],y = x_over[:,2],hue=y_over,ax=axes[1][0],alpha=0.5)
sns.scatterplot(x = x_sm[:,1],y = x_sm[:,2],hue=y_sm,ax=axes[1][1],alpha=0.5)
axes[0][0].set_title('Original Data')
axes[0][1].set_title('Random Under Sampling')
axes[1][0].set_title('Random Over Sampling')
axes[1][1].set_title('SMOTE')
plt.show()
Original Data 그래프는 0과 1의 범주 개수가 크게 차이가 나며,
Random Under Sampling은 0과 1의 범주가 동일한 수준으로 줄어들었습니다.
Random Over Sampling은 데이터를 단순 복제해 1의 범주 관측값들의 위치가
원본 데이터와 차이가 없습니다.
SMOTE는 원본 데이터와 다른 1의 범주를 생성해 골고루 분포해 있는 모습을 볼 수 있습니다.
'머신러닝' 카테고리의 다른 글
[파이썬] Polynomial linear-regression (15) | 2023.11.01 |
---|---|
[파이썬] Perceptron Gradient Descent (0) | 2023.10.25 |
[파이썬] 선형 회귀(Linear-regression) 심화 (7) | 2023.09.20 |
[파이썬] 딥러닝 활성화 함수(Activation function) (0) | 2023.09.18 |
[ R ] R 프로그래밍 언어로 데이터 분석하기 (0) | 2023.09.14 |