728x90

캐글 신용사기 검출 대회

- 2013년 9월 유럽 신용카드 트랜잭션 데이터

- 2일간 284,807 트랜잭션중 492건이 사기로 전체중 0.172%뿐으로 데이터가 매우 불균형함

ref : www.kaggle.com/mlg-ulb/creditcardfraud

 

 

 

The datasets contains transactions made by credit cards in September 2013 by european cardholders.
This dataset presents transactions that occurred in two days, where we have 492 frauds out of 284,807 transactions. The dataset is highly unbalanced, the positive class (frauds) account for 0.172% of all transactions.

 

 

 

 

언더 샘플링, 오버샘플링

- 불균형 레이블 분포를 적절한 학습데이터로 만드는 방법, 오버 샘플링이 유리

- 언더 샘플링 : 클래스가 많은 데이터를 클래스가 적은 데이터 만큼 축소

 ex. 정상 10,000건, 비정상 100건시 정상을 100건으로 줄임. *너무 많은 정상 데이터를 제거

- 오버 샘플링 : 클래스가 적은 데이터를 클래스가 많은 데이터 만큼 증가

 * 단순 증강 시 오버피팅이 발생. 원본 데이터 피처를 약간씩 변형하여 증감

 ex. SMOTE(Syntheic Minority Over sampling techinuqe : knn으로 적은 클래스 데이터 간의 차이로 새 데이터 생성

 * imbalanced-learning 사용

 

 

 

 

 

 

 

1. 데이터로드

 

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore")

path = "./res/credit card fraud detection/creditcard.csv"
df = pd.read_csv(path)
df.head()

 

2. 전처리 및 훈련, 테스트 데이터 셋 분리 정의

 

from sklearn.model_selection import train_test_split

def get_preprocessed_df(df=None):
    """
    input
    df : before preprocessing

    output
    res : after dropping time columns
    """
    res = df.copy()
    res.drop("Time", axis=1, inplace=True)
    return res

def get_train_test_datasets(df=None):
    df_copy = get_preprocessed_df(df)
    X_features = df_copy.iloc[:,:-1]
    y_target= df_copy.iloc[:,-1]
    
    X_train, X_test, y_train, y_test = train_test_split(X_features, y_target,
                                                       test_size=0.2,
                                                       stratify=y_target,
                                                       random_state=100)
    return X_train, X_test, y_train, y_test
    

 

 

 

3. 로지스틱 회귀 모델로 성능 확인

- 정확도는 좋으나 재현률과 F1 스코어가 크게 떨어짐

 

X_train, X_test, y_train, y_test = get_train_test_datasets(df)

from sklearn.linear_model import LogisticRegression
from utils.common import show_metrics


lr = LogisticRegression()
lr.fit(X_train, y_train)
y_pred = lr.predict(X_test)
show_metrics(y_test, y_pred)

 

 

 

4. 모델, 데이터를 줄때 학습 및 성능을 출력하는 함수 정의

 

def get_model_train_eval(model, ftr_train=None, ftr_test=None,
                        tgt_train=None, tgt_test=None):
    model.fit(ftr_train, tgt_train)
    y_pred = model.predict(ftr_test)
    show_metrics(y_test, y_pred)
    return model

 

 

 

5. LightGBM 성능 확인

- LR 보다 평가 지표들이 좋은 결과를 보임

 

from lightgbm import LGBMClassifier

lgbm = LGBMClassifier(n_estimators=1000, num_leaves=32,
                       n_jobs=-1, boost_from_average=False)
lgbm = get_model_train_eval(lgbm, ftr_train=X_train, ftr_test=X_test,
                    tgt_train=y_train, tgt_test = y_test)

 

 

6. 거래 금액 시각화

 

- df의 time, amount를 제외한 컬럼들은 PCA를 통해 얻은 주성분 요소들

- 로지스틱 회귀는 정규분포를 따르는 데이터를 사용하는것이 좋음 -> 표준화 정규기 사용

sns.distplot(df["Amount"])

 

 

 

7. amount 정규화 후 성능 비교

- 로지스틱 분류기나 LGBM이나 큰 성능 차이가 생기지는 않음

from sklearn.preprocessing import StandardScaler

def get_preprocessed_df(df=None):
    res = df.copy()
    scaler = StandardScaler()
    #res["Amount_Scaled"] = scaler.fit_transform(df["Amount"].values.reshape(-1,1))
    amount_scaled = scaler.fit_transform(df["Amount"].values.reshape(-1,1))
    res.insert(0, "Amount_Scaled", amount_scaled)
    res.drop(["Time","Amount"], axis=1, inplace=True)
    return res
    
X_train, X_test, y_train, y_test = get_train_test_datasets(df)
lr = LogisticRegression()
print("logistic regression classification evaluation")
get_model_train_eval(model=lr, ftr_train=X_train, ftr_test=X_test,
                    tgt_train=y_train, tgt_test=y_test)

lgbm = LGBMClassifier(n_estimators=1000, num_leaves=64,
                      n_jobs=-1,boost_from_average=False)
print("\nLGBM classification evaluation")
get_model_train_eval(model=lgbm, ftr_train=X_train, ftr_test=X_test,
                    tgt_train=y_train, tgt_test=y_test)

 

 

 

8. 로그 변환후 성능 비교

- 라벨 분포가 심하게 왜곡된 경우 사용

- log 연산을 통해 매우 큰값도 작은 값으로 변환

 -> 큰 변화는 생기지 않아보임. 교차 검증 필요

 

 

def get_preprocessed_df(df=None):
    res = df.copy()
    amount_scaled = np.log1p(res["Amount"])
    res.insert(0, "Amount_Scaled", amount_scaled)
    res.drop(["Time","Amount"], axis=1, inplace=True)
    return res


X_train, X_test, y_train, y_test = get_train_test_datasets(df)
lr = LogisticRegression()
print("logistic regression classification evaluation")
get_model_train_eval(model=lr, ftr_train=X_train, ftr_test=X_test,
                    tgt_train=y_train, tgt_test=y_test)

lgbm = LGBMClassifier(n_estimators=1000, num_leaves=64,
                      n_jobs=-1, boost_from_average=False)
print("\nLGBM classification evaluation")
get_model_train_eval(model=lgbm, ftr_train=X_train, ftr_test=X_test,
                    tgt_train=y_train, tgt_test=y_test)

 

 

9. 이상치 제거를 위한 히트맵 시각화

- 클래스와 가장 강한 음의 상관 관계를 갖는 변수로 V17이 있는걸 확인할수 있음.

card_df = get_preprocessed_df(df)
plt.figure(figsize=(12,12))
corr = card_df.corr()
sns.heatmap(corr, cmap="RdBu")

 

 

10. 이상치 제거 후 성능 비교

- 25% - 1.5 * IQR보다 작거나 75% + 1.5 * IQR보다 큰 경우 아웃라이어판단.

- V17의 아웃라이어들 제거 후 성능 평가.

- LGBM의 평가 지표들이 약간 개선됨

def get_outlier(df=None, column=None, weight=1.5):
    fraud = df[df["Class"] == 1][column]
    quatile_25 = np.percentile(fraud.values, 25)
    quatile_75 = np.percentile(fraud.values, 75)
    
    iqr = quatile_75 - quatile_25
    iqr_weight = iqr * weight
    lowest_val = quatile_25 - iqr_weight
    highest_val = quatile_75 + iqr_weight
    outlier_idx = fraud[(fraud < lowest_val) | (fraud > highest_val)].index
    return outlier_idx


def get_preprocessed_df(df=None):
    res = df.copy()
    amount_scaled = np.log1p(res["Amount"])
    res.insert(0, "Amount_Scaled", amount_scaled)
    res.drop(["Time","Amount"], axis=1, inplace=True)
    
    outlier_index = get_outlier(df=res, column = "V14", weight=1.5)
    res.drop(outlier_index, axis=0, inplace=True)
    return res
    
X_train, X_test, y_train, y_test = get_train_test_datasets(df)
lr = LogisticRegression()
print("logistic regression classification evaluation")
get_model_train_eval(model=lr, ftr_train=X_train, ftr_test=X_test,
                    tgt_train=y_train, tgt_test=y_test)

lgbm = LGBMClassifier(n_estimators=1000, num_leaves=64,
                      n_jobs=-1, boost_from_average=False)
print("\nLGBM classification evaluation")
get_model_train_eval(model=lgbm, ftr_train=X_train, ftr_test=X_test,
                    tgt_train=y_train, tgt_test=y_test)

 

 

 

11. SMOTE 사용하기 위한 imblanced-learn 설치. cannot import six 에러 해결(버전 매칭)

원랜 아래와 같이 하면되나

pip install imbalanced-learn

pypi.org/project/imbalanced-learn/#description

 

최신 imbalanced-learn이 scikit-learn 0.23 이상 버전을 요구

-> 자동 업그레이드 중 문제 발생했는지 cannot import six 에러로 사용불가

 

pip install -U imbalanced-learn==0.6.2

pip install -U scikit-learn==0.22.2

로 다운그레이드 하여 해결

 

 

12. over sampling

- SMOTE oversampling을 통해 작은 라밸을 큰 라밸과 크기를 맞춤

- lr의 경우 재현율은 좋아졌으나 정밀도와 f1 score는 크게저하

- lgbm은 성능 지표들이 대채로 저하

 

from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state=100)
X_train_over, y_train_over = smote.fit_sample(X_train, y_train)
print("berfore smote : ",X_train.shape, y_train.shape)
print("after smote : ", X_train_over.shape, y_train_over.shape)
print("label distribution after smote \n", pd.Series(y_train_over).value_counts())

 

 

 

 

 

 

13. 성능 지표 정리

 

 

300x250

+ Recent posts