본문 바로가기
Data Science/Machine Learning

[Machine Learning] 데이터 전처리(2) - Imbalanced class(2)

by 인사이티드 2023. 8. 19.

데이터 전처리(2): https://insighted-h.tistory.com/20

 

[Machine Learning] 데이터 전처리(2) - Imbalanced class(1)

데이터 전처리(1): https://insighted-h.tistory.com/16 [Machine Learning] 데이터 전처리(1) - 결측치 처리(1) 데이터 분석을 하기 위해서 데이터의 전처리는 필수적이다. 데이터들을 수집해서 나온 가공되지 않

insighted-h.tistory.com

 

이전 포스팅에서 imbalanced class 문제를 해결하는 이론적 방법에 대해서 알아보았다.

이번 포스팅에서는 실제 데이터를 가지고 방법에 따라 과연 어느 정도 효과가 있는지 알아보려고 한다.

 

Credit Card Fraud Detection: https://www.kaggle.com/datasets/mlg-ulb/creditcardfraud

 

Credit Card Fraud Detection

Anonymized credit card transactions labeled as fraudulent or genuine

www.kaggle.com

데이터는 kaggle의 신용카드 사기 탐지 데이터를 사용했다.

이 데이터의 변수들은 다음과 같다.

  • Time: 첫 번째 거래로부터의 시간(초)
  • V1~V28: 사용자 개인정보들 PCA결과
  • Amount: 거래량
  • Class: 0 - 정상 거래 / 1 - 사기 거래

해당 데이터의 클래스 분포를 보면 다음과 같았다.

sns.countplot(df, x="Class")
print('Class samples')
print(df["Class"].value_counts())
print('-'*30)
print('Class proportions')
print(df["Class"].value_counts(normalize=True))
print('-'*30)
Class samples
0    284315
1       492
Name: Class, dtype: int64
------------------------------
Class proportions
0    0.998273
1    0.001727
Name: Class, dtype: float64
------------------------------

클래스 0(정상 거래)은 99.83%, 1(사기 거래)은 0.17%의 극단적인 분포를 보인다.

확실한 imbalance class라는 것을 알 수 있다.

우선 실험을 위해 먼저 train / test 데이터로 나눠줬다.

 

from sklearn.model_selection import train_test_split

X = df.drop(columns="Class")
y = df["Class"]

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

 

실험을 위해 기본 모델은 XGBOOST, 평가 방법은 confusion matrix와 AUC score를 사용했다.

import xgboost as xgb	# 모델: xgboost
from sklearn.metrics import confusion_matrix	# 평가 방법: confusion matrix
from sklearn.metrics import roc_auc_score	# 평가 방법: AUC score

코드는 아래 링크에 정리해놓았다.

https://github.com/opkwisdom/ML_Project/blob/master/CreditCardFraud(imbalanced).ipynb

 

1. Base model

첫 번째로 imbalance class 문제에 대한 전처리 없이 모델링 해보았다.

그 결과 아래와 같은 결과가 나왔다.

print(confusion_matrix(y_test, y_pred))

[[56863     1]
 [   21    77]]
 
print(round(roc_auc_score(y_test, y_pred), 4))

0.8928

0(정상 거래) 클래스는 단 한 건만 오분류한 반면에, 1(사기 거래) 클래스는 98건 중 21건을 오분류한 것을 알 수 있다.

다수 클래스의 분류 정확도에 크게 편향되어 학습되었다.

AUC score는 0.8928이 나왔다.

 

 

2. Resampling

첫 번째 imbalance class 완화 문제로 여러 가지 리샘플링 기법을 활용해보았다.

Resampling을 위한 라이브러리로 imblearn을 사용했다.

 

imblearn: https://imbalanced-learn.org/stable/

 

imbalanced-learn documentation — Version 0.11.0

User guide The user guide provides in-depth information on the key concepts of imbalanced-learn with useful background information and explanation.

imbalanced-learn.org

 

2-1. Under-sampling (Random)

from imblearn.under_sampling import RandomUnderSampler

rus = RandomUnderSampler(random_state=42)
X_train_rus, y_train_rus = rus.fit_resample(X_train, y_train)

model2 = xgb.XGBClassifier(objective="binary:logistic", random_state=42)
model2.fit(X_train_rus, y_train_rus)

y_pred = model2.predict(X_test)

첫 번째는 RandomUnderSampler를 활용했다.

print(confusion_matrix(y_test, y_pred))

[[54724  2140]
 [    6    92]]
 
print(round(roc_auc_score(y_test, y_pred), 4))

0.9506

결과는 Base model과 상당히 달랐다.

Under-sampling은 0과 1 클래스의 비율을 동일하게 맞춰서 0에 대한 분류 정확도는 약간 떨어진 반면

(99.998% -> 96.24%), 1에 대한 분류 정확도가 매우 올라갔다. (78.57% -> 93.88%)

개인적으로 class imbalance가 심해서 Under-sampling할 경우 데이터가 많이 소실되기 때문에 예측 결과가 오히려

