Posts 신용카드 부정 사용자 데이터로 해보는 부스팅
Post
Cancel

신용카드 부정 사용자 데이터로 해보는 부스팅

1. 신용카드 부정사용자 검출


1.1 신용카드 부정사용자 검출

  • 신용카드 사기 검출 분류 실습용 데이터
  • 데이터에 class라는 이름의 컬럼이 사기 유무를 의미
  • class 컬럼의 불균형이 극심해서 전체 데이터의 약 0.172%가 사기(Fraud)를 가짐
  • 금융 데이터이고, 기업의 기밀 보호를 위해 대다수의 특성이름은 삭제되어있음
  • Amount : 거래금액
  • Class : 사기 여부(1이면 Fraud)


1.2 데이터 로드

1
2
3
import pandas as pd
raw_data = pd.read_csv('https://media.githubusercontent.com/media/hmkim312/datas/main/creditcard/creditcard.csv')
raw_data.head()
TimeV1V2V3V4V5V6V7V8V9...V21V22V23V24V25V26V27V28AmountClass
00.0-1.359807-0.0727812.5363471.378155-0.3383210.4623880.2395990.0986980.363787...-0.0183070.277838-0.1104740.0669280.128539-0.1891150.133558-0.021053149.620
10.01.1918570.2661510.1664800.4481540.060018-0.082361-0.0788030.085102-0.255425...-0.225775-0.6386720.101288-0.3398460.1671700.125895-0.0089830.0147242.690
21.0-1.358354-1.3401631.7732090.379780-0.5031981.8004990.7914610.247676-1.514654...0.2479980.7716790.909412-0.689281-0.327642-0.139097-0.055353-0.059752378.660
31.0-0.966272-0.1852261.792993-0.863291-0.0103091.2472030.2376090.377436-1.387024...-0.1083000.005274-0.190321-1.1755750.647376-0.2219290.0627230.061458123.500
42.0-1.1582330.8777371.5487180.403034-0.4071930.0959210.592941-0.2705330.817739...-0.0094310.798278-0.1374580.141267-0.2060100.5022920.2194220.21515369.990

5 rows × 31 columns

1.3 특성확인

1
raw_data.columns
1
2
3
4
5
Index(['Time', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6', 'V7', 'V8', 'V9', 'V10',
       'V11', 'V12', 'V13', 'V14', 'V15', 'V16', 'V17', 'V18', 'V19', 'V20',
       'V21', 'V22', 'V23', 'V24', 'V25', 'V26', 'V27', 'V28', 'Amount',
       'Class'],
      dtype='object')
  • 데이터의 특성은 여러가지의 이유로 감추어져 있어서 V1, V2 이런식임


1.4 데이터 라벨의 불균형 확인

1
raw_data['Class'].value_counts()
1
2
3
0    284315
1       492
Name: Class, dtype: int64
1
2
fraud_rate = round(raw_data['Class'].value_counts()[1] / len(raw_data) * 100, 2)
print(f'Frauds {fraud_rate}% of the dataset')
1
Frauds 0.17% of the dataset
  • 실제로 부정사용자는 전체 데이터의 0.17% 밖에 안됨
  • 그렇기 때문에 정확히 잘 잡는게 중요함


1.5 그래프로도 안보임

1
2
3
4
5
6
import seaborn as sns
import matplotlib.pyplot as plt

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

  • title을 적을때 \n하면 줄바꿈이 됨


1.6 X, y 로 데이터 나누기

1
2
3
4
5
6
7
from sklearn.model_selection import train_test_split

X = raw_data.iloc[:, 1:-1]
y = raw_data.iloc[:, -1]

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=13, stratify=y)
  • stratify : Data의 비율을 유지시킴
    • 만약 Y가 Data가 25%의 0과 75%의 1로 이루어진 Binary Set일 때, stratify=Y로 설정하면 나누어진 데이터셋들도 0과 1을 각각 25%, 75%로 유지한 채 분할됨


1.7 불균형 정도 확인

1
2
3
import numpy as np

np.unique(y_train, return_counts=True)
1
(array([0, 1]), array([199020,    344]))
  • np.unique에서 retunr_counts를 하면 각 갯수를 보여줌


