Ensemble - 신용카드 사기 검출(Kaggle)

Undersampling , Oversampling

  • 레이블 값이 불균형한 분포를 가질 때, 제대로 된 학습이 되자 않는다.
  • 따라서 적절한 학습 데이터를 얻기 위해서, 언더 샘플링(Undersampling)과 오버 샘플링(Oversampling)을 사용한다.

  • 언더 샘플링(Undersampling)의 경우 정상 레이블 데이터를 너무 많이 감소시킨다는 단점 때문에, 오히려 학습에 방해가 된다.
  • 따라서 오버 샘플링(Oversampling)을 이용하며, 대표적으로 SMOTE(Synthetic Minority Over-sampling Technique)을 사용한다.



실제 데이터를 이용한 분류

  • 데이터를 불러와서 피처를 파악한다.

[코드]

card_df = pd.read_csv('./train_creditcard.csv')
card_df.head()

[결과]


  • Amount 피처는 신용카드 트랜잭션 금액을 의미한다.
  • Class는 0의 경우 정상, 1의 경우 사기 트랜잭션이다.
  • Time 피처는 작업용 속성으로서 의미가 없다. 따라서 삭제한다.
  • 학습 데이터와 테스트 데이터로 분리한다.


[코드]

def get_preprocessed_df(df=None):
    df_copy = df.copy()
    df_copy.drop('Time', axis=1, inplace = True)
    return df_copy

def get_train_test_dataset(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.3, random_state=0, stratify=y_target)
    return X_train, X_test, y_train, y_test

[결과]

학습 데이터의 레이블 값 비율
0    99.827451
1     0.172549

테스트 데이터의 레이블 값 비율
0    99.826785
1     0.173215
  • 학습 데이터와 테스트 데이터의 레이블의 비율이 차이없이 잘 분할된 것을 확인할 수 있다.

  • 로지스틱 회귀와 lightGBM을 이용하여 데이터를 학습시키고 예측 성능을 비교한다.

[코드]

def get_model_train_eval(model, ftr_train=None, ftr_test=None, tgt_train=None, tgt_test=None):
    model.fit(ftr_train, tgt_train)
    pred = model.predict(ftr_test)
    pred_proba = model.predict_proba(ftr_test)[:,1]
    get_clf_eval(tgt_test, pred, pred_proba)
    
    
lr_clf = LogisticRegression()
get_model_train_eval(lr_clf, ftr_train=X_train, ftr_test = X_test, tgt_train = y_train, tgt_test = y_test)

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

[결과]

[로지스틱 회귀]
오차 행렬
[[85282    13]
 [   57    91]]
정확도 0.9992, 정밀도 0.8750, 재현율 : 0.6149, F1 : 0.7222, AUC : 0.9570

[lightGBM]
오차 행렬
[[85289     6]
 [   36   112]]
정확도 0.9995, 정밀도 0.9492, 재현율 : 0.7568, F1 : 0.8421, AUC : 0.9797
  • lightGBM의 정밀도 재현율, AUC의 값이 로지스틱의 것보다 높은 것을 확인할 수 있다.

  • 재현율과 AUC의 더 높이기 위해 데이터의 분포를 변화시킨다.

  • 데이터의 분포를 보면 꼬리가 긴 형태의 분포 곡선을 가진 것을 볼 수 있다.(1000이하의 데이터가 대부분이다.)
  • 정규 분포 형태로 바꾼다.

[코드]

def get_preprocessed_df(df=None):
    df_copy = df.copy()
    scaler = StandardScaler()
    amount_n = scaler.fit_transform(df_copy['Amount'].values.reshape(-1,1))
    df_copy.insert(0,'Amount_Scaled', amount_n)
    df_copy.drop(['Time','Amount'], axis=1, inplace=True)
    return df_copy

  • 다시 분류후 학습/예측/평가를 한다.
[로지스틱 회귀]
오차 행렬
[[85281    14]
 [   58    90]]
정확도 0.9992, 정밀도 0.8654, 재현율 : 0.6081, F1 : 0.7143, AUC : 0.9702

[lightGBM]
오차 행렬
[[85289     6]
 [   36   112]]
