현실 세계의 데이터는 생각보다 이상적이지 않다.
데이터에서 각 클래스의 개수가 현저하게 차이가 난 상태로 모델을 학습하면, 다수의 범주로 패턴 분류를 많이하게 되는 문제가 생기고 이는 곧 모델의 성능에 영향을 끼치게 된다.
이번에는 불균형 데이터 (imbalanced data) 의 문제를 해결할 수 있는 SMOTE(Synthetic Minority Oversampling TEchnique)에 대해서 설명해보고자 한다.
0. Undersampling vs. Oversampling
SMOTE는 대표적인 오버 샘플링 기법 중 하나이므로 먼저 오버 샘플링과 언더 샘플링의 개념을 알아보고자 한다. 언더 샘플링과 오버 샘플링은 클래스 불균형을 해결하기 위한 기법 중 하나이다.

언더 샘플링(Undersampling)은 불균형한 데이터셋에서 높은 비율을 차지하던 클래스(위에서 남성)의 데이터 수를 줄임으로써 클래스의 불균형을 해소하는 개념이다. 이 방법은 불균형을 해소할 수 있지만, 학습에 사용할 수 있는 전체 데이터 수를 감소시키고, 분류에 중요한 데이터를 학습 데이터에서 배제하여 성능 저하를 야기할 수 있다는 단점이 있다.
이에 반해 오버 샘플링(Oversampling)은 반대의 개념으로, 불균형한 데이터셋에서 낮은 비율을 차지하던 클래스(위에서 여성)의 데이터 수를 늘림으로써 불균형을 해소한다. 이 방법은 데이터 수가 감소한다는 그리고 중요한 데이터를 잃을 수 있다는 언더 샘플링의 단점을 보완할 수 있다. 하지만 존재하지 않는 데이터의 수를 늘리는 과정에서 과적합을 야기하여 성능을 저하할 수도 있다.
이 글에서는 오버 샘플링 기법 중 빈번하게 사용되는 SMOTE를 설명하고자 한다. 다만 샘플링 기법은 어느 하나가 더 좋다고 할 수 없고 기법마다 장단점이 존재하기에, 주어진 데이터와 과제에 적합한 기법을 활용하여 문제를 해결해야 한다.
1. SMOTE란

SMOTE의 동작 방식은 데이터의 개수가 적은 클래스의 표본을 가져온 뒤 임의의 값을 추가하여 새로운 샘플을 만들어 데이터에 추가하는 오버샘플링 방식이다.
1.1 SMOTE 동작 방식

SMOTE의 동작 방식을 간단히 설명하면, 임의의 소수 클래스 데이터 사이에 새로운 데이터를 생성하는 방법이라고 할 수 있다. SMOTE는 데이터 생성을 위해서 원본 데이터에 KNN을 활용하여 같은 클래스의 데이터를 임의로 증식한다. 이 방식을 차례대로 정리하면 다음과 같다.
- 소수 클래스의 데이터 중 특정 벡터(샘플)와 가장 가까운 K개의 이웃 벡터를 선정
- 기준 벡터와 선정한 벡터 사이를 선분으로 이음
- 선분 위의 임의의 점이 새로운 벡터(혹은 이 중 임의의 하나)

2. 파이썬 예제 코드
- Dataset: creditcard.csv [출처: Credit Card Fraud Detection (Kaggle)]
import pandas as pd
data = pd.read_csv('~/creditcard.csv')
data.Class.value_counts(normalize=True).plot(kind='bar')
print(data.Class.value_counts(normalize=True)*100)

보다시피 불균형하게 데이터가 분포되어 있는 것을 확인할 수 있고, 우선 현재 상태에서 아무 전처리 없이 모델링을 진행해보고자 한다.
from sklearn.model_selection import train_test_split
X = data.iloc[:,:-1]
y = data.iloc[:,-1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42) #모델링
def modeling(model, x_train, x_test, y_train, y_test):
model.fit(x_train,y_train)
pred = model.predict(x_test)
metrics(y_test,pred) #평가 지표
def metrics(y_test, pred):
accuracy = accuracy_score(y_test,pred)
precision = precision_score(y_test,pred)
recall = recall_score(y_test,pred)
f1 = f1_score(y_test,pred)
roc_score = roc_auc_score(y_test,pred,average='macro')
print('정확도 : {0:.2f}, 정밀도 : {1:.2f}, 재현율 : {2:.2f}'.format(accuracy,precision,recall))
print('f1-score : {0:.2f}, auc : {1:.2f}'.format(f1,roc_score,recall))
2-1 전처리 전 데이터로 모델 성능 평가
- 로지스틱
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
modeling(lr, X_train, X_test, y_train, y_test)