1
2
tmp = np.unique(y_train, return_counts=True)[1]
tmp[1] / len(y_train) * 100
1
0.17254870488152324
  • train 데이터에서의 불균형도 엄청남


1
np.unique(y_test, return_counts= True)
1
(array([0, 1]), array([85295,   148]))


1
2
tmp = np.unique(y_test, return_counts=True)[1]
tmp[1] / len(y_test) * 100
1
0.17321489179921118
  • test 데이터에서의 불균형


2. 첫번째 스탭


2.1 분류기의 성능을 측정하는 함수 작성

1
2
3
4
5
6
7
8
9
10
from sklearn.metrics import (accuracy_score, precision_score, recall_score, f1_score, roc_auc_score)

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
  • 각 분류기들의 accuracy, precision, recall, f1, roc를 볼수 있는 함수 작성


2.2 성능을 출력하는 함수 작성

1
2
3
4
5
6
7
8
9
10
11
from sklearn.metrics import confusion_matrix

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))
  • 모델별로 위에서 생성한 score를 출력하는 함수 작성


2.3 Logistic Regression

1
2
3
4
5
6
7
from sklearn.linear_model import LogisticRegression

lr_clf = LogisticRegression(random_state=13, solver='liblinear')
lr_clf.fit(X_train, y_train)
lr_pred = lr_clf.predict(X_test)

print_clf_eval(y_test, lr_pred)
1
2
3
4
5
6
==> confusion matrix
[[85284    11]
 [   60    88]]
=========
Accuracy : 0.999169, Precision : 0.888889
Recall : 0.594595, F1 : 0.712551, AUC : 0.797233
  • accuracy가 0.99??


2.4 Decision Tree

1
2
3
4
5
6
7
from sklearn.tree import DecisionTreeClassifier

dt_clf = DecisionTreeClassifier(random_state= 13, max_depth= 4)
dt_clf.fit(X_train, y_train)
dt_pred = dt_clf.predict(X_test)

print_clf_eval(y_test, dt_pred)
1
2
3
4
5
6
==> confusion matrix
[[85281    14]
 [   42   106]]
=========
Accuracy : 0.999345, Precision : 0.883333
Recall : 0.716216, F1 : 0.791045, AUC : 0.858026
  • 이것도 accuracy가 0.99?


2.5 Random Forest

1
2
3
4
5
6
from sklearn.ensemble import RandomForestClassifier
rf_clf = RandomForestClassifier(random_state=13, n_jobs=-1, n_estimators=100)
rf_clf.fit(X_train, y_train)
rf_pred = rf_clf.predict(X_test)

print_clf_eval(y_test, rf_pred)
1
2
3
4
5
6
==> confusion matrix
[[85290     5]
 [   38   110]]
=========
Accuracy : 0.999497, Precision : 0.956522
Recall : 0.743243, F1 : 0.836502, AUC : 0.871592
  • 마찬가지로 성능이 좋아보인다


2.6 LightGBM

1
2
3
4
5
6
7
8
from lightgbm import LGBMClassifier

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

lgbm_clf.fit(X_train, y_train)
lgbm_pred = lgbm_clf.predict(X_test)

print_clf_eval(y_test, lgbm_pred)
1
2
3
4
5
6
==> confusion matrix
[[85289     6]
 [   34   114]]
=========
Accuracy : 0.999532, Precision : 0.950000
Recall : 0.770270, F1 : 0.850746, AUC : 0.885100
  • 이것도 0.99가 나온다.


3. 전체 모델을 한번에 볼수있게 하기


3.1 모델과 데이터를 주면 성능을 출력하는 함수 작성

1
2
3
4
5
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)


3.2 다수의 모델의 성능을 정리해서 DataFrame으로 반환하는 함수 작성

1
2
3
4
5
6
7
8
9
def get_result_pd(models, model_names, X_train, y_train, X_test, y_test):
    col_names = ['accuracy', 'precision', 'recall', 'f1', 'roc_acu']
    
    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)


3.3 4개의 분류 모델을 한번에 표로 정리하기

1
2
3
4
5
6
7
8
9
10
11
12
import time

