Oversampling(신용카드 부정사용 검출)

yeoni·2023년 6월 16일
0

머신러닝

목록 보기
21/40

학습 중요 포인트

  • Undersampling vs Oversampling

1. 데이터

  • Kaggle: https://www.kaggle.com/MLG-ULB/CREDITCARDFRAUD
  • 신용카드 사기 검출 분류 실습용 데이터
  • 'class' 컬럼 → 사기 유무 의미
  • 'class' 컬럼의 불균형 극심 → 약 0.172%(사기 Fraud)
  • 'Amount': 거래금액
  • 데이터의 특성은 여러 이유로 이름이 감춰져 있다.
import pandas as pd
raw_data = pd.read_csv('./data/creditcard.csv')
raw_data.head()

frauds_rate = round(raw_data['Class'].value_counts()[1]/len(raw_data) * 100, 2)
print('Frauds', frauds_rate, '% of the dataset') # Frauds 0.17 % of the dataset

import seaborn as sns
import matplotlib.pyplot as plt
sns.countplot(x='Class', data=raw_data)
plt.title('Class Distributions \n (0: No Fraud || 1: Fraud)', fontsize=14)
plt.show()

2. raw data 분석

필요한 함수 정의

  • 분류기의 성능을 return하는 함수
  • 성능을 출력하는 함수
  • 모델과 데이터(train, test) 입력 → fit, predict →성능을 return하는 함수
  • 다수의 모델의 성능 DataFrame으로 출력하는 함수
  • 모델별 ROC 커브를 그리는 함수
  • Outlier를 정리하기 위해 Outlier의 인덱스를 파악하는 함수
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score
from sklearn.metrics import confusion_matrix

# 분류기의 성능을 return하는 함수
def get_clf_eval(y_test, pred):
    acc = accuracy_score(y_test, pred)
    pre = precision_score(y_test, pred)
    re = recall_score(y_test, pred)
    f1 = f1_score(y_test, pred)
    auc = roc_auc_score(y_test, pred)

    return acc, pre, re, f1, auc

# 성능을 출력하는 함수
def print_clf_eval(y_test, pred):
    confusion = confusion_matrix(y_test, pred)
    acc, pre, re, f1, auc = get_clf_eval(y_test, pred)

    print('=> confusion matrix')
    print(confusion)
    print('===============')

    print('Accuracy: {0: .4f}, Precision: {1:.4f}'.format(acc, pre))
    print('Recall: {0: .4f}, F1: {1:.4f}, AUC: {2:.4f}'.format(re, f1, auc))

# 모델과 데이터를 주면 성능을 return하는 함수
def get_result(model, X_train, y_train, X_test, y_test):
    model.fit(X_train, y_train)
    pred = model.predict(X_test)

    return get_clf_eval(y_test, pred)

# 다수의 모델의 성능을 정리해서 DataFrame으로 반환하는 함수
def get_result_pd(models, model_names, X_train, y_train, X_test, y_test):
    col_names = ['accuracy', 'precision', 'recall', 'f1', 'roc_auc']
    tmp = []
    for model in models:
        tmp.append(get_result(model, X_train, y_train, X_test, y_test))
    return pd.DataFrame(tmp, columns=col_names, index=model_names)

# 모델별 ROC 커브
from sklearn.metrics import roc_curve
def draw_roc_curve(models, models_names, X_test, y_test):
    plt.figure(figsize=(10, 10))

    for model in range(len(models)):
        pred = models[model].predict_proba(X_test)[:, 1]
        fpr, tpr, thresholds = roc_curve(y_test, pred)
        plt.plot(fpr, tpr, label=models_names[model])

    plt.plot([0, 1], [0, 1], 'k--', label='random quess')
    plt.title('ROC')
    plt.legend()
    plt.grid()
    plt.show()
    