- lightgbm
from lightgbm import LGBMClassifier
lgb = LGBMClassifier(n_estimators=1000, num_leaves=64, n_jobs=-1, boost_from_average=False)
modeling(lgb, X_train, X_test, y_train, y_test)

SMOTE를 적용시키기 전, 모델링을 한 결과 다음과 같이 모델의 성능을 볼 수 있다.
기본적인 데이터로 모델 성능을 확인했으니 이제 데이터 정규화와 outlier를 제거하는 과정을 거친 후 모델의 성능을 확인해보자.
2-2 전처리 후 모델 성능 평가
- 로지스틱
lr = LogisticRegression()
modeling(lr, X_train, X_test, y_train ,y_test)

- lightgbm
⇒ 위에 제시한 파라미터와 다르게 is_unblance 파라미터를 True로 설정하여 진행했다.
lgb = LGBMClassifier(n_estimators=1000, num_leaves=64, n_jobs=-1,
is_unbalance=True, boost_from_average=False)
modeling(lgb, X_train, X_test, y_train, y_test)

전처리(정규화,아웃라이어 제거)만 해도 굉장히 성능이 좋아지는 것을 확인할 수 있다. 그럼 이 상태에서 imbalanced data의 문제를 해결할 수 있는 SMOTE 기법을 적용시켜보도록 한다.
2-3 SMOTE 적용
from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state=0)
X_train_over, y_train_over = smote.fit_sample(X_train, y_train)
print('SMOTE 적용 전 학습용 피처/레이블 데이터 세트: ', X_train.shape, y_train.shape)
print('SMOTE 적용 후 학습용 피처/레이블 데이터 세트: ', X_train_over.shape, y_train_over.shape)
print('SMOTE 적용 후 레이블 값 분포: \n', pd.Series(y_train_over).value_counts())

- 로지스틱
lr = LogisticRegression()
modeling(lr, X_train_over, X_test, y_train_over, y_test)

- lightgbm
lgb = LGBMClassifier(n_estimators=1000, num_leaves=64, n_jobs=-1, boost_from_average=False)
modeling(lgb, X_train_over, X_test, y_train_over, y_test)

결론

(로지스틱 회귀 모델) 전처리 후에 비하여 정확도, 정밀도, f1-score가 급감
(lightgbm) 정밀도가 살짝 감소한 것을 제외하면 재현율과 AUC가 증가
물론 lightgbm 같은 경우는 “scale_pos_weight” 과 “is_unbalance” 의 파라미터 튜닝을 통해 어느 정도 성능을 높일 수 있으나 해당 데이터 셋으로 테스트 해본 결과 SMOTE를 통해 오버 샘플링한 모델성능이 가장 좋은 것을 확인할 수 있다.
마무리
오버 샘플링 수행 시, 소수였던 클래스의 데이터 수가 보완되면서, 정밀도와 재현율에 변화가 발생한다. 이 변화가 너무 크지 않도록 유의하여 오버 샘플링을 수행해야 한다. SMOTE는 앞서 언급한 바와 같이, 무작위로 오버 샘플링을 수행하는 방법보다는 과적합 가능성이 작다.
또한, 언더 샘플링에 비해 정보가 소실되지 않고, 데이터의 수가 줄어들지 않는다는 장점이 있다. 하지만 단점도 존재하는데, SMOTE의 작동 원리를 통해, 이 방법은 낮은 비율로 존재하는 클래스 외의 다른 클래스는 고려하지 않고 샘플링한다는 것을 알 수 있다.
따라서 생성된 데이터가 다른 클래스의 데이터와 중첩되는 경우가 발생하여 노이즈를 생성할 수도 있다. 위와 같은 단점에도 불구하고, SMOTE은 다양한 과제의 클래스 불균형 문제를 해결하기 위해 빈번하게 사용되고 있다. 이뿐만 아니라, borderline-SMOTE, SMOTE-ENN, SMOTE-Tomek 등 SMOTE과 다른 기법을 혼합한 여러 가지 방법이 소개되고 있다. SMOTE에 대한 이해를 기반으로 다양한 샘플링 기법의 작동 원리, 장단점을 파악하면, 해결하려는 불균형 문제에 적합한 방식을 선정할 수 있을 것이다.
'Machine Learning' 카테고리의 다른 글
[sklearn] KNNImputer (0) | 2024.06.23 |
---|