models = [lr_clf, dt_clf, rf_clf, lgbm_clf]

model_names = ['LinearReg', 'DecisionTree', 'RandomForest', 'LightGBM']

start_time = time.time()

results = get_result_pd(models, model_names, X_train, y_train, X_test, y_test)

print('Fit time : ', time.time() - start_time)
results
1
Fit time :  26.41209316253662
accuracyprecisionrecallf1roc_acu
LinearReg0.9991690.8888890.5945950.7125510.797233
DecisionTree0.9993450.8833330.7162160.7910450.858026
RandomForest0.9994970.9565220.7432430.8365020.871592
LightGBM0.9995320.9500000.7702700.8507460.885100
  • 앙상블 계열의 성능이 우수함을 알수 있음


4. 데이터를 정리해서 다시 도전해보기


4.1 raw_data의 Amount 컬럼 확인

1
2
3
plt.figure(figsize=(10, 5))
sns.distplot(raw_data['Amount'], color='r')
plt.show()

  • 특정 구간의 분포가 많음


4.2 Amount 컬럼에 StandardScaler 적용

1
2
3
4
5
6
7
8
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
amount_n = scaler.fit_transform(raw_data['Amount'].values.reshape(-1, 1))

raw_data_copy = raw_data.iloc[:, 1:-2]
raw_data_copy['Amount_Scaled'] = amount_n
raw_data_copy.head()
V1V2V3V4V5V6V7V8V9V10...V20V21V22V23V24V25V26V27V28Amount_Scaled
0-1.359807-0.0727812.5363471.378155-0.3383210.4623880.2395990.0986980.3637870.090794...0.251412-0.0183070.277838-0.1104740.0669280.128539-0.1891150.133558-0.0210530.244964
11.1918570.2661510.1664800.4481540.060018-0.082361-0.0788030.085102-0.255425-0.166974...-0.069083-0.225775-0.6386720.101288-0.3398460.1671700.125895-0.0089830.014724-0.342475
2-1.358354-1.3401631.7732090.379780-0.5031981.8004990.7914610.247676-1.5146540.207643...0.5249800.2479980.7716790.909412-0.689281-0.327642-0.139097-0.055353-0.0597521.160686
3-0.966272-0.1852261.792993-0.863291-0.0103091.2472030.2376090.377436-1.387024-0.054952...-0.208038-0.1083000.005274-0.190321-1.1755750.647376-0.2219290.0627230.0614580.140534
4-1.1582330.8777371.5487180.403034-0.4071930.0959210.592941-0.2705330.8177390.753074...0.408542-0.0094310.798278-0.1374580.141267-0.2060100.5022920.2194220.215153-0.073403

5 rows × 29 columns

  • 일단 모든 데이터에 Scaler를 적용함


4.3 데이터 나누기

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


4.4 모델 재 평가

1
2
3
4
5
6
7
8
9
10
11
12
import time

models = [lr_clf, dt_clf, rf_clf, lgbm_clf]

model_names = ['LinearReg', 'DecisionTree', 'RandomForest', 'LightGBM']

start_time = time.time()

results = get_result_pd(models, model_names, X_train, y_train, X_test, y_test)

print('Fit time : ', time.time() - start_time)
results
1
Fit time :  23.386101961135864
accuracyprecisionrecallf1roc_acu
LinearReg0.9991690.8888890.5945950.7125510.797233
DecisionTree0.9993450.8833330.7162160.7910450.858026
RandomForest0.9994970.9565220.7432430.8365020.871592
LightGBM0.9995200.9495800.7635140.8464420.881722

4.5 모델별 ROC커브

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from sklearn.metrics import roc_curve


def draw_roc_curve(models, model_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=model_names[model])

    plt.plot([0, 1], [0, 1], 'k--', label='random quess')
    plt.title('ROC')
    plt.legend()
    plt.grid()
    plt.show()
    
draw_roc_curve(models, model_names, X_test, y_test)

  • 딱히 변화는 없어보인다.
  • 스케일링을 다른것으로 해볼까?


4.6 log scale

1
2
3
4
amount_log = np.log1p(raw_data['Amount'])

