Notice
Recent Posts
Recent Comments
Link
«   2026/02   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

Silver bullet

IQR 기반 Outlier 탐지 및 제거 & SMOTE Over-sampling 본문

AI/AI

IQR 기반 Outlier 탐지 및 제거 & SMOTE Over-sampling

밀크쌀과자 2024. 7. 12. 03:52

imbalanced-learn 라이브러리는 Scikit-learn을 비롯해 많은 라이브러리들의 버전에 영향을 줍니다.
 Colab에서 실습을 진행하는 것을 권장합니다.

* Original source @ https://www.kaggle.com/dogdriip/iqr-outlier-smote-oversampling/notebook

 

본 실습에서 다뤄지는 신용카드 데이터셋의 경우 심한 class-imbalance 상태임
- class 0 == 정상적인 신용카드 거래 데이터
- class 1 == 사기 신용카드 거래 데이터
전체 데이터셋 중 0.1715% 만이 class 1 (사기거래) 데이터


일반적으로 Fraud detection & Anomaly detection 의 대상이 되는 데이터셋의 경우
이처럼 class-imbalanced data인 경우가 많음 (Fraud or Anomaly 자체가 극히 드물게 발생하기 때문)

 


!pip install lightgbm==3.3.2

# "액세스가 거부되었습니다" 에러 발생 시,
# cmd(명령프롬프트) 우클릭 & 관리자권한으로 실행 후,
# "pip install lightgbm==3.3.2"를 입력 및 실행하여 설치를 진행합니다.

2) Imbalanced-learn (imblearn) 설치

  • (로컬 환경에서 진행 시) cmd(명령프롬프트) 우클릭 & 관리자권한 실행 후, 아래 명령어를 입력해 설치를 진행합니다.
  • 에러가 발생할 경우 뒷 부분의 버전 정보("==0.7.0")를 제외해주세요.

conda install -c conda-forge imbalanced-learn==0.7.0

 

import numpy as np 
import pandas as pd 
import matplotlib.pyplot as plt
import seaborn as sns

%matplotlib inline

import warnings                 
warnings.filterwarnings('ignore')

# 데이터 출처 : Credit Card Fraud Detection @ https://www.kaggle.com/datasets/mlg-ulb/creditcardfraud

card_df = pd.read_csv('creditcard.csv')
card_df.shape

card_df = card_df.drop('Time', axis=1)

sns.distplot(card_df['Amount'])

# 로그변환과 np.log()가 아닌 np.log1p()를 하는 이유 @ https://suppppppp.github.io/posts/Why-Series-MDM-1/

sns.distplot(np.log1p(card_df['Amount']))

card_df['Amount'] = np.log1p(card_df['Amount'])

로그가 아닌 standardscaling을 써도 됨.

 

3. Outlier 탐지 및 제거

본 데이터셋과 같이 Feature의 수가 무척 많을 경우, Label(Target) 열과 가장 높은 상관관계를 갖고 있는 Feature들을 위주로 Outlier handling을 진행해주는 것이 좋음


- 모든 Feature를 대상으로 이상치 검출 & 제거를 하는 경우 많은 시간이 소모됨
- 모든 Feature를 대상으로 이상치 검출 & 제거를 하는 경우 과도하게 많은 행이 삭제될 수 있음
- Label(Target) 열과 낮은 상관관계를 갖고 있는 Feature들의 경우 이상치 제거를 하더라도 결과적인 모델 성능 향상이 미미함
card_df.corr
plt.figure(figsize=(10, 10))
sns.heatmap(card_df.corr(), cmap='RdBu', linewidths=.5)

# Correlation dataframe 기준 최하단 "Class" 행에서 가장 절대값이 큰 상관관계를 보이는 feature 찾기

card_df.corr().loc['Class'].sort_values()
V17      -0.326481
V14      -0.302544
V12      -0.260593
V10      -0.216883
V16      -0.196539
V3       -0.192961
V7       -0.187257
V18      -0.111485
V1       -0.101347
V9       -0.097733
V5       -0.094974
V6       -0.043643
Amount   -0.008326
V24      -0.007221
V13      -0.004570
V15      -0.004223
V23      -0.002685
V22       0.000805
V25       0.003308
V26       0.004455
V28       0.009536
V27       0.017580
V8        0.019875
V20       0.020090
V19       0.034783
V21       0.040413
V2        0.091289
V4        0.133447
V11       0.154876
Class     1.000000
Name: Class, dtype: float64
outlier_index = []

for col in ['V14', 'V17']:
    
    # 전체 데이터프레임을 대상으로 outlier 제거 시, class 1 기준 행의 수가 492개에서 47개로 줄어들어버립니다.
    df_fraud = card_df[card_df['Class'] == 1] 
    
    quantile_25 = df_fraud[col].quantile(0.25)
    quantile_75 = df_fraud[col].quantile(0.75)
    iqr = quantile_75 - quantile_25

    outlier_df = df_fraud[(df_fraud[col] < quantile_25 - iqr * 1.5) | (df_fraud[col] > quantile_75 + iqr * 1.5) ]
    outlier_index = outlier_index + list(outlier_df.index)