정확도 0.9995, 정밀도 0.9492, 재현율 : 0.7568, F1 : 0.8421, AUC : 0.9773

  • 정규 분포 형태로 바꾸기 전과 큰 차이가 없음을 알 수 있다.

  • 로그 변환 기법을 이용해본다. (데이터의 분포도가 심하게 왜곡되었을 때, 자주 적용하는 기법)

[코드]

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

[결과]

[로지스틱 회귀]
오차 행렬
[[85283    12]
 [   59    89]]
정확도 0.9992, 정밀도 0.8812, 재현율 : 0.6014, F1 : 0.7149, AUC : 0.9727

[lightGBM]
오차 행렬
[[85290     5]
 [   35   113]]
정확도 0.9995, 정밀도 0.9576, 재현율 : 0.7635, F1 : 0.8496, AUC : 0.9786
  • 두 모델 모두 재현율, AUC가 소폭 상승한 것을 볼 수 있다.


  • 이상치 데이터를 탐색하고 해당 데이터를 삭제한다.
  • IQR 방식으로 사분위 값을 이용한다.

[코드]

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
  • 해당하는 행을 삭제하고 다시 분류/학습/예측을 수행한다.
[로지스틱 회귀]
오차 행렬
[[85281    14]
 [   48    98]]
정확도 0.9993, 정밀도 0.8750, 재현율 : 0.6712, F1 : 0.7597, AUC : 0.9743

[lightGBM]
오차 행렬
[[85291     4]
 [   25   121]]
정확도 0.9997, 정밀도 0.9680, 재현율 : 0.8288, F1 : 0.8930, AUC : 0.9831
  • 이전의 재현율 60.14%, 76.35%에 비해서 67.12%, 82.88%로 크게 상승한 것을 볼 수 있다.
  • 현재까지 이상치 제거하는 방법이 가장 높은 상승률을 보여준다.


  • 다음으로 SMOTE 오버샘플링을 적용하여 학습/예측/평가를 진행한다(해당 데이터세트는 이상치를 제거한 데이터 세트이다.)

[코드]

smote = SMOTE(random_state=0)
X_train_over, y_train_over = smote.fit_sample(X_train, y_train)

[결과]

SMOTE 적용전 학습용 피처/레이블 데이터 세트 :  (199364, 29) (199364,)
SMOTE 적용 후 학습용 피처/레이블 데이터 세트 :  (398040, 29) (398040,)
SMOTE 적용 후 레이블 값 분포 : 
0    199020
1       344
  • 위의 표에서 알 수 있듯이, 데이터 세트의 개수는 늘었지만, 레이블의 분포는 동일한 것을 볼 수 있다.
  • 해당 데이터 세트에 학습/예측/평가를 진행한다.(로지스틱 회귀)
오차 행렬
오차 행렬
[[82937  2358]
 [   11   135]]
정확도 0.9723, 정밀도 0.0542, 재현율 : 0.9247, F1 : 0.1023, AUC : 0.9737
  • 재현율이 92.47% 큰 상승을 보이는 반면 정밀도는 5.42%로 폭락한 것을 알 수 있다.
  • 이러한 현상은 오버 샘플링으로 인해 Class=1의 데이터가 지나치게 많아지면서 정밀도가 떨어진것으로 보인다.
  • 임계값에 따라서 정밀도와 재현율이 어떠한 변화를 보이는지 시각화해서 확인한다.

  • 임계값 0.99에서 상당히 민감도가 심한 것을 볼 수 있다. (0.99를 기준으로 정밀도, 재현율 값이 크게 변한다.)
  • 따라서 로지스틱 회귀는 SMOTE로 오버샘플링된 데이터 세트에 적절하지 못하다고 할 수 있다.
  • lightGBM 모델로 학습/예측/평가를 수행한다.
오차 행렬
[[85286     9]
 [   22   124]]
정확도 0.9996, 정밀도 0.9323, 재현율 : 0.8493, F1 : 0.8889, AUC : 0.9789
  • 재현율은 82.88%에서 84.93%로 상승했다. 하지만 정밀도는 93.23%로 하락한 것을 볼 수 있다.
  • SMOTE 오버 샘플림을 적용하면 일반적으로 재현율은 상승하고 정밀도가 떨어진다.