raw_data_copy['Amount_Scaled'] = amount_log
raw_data_copy.head()
V1V2V3V4V5V6V7V8V9V10...V20V21V22V23V24V25V26V27V28Amount_Scaled
0-1.359807-0.0727812.5363471.378155-0.3383210.4623880.2395990.0986980.3637870.090794...0.251412-0.0183070.277838-0.1104740.0669280.128539-0.1891150.133558-0.0210535.014760
11.1918570.2661510.1664800.4481540.060018-0.082361-0.0788030.085102-0.255425-0.166974...-0.069083-0.225775-0.6386720.101288-0.3398460.1671700.125895-0.0089830.0147241.305626
2-1.358354-1.3401631.7732090.379780-0.5031981.8004990.7914610.247676-1.5146540.207643...0.5249800.2479980.7716790.909412-0.689281-0.327642-0.139097-0.055353-0.0597525.939276
3-0.966272-0.1852261.792993-0.863291-0.0103091.2472030.2376090.377436-1.387024-0.054952...-0.208038-0.1083000.005274-0.190321-1.1755750.647376-0.2219290.0627230.0614584.824306
4-1.1582330.8777371.5487180.403034-0.4071930.0959210.592941-0.2705330.8177390.753074...0.408542-0.0094310.798278-0.1374580.141267-0.2060100.5022920.2194220.2151534.262539

5 rows × 29 columns

  • 단위수가 너무 큰 값들을 바로 회귀분석 할 경우, 결과를 왜곡할 우려가 있으므로 이를 방지하기 위해.
  • 독립변수와 종속변수의 변화관계에서 절대량이 아닌 비율을 확인하기 위해
  • 비선형관계의 데이터를 선형으로 만들기 위해


4.7 Log scale 후의 분포

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

  • log를 취해주니, 분포의 변화가 생겼음


4.8 성능 평가

1
2
3
4
5
6
7
8
9
10
11
12
import time

models = [lr_clf, dt_clf, rf_clf, lgbm_clf]

model_names = ['LinearReg', 'DecisionTree', 'RandomForest', 'LightGBM']

start_time = time.time()

results = get_result_pd(models, model_names, X_train, y_train, X_test, y_test)

print('Fit time : ', time.time() - start_time)
results
1
Fit time :  23.800795793533325
accuracyprecisionrecallf1roc_acu
LinearReg0.9991690.8888890.5945950.7125510.797233
DecisionTree0.9993450.8833330.7162160.7910450.858026
RandomForest0.9994970.9565220.7432430.8365020.871592
LightGBM0.9995200.9495800.7635140.8464420.881722


4.9 ROC 커브 결과

1
draw_roc_curve(models, model_names, X_test, y_test)

  • 이것도 큰 변화는 없음


5. Boxplot


5.1 Boxplot

  • 갑자기 중간에 Boxplot을 넣은 이유는 데이터의 Outlier를 이야기하기 위해서
  • 위의 데이터에서 Scale을 진행하고도 큰 변화가 없기에 outlier를 제거를 해보려고함
  • Boxplot이 Outlier를 보기 쉽다
  • 물론 Outlier라고 판단하는건 그외의 다른 도메인 지식의 영향이 있을수 있음


5.2 Boxplot의 구성

  • 최소값 : 제 1사분위에서 1.5 IQR1을 뺀 위치이다.
  • 제 1사분위(Q1) : 25%의 위치를 의미한다.
  • 제 2사분위(Q2) : 50%의 위치로 중앙값(median)을 의미한다.
  • 제 3사분위(Q3) : 75%의 위치를 의미한다.
  • 최대값 : 제 3사분위에서 1.5 IQR을 더한 위치이다.
  • 최소값과 최대값을 넘어가는 위치에 있는 값을 이상치(Outlier)라고 부른다.


5.3 간단한 데이터로 실습

1
2
3
samples = [1, 7, 9, 16, 36, 39, 45, 45, 46, 48, 51, 100, 101]
tmp_y = [1] * len(samples)
tmp_y
1
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
1
2
3
4
plt.figure(figsize=(12,4))
plt.scatter(samples, tmp_y)
plt.grid()
plt.show()