print(outlier_index)
[8296, 8615, 9035, 9252]
card_df = card_df.drop(outlier_index)

SMOTE 실습

 

Class-imbalance가 심한 데이터셋을 활용할 시,
무조건 대다수 데이터가 포함된 class를 선택하는 방식으로 모델이 잘못 학습될 수 있습니다. (다수 데이터 class에 치우친 학습 진행)

이 경우, 적은 수의 데이터가 포함된 class의 데이터 양을 늘려주는 방법(over-sampling)을 적용해볼 수 있습니다.
기본적으로 Over-sampling 기법들은 원본 데이터의 값을 살짝씩 변경해 새로운 데이터를 생성하는 방식으로 작동합니다.

저희가 활용할 SMOTE(Synthetic Minority Over-sampling Technique) 기법은
- 적은 수의 데이터가 포함된 class의 개별 데이터들에 대하여 K-Nearest-Neighbor들을 찾아,
- 원본 데이터와 K개 최근접 이웃 사이의 차이를 일정 값으로 만들어,
- 기존 데이터와 약간 차이가 나는 새로운 데이터들을 생성하는 방식으로 작동합니다.

 

x_data = card_df.copy()
del x_data['Class']

y_data = card_df['Class']
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(x_data, y_data, 
                                                    test_size=0.3, 
                                                    stratify=y_data)
                                                    
print('[ Train y ]')
print(y_train.value_counts())
print(y_train.value_counts() / y_train.shape[0] * 100)
print()

print('[ Test y ]')
print(y_test.value_counts())
print(y_test.value_counts() / y_test.shape[0] * 100)
 
[ Train y ]
0    199020
1       342
Name: Class, dtype: int64
0    99.828453
1     0.171547
Name: Class, dtype: float64

[ Test y ]
0    85295
1      146
Name: Class, dtype: int64
0    99.829122
1     0.170878
Name: Class, dtype: float64

 

2) SMOTE 기반 Over-sampling

from imblearn.over_sampling import SMOTE

smote = SMOTE(random_state=42)

X_train_over, y_train_over = smote.fit_sample(X_train, y_train)

print('SMOTE-sampling 적용 전 :', X_train.shape, y_train.shape)
print('SMOTE-sampling 적용 후 :', X_train_over.shape, y_train_over.shape)

print('SMOTE-sampling 적용 전 :')
print(y_train.value_counts())
print()

print('SMOTE-sampling 적용 후 :')
print(y_train_over.value_counts())
SMOTE-sampling 적용 전 : (199362, 29) (199362,)
SMOTE-sampling 적용 후 : (398040, 29) (398040,)
SMOTE-sampling 적용 전 :
Class
0    199020
1       342
Name: count, dtype: int64

SMOTE-sampling 적용 후 :
Class
0    199020
1    199020
Name: count, dtype: int64

 

5. Model training & evaluation (Light-GBM 활용)

conda install lightgbm
model_with_smote = LGBMClassifier(n_estimators=1000, 
                                  num_leaves=64, 
                                  boost_from_average=False)

model_with_smote.fit(X_train_over, y_train_over) # Over-sampling 적용 후

pred = model_with_smote.predict(X_test)
pred_proba = model_with_smote.predict_proba(X_test)[:, 1]

confusion = metrics.confusion_matrix(y_test, pred)
accuracy = metrics.accuracy_score(y_test, pred)
precision = metrics.precision_score(y_test, pred)
recall = metrics.recall_score(y_test, pred)
f1 = metrics.f1_score(y_test, pred)
roc_auc = metrics.roc_auc_score(y_test, pred_proba)

print('Confusion matrix:')
print(confusion)
print()

print('Accuracy :', accuracy)
print('Precision :', precision)
print('Recall :', recall)
print('F1 :', f1)
print('ROC_AUC :', roc_auc)
Confusion matrix:
[[85275    20]
 [   26   120]]

Accuracy : 0.999461616788193
Precision : 0.8571428571428571
Recall : 0.821917808219178
F1 : 0.8391608391608392
ROC_AUC : 0.9784783993023407

SMOTE 기반 Over-sampling 적용 결과,

-> Class 1 데이터 증대
-> 실제 Class 값이 1 인 데이터  model 이 class 1 으로 잘 맞춰낸 비율 Recall 상승

※ 일반적으로 Recall & Precision은 Trade-off 관계가 있음

 

precision : 모델이 True로 예측한 후 실제 True인 예측 결과의 수

recall : 실제 True인 데이터 중 우리 모델이 True로 예측한 결과의 수