떨어질 수도 있겠다는 생각을 했었다.

그러나 예상보다 훨씬 더 고른 결과가 나와서 놀랐다.

AUC score는 0.9506이 나왔다.

 

2-2. Under-sampling (Cluster Centroids)

from imblearn.under_sampling import ClusterCentroids

cc = ClusterCentroids(random_state=42)
X_train_cc, y_train_cc = cc.fit_resample(X_train, y_train)

model3 = xgb.XGBClassifier(objective="binary:logistic", random_state=42)
model3.fit(X_train_cc, y_train_cc)

y_pred = model3.predict(X_test)

두 번째는 Cluster Centroids를 활용했다.

print(confusion_matrix(y_test, y_pred))

[11591 45273]
 [    1    97]]

print(round(roc_auc_score(y_test, y_pred), 4))

0.5968

K 값은 기본으로 진행했는데, 다수 클래스에 대한 오분류가 매우 크게 늘어났다.

다수 클래스에 대한 예측은 랜덤으로 예측하는 50%보다 훨씬 못한 20.38%의 정확도가 나왔다.

AUC score는 0.5968이 나왔다.

 

2-3. Over-sampling (Random)

from imblearn.over_sampling import RandomOverSampler

ros = RandomOverSampler(random_state=42)
X_train_ros, y_train_ros = ros.fit_resample(X_train, y_train)

model4 = xgb.XGBClassifier(objective="binary:logistic", random_state=42)
model4.fit(X_train_ros, y_train_ros)

y_pred = model4.predict(X_test)

세 번째는 RandomOverSampler를 활용했다.

print(confusion_matrix(y_test, y_pred))

[[56860     4]
 [   17    81]]
 
print(round(roc_auc_score(y_test, y_pred), 4))
 
0.9132

역시 RandomUnderSampler와 마찬가지로 Resampling을 안 했을 때에 비해 클래스 예측에 균형이 생기는 것을 확인할 수

있었다. 하지만, 여전히 소수 클래스의 오분류율이 크다.

AUC score는 0.9132가 나왔다.

 

2-4. Over-sampling (SMOTE)

from imblearn.over_sampling import SMOTE

smote = SMOTE(random_state=42)
X_train_sm, y_train_sm = smote.fit_resample(X_train, y_train)

model5 = xgb.XGBClassifier(objective="binary:logistic", random_state=42)
model5.fit(X_train_sm, y_train_sm)

y_pred = model5.predict(X_test)

마지막은 Over-sampling의 대표 주자, SMOTE 기법을 활용했다.

print(confusion_matrix(y_test, y_pred))

[[56846    18]
 [   15    83]]
 
print(round(roc_auc_score(y_test, y_pred), 4))

0.9233

RandomOverSampler에 비해 더 좋은 성능을 보이고 있다. 예측컨대, Over-fitting이 덜 발생한 결과로 보인다.

AUC score는 0.9233이 나왔다.

 

참고로 해당 데이터에서 Train 데이터의 Under-sampling과 Over-sampling 간의 데이터 수 차이는 엄청나게 많이 났다.

print(f"Under: {len(y_train_rus)}")
print(f"Over: {len(y_train_ros)}")

Under: 788
Over: 454902

 

3. Class weight

다음으로 Class weight를 변경해서 확인해보았다.

from sklearn.model_selection import GridSearchCV

model6 = xgb.XGBClassifier(objective="binary:logistic", random_state=42)
param_grid = {"scale_pos_weight": [10, 100, 577.28]}

grid = GridSearchCV(estimator=model6, param_grid=param_grid, n_jobs=-1, cv=5, scoring='roc_auc')
grid_result = grid.fit(X_train, y_train)

Class weight는 GridSearch로 최적의 튜닝을 했다. 여기서 577.28은 (다수 클래스 수)/(소수 클래스 수)이다.

GridSearch 결과 577.28이 최적의 weight였다.

model7 = xgb.XGBClassifier(objective="binary:logistic",
                           scale_pos_weight=577.28, random_state=42)
model7.fit(X_train, y_train)

y_pred = model7.predict(X_test)
print(confusion_matrix(y_test, y_pred))

[[56861     3]
 [   16    82]]
 
print(round(roc_auc_score(y_test, y_pred), 4))
 
0.9183

AUC score는 0.9183이 나왔다.

 

결론적으로 모든 기법들은 naive한 접근에 비해 항상 더 좋은 결과를 보장했다.

리샘플링 기법과 클래스 웨이트를 비교한 결과, 다수 클래스의 오분류를 중요하게 생각하지 않는다면,

RandomUnderSampler가 최적의 결과를 냈다.

만약 다수 클래스의 오분류 또한 중요하다면 SMOTE 또는 Class weight의 변경이 최적의 결과를 가져올 것이다.


참고자료

[1] Imbalanced API reference

https://imbalanced-learn.org/stable/references/index.html

[2] "How to configure XGBoost for imbalanced classification", 2020, Jason Brownlee

https://machinelearningmastery.com/xgboost-for-imbalanced-classification/