5.4 해당 위치의 지표 찾기

1
2
3
4
5
6
7
print(f'1사 분위 : {np.percentile(samples, 25)}')
print(f'2사 분위 : {np.median(samples)}')
print(f'3사 분위 : {np.percentile(samples, 75)}')
print(f'IQR : {np.percentile(samples, 75) - np.percentile(samples, 25)}')
print(f'1.5 IQR : {1.5 * (np.percentile(samples, 75) - np.percentile(samples, 25))}')
print(f'최소값 : {np.percentile(samples, 25) - (1.5 * (np.percentile(samples, 75) - np.percentile(samples, 25)))}')
print(f'최대값 : {np.percentile(samples, 75) + (1.5 * (np.percentile(samples, 75) - np.percentile(samples, 25)))}')
1
2
3
4
5
6
7
1사 분위 : 16.0
2사 분위 : 45.0
3사 분위 : 48.0
IQR : 32.0
1.5 IQR : 48.0
최소값 : -32.0
최대값 : 96.0
  • 1사분위 : np.percentile(samples, 25)
  • 2사분위 : np.median(samples)
  • 3사분위 : np.percentile(samples, 75)
  • iqr : np.percentile(samples, 75) - np.percentile(samples, 25)
  • 1.5 iqr = (np.percentile(samples, 75) - np.percentile(samples, 25)) * 1.5
  • 최소값 = np.percentile(samples, 25) - ((np.percentile(samples, 75) - np.percentile(samples, 25)) * 1.5)
  • 최대값 = np.percentile(samples, 75) + ((np.percentile(samples, 75) - np.percentile(samples, 25)) * 1.5)


5.5 실제로 그려보기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
q1 = np.percentile(samples, 25)
q2 = np.median(samples)
q3 = np.percentile(samples, 75)
upper_fence = q3 + iqr * 1.5
lower_fence = q1 - iqr * 1.5
plt.figure(figsize=(12,4))
plt.scatter(samples, tmp_y)
plt.axvline(x=q1, color = 'black')
plt.axvline(x=q2, color = 'red')
plt.axvline(x=q3, color = 'black')
plt.axvline(x=upper_fence, color = 'black', ls = 'dashed')
plt.axvline(x=lower_fence, color = 'black', ls = 'dashed')
plt.grid()
plt.show()

  • 위의 내용을 모두 적용하여 그려봄


5.6 Boxplot은 seabonr에 있음

1
2
3
4
5
import seaborn as sns
plt.figure(figsize=(12,4))
sns.boxplot(samples)
plt.grid()
plt.show()

6. Boxplot을 이용하여 Outlier를 정리해보기


6.1 Boxplot으로 그리기

1
2
3
4
5
import seaborn as sns

plt.figure(figsize=(10, 7))
sns.boxplot(data=raw_data[['V13','V14','V15']])
plt.show()

  • 딱히 해당 데이터에 대해 도메인지식은 없지만, V14 컬럼은 Outlier라고 할만큼 이상한 데이터가 많음


6.2 Outlier의 인덱스를 파악하는 함수 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
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


6.3 Outlier 찾기

1
get_outlier(df = raw_data, column='V14', weight=1.5)
1
Int64Index([8296, 8615, 9035, 9252], dtype='int64')


6.4 Outlier 제거

1
raw_data_copy.shape
1
(284807, 29)
1
2
3
outlier_index = get_outlier(df = raw_data, column= 'V14', weight= 1.5)
raw_data_copy.drop(outlier_index, axis =0, inplace=True)
raw_data_copy.shape
1
(284803, 29)
  • V14의 컬럼에 아웃라이어를 인덱스로 찾아서 삭제함


6.5 Outlier제거 한 뒤 데이터 다시 나누기

1
2
3
4
5
6
X = raw_data_copy

raw_data.drop(outlier_index, axis =0, inplace =True)
y = raw_data.iloc[:,-1]

X_train, X_test, y_train, y_test = train_test_split(X,y, test_size = 0.3, random_state = 13, stratify = y)
  • 삭제한 아웃라이어를 기준으로 데이터를 나누었음


6.6 Outlier 제거 후 성능 평가