# Outlier를 정리하기 위해 Outlier의 인덱스를 파악하는 코드 -> boxplot 이용
def get_outlier(df=None, column=None, weight=1.5):
    fraud = df[df['Class']==1][column]
    quantile_25 = np.percentile(fraud.values, 25)
    quantile_75 = np.percentile(fraud.values, 75)

    iqr = quantile_75 - quantile_25
    iqr_weight = iqr * weight
    lowest_val = quantile_25 - iqr_weight
    highest_val = quantile_75 + iqr_weight

    outlier_index = fraud[(fraud < lowest_val) | (fraud > highest_val)].index

    return outlier_index

분류 모델 실행

1) X, y로 데이터 선정
2) 데이터 분리
3) LogisticRegression, RandomForestClassifier, LGBMClassifier & 성능 return 함수와 데이터 프레임화 함수 사용

  • 은행: Recall(재현율, 실제 True인 것 중에서 모델이 True라고 예측한 것), 사용자: Precision(정밀도, True라고 분류한 것 중에서 실제 True인 것의 비율) 향상된 결과가 좋다.
  • 앙상블 계열의 성능이 우수
X = raw_data.iloc[:, 1:-1]
y= raw_data.iloc[:, -1]

# 데이터 분리
from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=13, stratify=y)


from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from lightgbm import LGBMClassifier

# LogisticRegression
lr_clf = LogisticRegression(random_state=13, solver='liblinear')

# DecisionTreeClassifier
dt_clf = DecisionTreeClassifier(random_state=13, max_depth=4)

# RandomForestClassifier
rf_clf = RandomForestClassifier(random_state=13, n_jobs=-1, n_estimators=100)

# LGBMClassifier
lgbm_clf = LGBMClassifier(n_estimators=1000, num_leaves=64, n_jobs=-1, boost_from_average=False)

import time
models = [lr_clf, dt_clf, rf_clf, lgbm_clf]
models_names = ['LinearReg', 'DecisionTree', 'RandomForest', 'LightGBM']
start_time = time.time()
results = get_result_pd(models, models_names, X_train, y_train, X_test, y_test)
print('Fit time: ', time.time() - start_time)
results

3. StandardScaler

  • raw_data의 Amount 컬럼 확인 → 컬럼의 분포가 특정 대역이 아주 많다.
  • Amount 컬럼에 StandardScaler 적용
  • 데이터 분리 이후 모델평가 & ROC curve
  • 큰 변화는 없다.
  • reshape(정수, 정수) : reshape(정수 행, 정수 열)의 2차원 배열로 값을 변형
  • reshape(-1, 정수) : 남은 배열의 길이와 남은 차원으로부터 추정해서 알아서 지정하라는 의미 ex) 12개 원소 1차원 배열 reshape(-1, 1) = reshape(12, 1)
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
amount_n = scaler.fit_transform(raw_data['Amount'].values.reshape(-1, 1))

raw_data_copy = raw_data.copy()
raw_data_copy = raw_data_copy.iloc[:, 1:-2]
raw_data_copy['Amount_Scaled'] = amount_n

X_train, X_test, y_train, y_test = train_test_split(raw_data_copy, y, test_size=0.3, random_state=13, stratify=y)

# 모델 다시 평가
models = [lr_clf, dt_clf, rf_clf, lgbm_clf]
models_names = ['LinearReg', 'DecisionTree', 'RandomForest', 'LightGBM']
start_time = time.time()
results = get_result_pd(models, models_names, X_train, y_train, X_test, y_test)
print('Fit time: ', time.time() - start_time)

draw_roc_curve(models, models_names, X_test, y_test)

4. log scale

  • log scale → 컬럼의 분포가 변화
  • 데이터분리 → 모델 평가 & 그래프
  • 아주 미세한 변화
import numpy np
amount_log = np.log1p(raw_data['Amount']) 
raw_data_copy['Amount_Scaled'] = amount_log

plt.figure(figsize=(10, 5)) 
sns.distplot(raw_data_copy['Amount_Scaled'], color='r')
plt.show()