1
2
3
4
5
6
7
8
9
10
11
12
import time

models = [lr_clf, dt_clf, rf_clf, lgbm_clf]

model_names = ['LinearReg', 'DecisionTree', 'RandomForest', 'LightGBM']

start_time = time.time()

results = get_result_pd(models, model_names, X_train, y_train, X_test, y_test)

print('Fit time : ', time.time() - start_time)
results
1
Fit time :  25.215320110321045
accuracyprecisionrecallf1roc_acu
LinearReg0.9992860.9047620.6506850.7569720.825284
DecisionTree0.9994270.8702290.7808220.8231050.890311
RandomForest0.9994970.9186990.7739730.8401490.886928
LightGBM0.9996020.9516130.8082190.8740740.904074
  • ROC 커브로 봐봐야 겠음


6.7 ROC커브

1
draw_roc_curve(models, model_names, X_test, y_test)

  • Outlier를 제거했는데도 별 차이는 없어보임


7. SMOTE Oversampling


7.1 SMOTE Oversampling

  • 사실 위의 데이터는 조금 문제가 있는게, 전체 데이터에서 y의 1값이 0.17%라는것이다
  • 그 뜻은 그냥 모든 y값을 0으로 예측해도 틀린값은 0.17% 밖에 안된다는 이야기
  • 그래서 위의 모든 데이터가 Acc가 높게 나온것
  • 해당 문제를 잡기 위해 Oversampling이 필요함


7.2 Undersampling vs Oversampling

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


7.3 설치

  • pip install imbalanced-learn


7.4 SMOTE 적용하기

1
2
3
4
from imblearn.over_sampling import SMOTE

smote = SMOTE(random_state=13)
X_train_over, y_train_over = smote.fit_sample(X_train, y_train)
  • 단, 데이터 증강은 train 데이터에서만 한다

7.5 데이터 증강의 효과

1
2
print(f'증강 전 : X_train : {X_train.shape}, y_train : {y_train.shape}')
print(f'증강 후 : X_train : {X_train_over.shape}, y_train : {y_train_over.shape}')
1
2
증강 전 : X_train : (199362, 29), y_train : (199362,)
증강 후 : X_train : (398040, 29), y_train : (398040,)
1
2
print(np.unique(y_train, return_counts= True))
print(np.unique(y_train_over, return_counts= True))
1
2
(array([0, 1]), array([199020,    342]))
(array([0, 1]), array([199020, 199020]))
  • 증강 전의 전체 데이터는 199362였고, 증강 후에 398040으로 늘어남
  • 증강 전의 y_train의 1값은 342개 으나, 증강 후의 1값은 199020개로 대폭 늘어나게 됨


7.6 Oversampling 후의 성능확인

1
2
3
4
5
6
7
8
9
10
11
12
import time

models = [lr_clf, dt_clf, rf_clf, lgbm_clf]

model_names = ['LinearReg', 'DecisionTree', 'RandomForest', 'LightGBM']

start_time = time.time()

results = get_result_pd(models, model_names, X_train_over, y_train_over, X_test, y_test)

print('Fit time : ', time.time() - start_time)
results
1
Fit time :  50.29139733314514
accuracyprecisionrecallf1roc_acu
LinearReg0.9756090.0595450.8972600.1116790.936502
DecisionTree0.9689840.0460480.8698630.0874660.919509
RandomForest0.9995320.8732390.8493150.8611110.924552
LightGBM0.9995320.8732390.8493150.8611110.924552
  • 그전과 비교하여 recall이 좋아진것을 확인할수 있음


7.7 ROC 커브

1
draw_roc_curve(models, model_names, X_test, y_test)
- 그전과 비교해서 ROC커브도 많이 좋아졌음
### 7.8 결론 - 데이터의 불균형이 있을땐 Oversampling도 하나의 방안이 될수있음 - Outlier는 수치적으로 이상한 애들이긴 하지만, 도메인지식으로 인해 제거를 안할수도 있음
This post is licensed under CC BY 4.0 by the author.

HAR 데이터로 해보는 GBM, XGBoost, LightGBM

아이리스 데이터와 와인데이터로 해보는 PCA

Comments powered by Disqus.