# 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(raw_data_copy, y, test_size=0.3, random_state=13, stratify=y)

models = [lr_clf, dt_clf, rf_clf, lgbm_clf]
models_names = ['LinearReg', 'DecisionTree', 'RandomForest', 'LightGBM']
start_time = time.time()
results = get_result_pd(models, models_names, X_train, y_train, X_test, y_test)
print('Fit time: ', time.time() - start_time)

draw_roc_curve(models, models_names, X_test, y_test)


5. Outlier 정리

  • 특이 데이터가 있는지 확인 → 'V14' 컬럼에 많은 것을 확인
  • Outlier 인덱스 함수 이용
  • X는 logscale 데이터 이용 & Outlier 인덱스 제거
  • y는 raw_data에서 'class'컬럼 분리 후 Outlier 인덱스 제거
  • 다시 데이터 분리하고 모델평가
# Outlier 찾기
outlier_index = get_outlier(df=raw_data, column='V14')

# Outlier 제거, logscale 데이터 이용
raw_data_copy_copy = raw_data_copy.copy()
raw_data_copy_copy.drop(outlier_index, axis=0, inplace=True)

# 데이터 분리
X = raw_data_copy_copy
raw_data_copy2 = raw_data.copy() 
raw_data_copy2.drop(outlier_index, axis=0, inplace=True)
y = raw_data_copy2.iloc[:, -1] 

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=13, stratify=y)

# 모델 다시 평가
models = [lr_clf, dt_clf, rf_clf, lgbm_clf]
models_names = ['LinearReg', 'DecisionTree', 'RandomForest', 'LightGBM']
start_time = time.time()
results = get_result_pd(models, models_names, X_train, y_train, X_test, y_test)
print('Fit time: ', time.time() - start_time)

draw_roc_curve(models, models_names, X_test, y_test)

6. Undersampling vs Oversampling

  • 데이터의 불균형이 극심할 때 불균형한 두 클래스의 분포를 강제로 맞춰보는 작업
  • 언더샘플링 : 많은 수의 데이터를 적은 수의 데이터로 강제로 조정
  • 오버샘플링 :
    • 원본데이터의 피처 값들을 아주 약간 변경하여 증식
    • 대표적으로 SMOTE(Synthetic Minority Over-sampling Technique) 방법이 있음
    • 적은 데이터 세트에 있는 개별 데이터를 k-최근접이웃 방법으로 찾아서
      데이터의 분포 사이에 새로운 데이터를 만드는 방식
    • imbalanced-learn 이라는 Python pkg가 있음

colab에서 SMOTE 실습

  • pip install imbalanced-learn 이후 커널이 죽어서 코랩에서 실습 진행
  • logscale, outlier 정리한 데이터로 실습
  • 오버샘플링 실습 결과: recall이 좋아짐.
from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state=13)
X_train_over, y_train_over = smote.fit_resample(X_train, y_train)

# 데이터 증강효과
print(np.unique(y_train, return_counts=True)) # (array([0, 1]), array([199020,    342]))
print(np.unique(y_train_over, return_counts=True)) # (array([0, 1]), array([199020, 199020]))

# 모델 평가
models = [lr_clf, dt_clf, rf_clf, lgbm_clf]
models_names = ['LinearReg', 'DecisionTree', 'RandomForest', 'LightGBM']
start_time = time.time()
results = get_result_pd(models, models_names, X_train_over, y_train_over, X_test, y_test)
print('Fit time: ', time.time() - start_time)
results

draw_roc_curve(models, models_names, X_test, y_test)



Reference
1) 제로베이스 데이터스쿨 강의자료
2) https://yololife-sy.medium.com/python-reshape-1-1-%EC%97%90%EC%84%9C-1%EC%9D%98-%EC%9D%98%EB%AF%B8-97b713be5230

profile
데이터 사이언스 / just do it

0개의 댓글