본문 바로가기

Euron/정리

[Week4] 04. 분류 - 캐글 실습

09. 캐글 산탄데르 고객 만족 예측 

 

 

데이터 전처리

 

캐글 산탄데르 고객 만족 데이터 세트를 이용해 고객 만족 여부를 XGBoost와 LightGBM을 활용해 예측한다.

클래스 레이블 명은 RARGET이며, 이 값이 1이면 불만을 가진 고객, 0이면 만족한 고객이다.

이러한 데이터의 대부분은 만족이르모, 정확도 수치보다는 ROC-AUC로 성능을 평가하는 것이 좋다.

cust_df = pd.read_csv(r"C:\Users\jain5\Desktop\Euron\Data_Handling\train.csv", encoding='latin-1')
print('dataset shape:', cust_df.shape)
cust_df.head(3)
# 클래스 레이블 명 TARGET, 1이면 불만족, 0이면 만족한 고객

 encoding='latin-1' 은 인코딩 옵션인데, 주어진 글자가 깨지지 않게 데이터의 인코딩 방식을 알아낸 후 적절하게 옵션을 넣어준다.

cust_df.info()

 

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 76020 entries, 0 to 76019
Columns: 371 entries, ID to TARGET
dtypes: float64(111), int64(260)
memory usage: 215.2 MB

클래스 값을 포함한 피처(열)가 371개 존재하며, 그중 111개는 float형, 260개는 int형이다.

또한 Null값은 없다. 

 

만족과 불만족의 비율을 살펴보기 위해 Target 속성의 값의 분포를 알아본다.

print(cust_df['TARGET'].value_counts())
unsatisfied_cnt = cust_df[cust_df['TARGET'] == 1].TARGET.count()
total_cnt = cust_df.TARGET.count()
print('unsatisfied 비율은 {0:.2f}'.format((unsatisfied_cnt / total_cnt)))
# 불만족인 고객 4% -> ROC-AUC로 성능 평가
0    73012
1     3008
Name: TARGET, dtype: int64
unsatisfied 비율은 0.04

 

DataFrame의 describe() 메서드를 이용해 각 피처의 값 분포를 확인한다.

cust_df.describe( )

var3 주목

var3 칼럼의 min 값이 -9999..이다. 이런 값의 경우 Null값이나 특정 예외 값을 -999999로 변환한 것이다.

이 값은 편차가 심하므로 가장 값이 많은 2로 변환한다.

또한 ID는 단순 식별자이므로 드롭한다.

# var3 피처 값 대체 및 ID 피처 드롭
cust_df['var3'].replace(-999999, 2, inplace=True)
cust_df.drop('ID', axis=1, inplace=True)

# 피처 세트와 레이블 세트분리. 레이블 컬럼은 DataFrame의 맨 마지막에 위치해 컬럼 위치 -1로 분리
X_features = cust_df.iloc[:, :-1]
y_labels = cust_df.iloc[:, -1]
print('피처 데이터 shape:{0}'.format(X_features.shape))

이제 학습과 성능 평가를 위해 원본 데이터 세트에서 학습 데이터 세트와 테스트 데이터 세트를 분리한다.

비대칭한 데이터 세트의 경우, 클래스인 Target값의 분포도가 비슷한지 확인하는 것이 좋다.

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(X_features, y_labels,
                                                    test_size=0.2, random_state=0)
train_cnt = y_train.count()
test_cnt = y_test.count()
print('학습 세트 Shape:{0}, 테스트 세트 Shape:{1}'.format(X_train.shape , X_test.shape))

print(' 학습 세트 레이블 값 분포 비율')
print(y_train.value_counts()/train_cnt)
print('\n 테스트 세트 레이블 값 분포 비율')
print(y_test.value_counts()/test_cnt)
# Target 값의 분포도가 학습 및 테스트 데이터 세트에 비슷하게 추출됐는지 확인(결과 생략)

 

 

XGBoost 모델 학습과 하이퍼 파라미터 튜닝

 

사이킷런 래퍼인 XGBClassifier를 기반으로 학습을 수행한다.

n_estimators(사용할 트리 개수)는 500, early_stopping_rounds(조기 중단 조건)는 100으로 한다.

또한 XGBClassifier의 eval_metric(성능 평가 기준)은 'auc'로 한다.

# X_train, y_train을 다시 학습과 검증 데이터 세트로 분리. (조기중단)
X_tr, X_val, y_tr, y_val = train_test_split(X_train, y_train,
                                                    test_size=0.3, random_state=0)
                                                    
from xgboost import XGBClassifier
from sklearn.metrics import roc_auc_score

# n_estimators는 500으로, learning_rate 0.05, random state는 예제 수행 시마다 동일 예측 결과를 위해 설정. 
xgb_clf = XGBClassifier(n_estimators=500, learning_rate=0.05, random_state=156)

# 성능 평가 지표를 auc로, 조기 중단 파라미터는 100으로 설정하고 학습 수행. 
xgb_clf.fit(X_tr, y_tr, early_stopping_rounds=100, eval_metric='auc', eval_set=[(X_tr, y_tr), (X_val, y_val)])

xgb_roc_score = roc_auc_score(y_test, xgb_clf.predict_proba(X_test)[:, 1])
print('ROC AUC: {0:.4f}'.format(xgb_roc_score))
# 꽤 오래 걸렸다.
[234]	validation_0-auc:0.91414	validation_1-auc:0.83307
[235]	validation_0-auc:0.91415	validation_1-auc:0.83305
ROC AUC: 0.8429

auc 성능 평가는 sklearn에 내장된 roc_auc_score()를 이용한다.

여기서 predict_proba가 사용되었는데, 개별 레이블 별 예측값이 아닌 예측 확률을 반환받는다. 확률이 큰 레이블 값으로 예측하기(까먹지 않기).

튜닝없이 테스트 데이터 세트로 예측 시 ROC AUC 값은 약 0.8429가 나왔다.

 

이제 하이퍼 파라미터 튜닝을 위해 HyperOpt를 이용해 베이지안 최적화 기반으로 수행을 하자.

먼저 검색 공간 설정!

from hyperopt import hp

# max_depth는 5에서 15까지 1간격으로, min_child_weight는 1에서 6까지 1간격으로
# colsample_bytree는 0.5에서 0.95사이, learning_rate는 0.01에서 0.2사이 정규 분포된 값으로 검색. 

xgb_search_space = {'max_depth': hp.quniform('max_depth', 5, 15, 1), 
                    'min_child_weight': hp.quniform('min_child_weight', 1, 6, 1),
                    'colsample_bytree': hp.uniform('colsample_bytree', 0.5, 0.95),
                    'learning_rate': hp.uniform('learning_rate', 0.01, 0.2)
}

정수형 파라미터는 quniform(), bytree는 uniform() 사용하기..

 

다음은 목적함수 설정!

from sklearn.model_selection import KFold
from sklearn.metrics import roc_auc_score

# 목적 함수 설정. 
# 추후 fmin()에서 입력된 search_space값으로 XGBClassifier 교차 검증 학습 후 -1* roc_auc 평균 값을 반환.  
def objective_func(search_space):
    xgb_clf = XGBClassifier(n_estimators=100, max_depth=int(search_space['max_depth']),
                            min_child_weight=int(search_space['min_child_weight']),
                            colsample_bytree=search_space['colsample_bytree'],
                            learning_rate=search_space['learning_rate']
                           )
    # 3개 k-fold 방식으로 평가된 roc_auc 지표를 담는 list
    roc_auc_list= []
    
    # 3개 k-fold방식 적용 
    kf = KFold(n_splits=3)
    # X_train을 다시 학습과 검증용 데이터로 분리
    for tr_index, val_index in kf.split(X_train):
        # kf.split(X_train)으로 추출된 학습과 검증 index값으로 학습과 검증 데이터 세트 분리 
        X_tr, y_tr = X_train.iloc[tr_index], y_train.iloc[tr_index]
        X_val, y_val = X_train.iloc[val_index], y_train.iloc[val_index]
        # early stopping은 30회로 설정하고 추출된 학습과 검증 데이터로 XGBClassifier 학습 수행. 
        xgb_clf.fit(X_tr, y_tr, early_stopping_rounds=30, eval_metric='auc',
                   eval_set=[(X_tr, y_tr), (X_val, y_val)])
    
        # 1로 예측한 확률값 추출후 roc auc 계산하고 평균 roc auc 계산을 위해 list에 결과값 담음. 
        score = roc_auc_score(y_val, xgb_clf.predict_proba(X_val)[:, 1])
        roc_auc_list.append(score)
        
    # 3개 k-fold로 계산된 roc_auc값의 평균값을 반환하되, 
    # HyperOpt는 목적함수의 최소값을 위한 입력값을 찾으므로 -1을 곱한 뒤 반환. 
    return -1 * np.mean(roc_auc_list)

3 Fold 교차 검증을 이용해 평균 ROC-AUC 값을 반환하되, -1을 곱해 최대 ROC-AUC 값이 최소 반환값이 되게 한다.

KFOLD 클래스를 이용해 학습과 검증 데이터 세트를 추출하고 이를 교차 검증 횟수(3)만큼 학습과 성능 평가를 수행한다.

 

이제 최적의 하이퍼 파라미터를 도출한다.

from hyperopt import fmin, tpe, Trials

trials = Trials()

# fmin()함수를 호출. max_evals지정된 횟수만큼 반복 후 목적함수의 최소값을 가지는 최적 입력값 추출.
best = fmin(fn=objective_func,
            space=xgb_search_space,
            algo=tpe.suggest,
            max_evals=50, # 최대 반복 횟수를 지정합니다.
            trials=trials, rstate=np.random.default_rng(seed=30))

print('best:', best)
[84]	validation_0-auc:0.92659	validation_1-auc:0.83251                                                                 
[85]	validation_0-auc:0.92671	validation_1-auc:0.83250                                                                 
100%|█████████████████████████████████████████████| 50/50 [1:10:42<00:00, 84.85s/trial, best loss: -0.8378699584549635]
best: {'colsample_bytree': 0.5802594350237742, 'learning_rate': 0.15309606486156122, 'max_depth': 5.0, 'min_child_weight': 5.0}

잊었을까봐...fmin()은 목적 함수의 반환 값이 최소가 될 수 있는 최적의 입력 값을 찾아주는 HyperOpt가 제공하는 함수

fmin(fn=목적함수, space=검색공간, algo=tpe.suggest(베이지안 최적화 적용 알고리즘), max_evals=최대 반복 횟수, trials=시도한 입력값 및 입력 결과 저장, rstate=랜덤 시드 값)

 

best 값으로 알아낸 최적 하이퍼 파라미터 값을 기반으로 XGBClassifier를 재학습 시키고, ROC AUC를 측정한다.

# n_estimators를 500증가 후 최적으로 찾은 하이퍼 파라미터를 기반으로 학습과 예측 수행.
xgb_clf = XGBClassifier(n_estimators=500, learning_rate=round(best['learning_rate'], 5),
                        max_depth=int(best['max_depth']), min_child_weight=int(best['min_child_weight']), 
                        colsample_bytree=round(best['colsample_bytree'], 5)   
                       )

# evaluation metric을 auc로, early stopping은 100 으로 설정하고 학습 수행. 
xgb_clf.fit(X_tr, y_tr, early_stopping_rounds=100, 
            eval_metric="auc",eval_set=[(X_tr, y_tr), (X_val, y_val)])

xgb_roc_score = roc_auc_score(y_test, xgb_clf.predict_proba(X_test)[:,1])
print('ROC AUC: {0:.4f}'.format(xgb_roc_score))
[179]	validation_0-auc:0.91062	validation_1-auc:0.83327
[180]	validation_0-auc:0.91071	validation_1-auc:0.83330
ROC AUC: 0.8460

아까의 0.8429보다는 아주 약간 상승한 결과를 볼 수 있다.

 

추가로 각 피처의 중요도를 막대 그래프를 이용해 알아본다.

from xgboost import plot_importance
import matplotlib.pyplot as plt
%matplotlib inline

fig, ax = plt.subplots(1,1,figsize=(10,8))
plot_importance(xgb_clf, ax=ax , max_num_features=20,height=0.4)

plot_importance

var38, var15 피처가 중요함을 알 수 있다.

 

 

LightGBM 모델 학습과 하이퍼 파라미터 튜닝

 

XGBoost의 단점인 수행 시간을 줄이기 위해 LightGBM을 이용해본다.

XGBoost와 같이 하이퍼 파라미터 튜닝도 진행한다.

lgbm_search_space = {'num_leaves': hp.quniform('num_leaves', 32, 64, 1),
                     'max_depth': hp.quniform('max_depth', 100, 160, 1),
                     'min_child_samples': hp.quniform('min_child_samples', 60, 100, 1),
                     'subsample': hp.uniform('subsample', 0.7, 1),
                     'learning_rate': hp.uniform('learning_rate', 0.01, 0.2)
                    }

검색 공간에 subsmaple이 추가되고 LGBMClassifier 객체를 생성하는 부분만 달라졌다.

fmin()으로 얻은 최적 파라미터 값을 이용해 학습/예측을 진행한다.

lgbm_clf =  LGBMClassifier(n_estimators=500, num_leaves=int(best['num_leaves']),
                           max_depth=int(best['max_depth']),
                           min_child_samples=int(best['min_child_samples']), 
                           subsample=round(best['subsample'], 5),
                           learning_rate=round(best['learning_rate'], 5)
                          )

# evaluation metric을 auc로, early stopping은 100 으로 설정하고 학습 수행. 
lgbm_clf.fit(X_tr, y_tr, early_stopping_rounds=100, 
            eval_metric="auc",eval_set=[(X_tr, y_tr), (X_val, y_val)])

lgbm_roc_score = roc_auc_score(y_test, lgbm_clf.predict_proba(X_test)[:,1])
print('ROC AUC: {0:.4f}'.format(lgbm_roc_score))
[139]	training's auc: 0.947514	training's binary_logloss: 0.0931953	valid_1's auc: 0.824876	valid_1's binary_logloss: 0.138568
[140]	training's auc: 0.947793	training's binary_logloss: 0.0930837	valid_1's auc: 0.824702	valid_1's binary_logloss: 0.138638
[141]	training's auc: 0.947875	training's binary_logloss: 0.0929829	valid_1's auc: 0.824448	valid_1's binary_logloss: 0.138739
ROC AUC: 0.8446

성능은 아주 약간 떨어졌지만 수행시간은 훨씬 빠르다.

 


10. 캐글 신용카드 사기 검출

 

card_df['Class'].value_counts()
0    284315
1       492
Name: Class, dtype: int64

해당 데이터 세트의 레이블인 Class 속성은 매우 불균형한 분포를 가진다. 0은 정상적인 신용카드 트랜잭션 데이터, 1은 신용카드 사기 트랜잭션 데이터이다.

이러한 사기 검출(Fraud Detection)이나 이상 검출(Anomaly Detection)과 같은 데이터 세트는 레이블 값이 극도로 불균형한데, 이를 그대로 이용하면 예측 성능에 문제가 생길 수 있다.

 

언더 샘플링과 오버 샘플링의 이해

 

다양한 유형을 학습할 수 있도록 적절한 학습 데이터를 확보하는 방안으로 오버 샘플링(Oversampling)언더 샘플링(Undersampling) 방법이 있다.(상대적으로 오버 샘플링이 예측에 유리)

  • 언더 샘플링 : 많은 데이터 세트를 적은 데이터 세트 수준으로 감소시키는 방식. 과도하게 정상 레이블로 학습/예측되는 부작용을 개선할 수 있지만, 정상 레이블 데이터가 적어 제대로 된 학습을 수행할 수 없는 문제가 발생할 수 있다.
  • 오버 샘플링 : 적은 데이터 세트를 증식하여 학습을 위한 충분한 데이터를 확보하는 방식. 과적합 방지를 위해 원본 데이터의 피처 값들을 아주 약간만 변경하여 증식한다. 
    • SMOTE : 대표적인 오버 샘플링 방법. 적은 데이터 세트에 있는 개별 데이터들의 K 최근접 이웃을 찾아서 이 데이터와 K개 이웃들의 차이를 일정 값으로 만들어서 기존 데이터와 약간 차이가 나는 새로운 데이터들을 생성한다.

SMOTE 구현 파이썬 패키지인 imbalanced_learn을 설치한다.

 

데이터 일차 가공 및 모델 학습/예측/평가

 

다양한 데이터 사전 가공 수행을 위해 DataFrame을 복사해 가공한 뒤 반환하는 함수와 가공 후 분리된 학습/테스트 데이터 세트를 반환하는 함수를 생성한다.

from sklearn.model_selection import train_test_split

# 인자로 입력받은 DataFrame을 복사 한 뒤 Time 컬럼만 삭제하고 복사된 DataFrame 반환
def get_preprocessed_df(df=None):
    df_copy = df.copy()
    df_copy.drop('Time', axis=1, inplace=True) # Time 피쳐 삭제
    return df_copy
    
# 사전 데이터 가공 후 학습과 테스트 데이터 세트를 반환하는 함수.
def get_train_test_dataset(df=None):
    # 인자로 입력된 DataFrame의 사전 데이터 가공이 완료된 복사 DataFrame 반환
    df_copy = get_preprocessed_df(df)
    # DataFrame의 맨 마지막 컬럼이 레이블, 나머지는 피처들
    X_features = df_copy.iloc[:, :-1]
    y_target = df_copy.iloc[:, -1]
    # train_test_split( )으로 학습과 테스트 데이터 분할. stratify=y_target으로 Stratified 기반 분할
    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

X_train, X_test, y_train, y_test = get_train_test_dataset(card_df)

 

로지스틱 회귀와 LightGBM 기반의 모델로 학습 및 예측을 수행한다.

데이터 가공을 통해 예측 성능의 변화 또한 알아본다.

# 3장의 예측 성능 평가 함수
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score, f1_score
from sklearn.metrics import roc_auc_score

def get_clf_eval(y_test, pred=None, pred_proba=None):
    confusion = confusion_matrix( y_test, pred)
    accuracy = accuracy_score(y_test , pred)
    precision = precision_score(y_test , pred)
    recall = recall_score(y_test , pred)
    f1 = f1_score(y_test,pred)
    # ROC-AUC 추가 
    roc_auc = roc_auc_score(y_test, pred_proba)
    print('오차 행렬')
    print(confusion)
    # ROC-AUC print 추가
    print('정확도: {0:.4f}, 정밀도: {1:.4f}, 재현율: {2:.4f},\
    F1: {3:.4f}, AUC:{4:.4f}'.format(accuracy, precision, recall, f1, roc_auc))

 

반복적으로 학습/예측/평가를 수행하므로 이를 한번에 하는 함수를 생성한다.

# 인자로 사이킷런의 Estimator객체와, 학습/테스트 데이터 세트를 입력 받아서 학습/예측/평가 수행.
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)

 

참고로 LGBMClassifier 객체 생성시 극도로 불균형한 레이블 값 분포도를 가지고 있으므로 boost_from_average=False로 파라미터를 설정한다.

boost_from_average

가중치 update마다 레이블의 평균 값으로 score를 조정한다...

무슨 소린지는 모르겠는데 불균형한 레이블 분포에서 default인 True 설정은 재현률 및 ROC-AUC 성능을 매우 크게 저하시킨다고 한다.

 

데이터 분포도 변환 후 모델 학습/예측/평가

 

중요 피처 값의 분포도를 살펴본다.

import seaborn as sns

plt.figure(figsize=(8, 4))
plt.xticks(range(0, 30000, 1000), rotation=60)
sns.histplot(card_df['Amount'], bins=100, kde=True)
plt.show()

Amount 피처의 분포도

중요한 속성으로 보이는 Amount 피처를 살펴본 결과이다.

이 Amount를 표준 정규 분포 형태로 변환한 뒤 로지스틱 회귀의 예측 성능을 측정한다. 정규 분포 형태 변환은 사이킷런의 StandardScaler 클래스를 이용한다.

from sklearn.preprocessing import StandardScaler
# 사이킷런의 StandardScaler를 이용하여 정규분포 형태로 Amount 피처값 변환하는 로직으로 수정. 
def get_preprocessed_df(df=None):
    df_copy = df.copy()
    scaler = StandardScaler()
    amount_n = scaler.fit_transform(df_copy['Amount'].values.reshape(-1, 1))
    # 변환된 Amount를 Amount_Scaled로 피처명 변경후 DataFrame맨 앞 컬럼으로 입력
    df_copy.insert(0, 'Amount_Scaled', amount_n)
    # 기존 Time, Amount 피처 삭제
    df_copy.drop(['Time','Amount'], axis=1, inplace=True)
    return df_copy

(결과는 생략)

정규 분포 형태로 변환 후, 로지스틱 회귀의 경우는 정밀도와 재현율이 조금 저하되었고, LightGBM의 경우는 약간 정밀도와 재현율이 저하되었지만 큰 성능상의 변경은 없다.

 

다음으로는 StandardSclaer가 아닌 로그 변환으로 값을 수정한다.

로그 변환은 데이터 분포도가 심하게 왜곡되어 있을 경우 적용하는 기법 중에 하나이다. 로그 변환은 넘파이의 log1p() 함수를 이용해 변환이 가능하다.

def get_preprocessed_df(df=None):
    df_copy = df.copy()
    # 넘파이의 log1p( )를 이용하여 Amount를 로그 변환 
    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

로지스틱 회위의 경우 원본 데이터 대비 정밀도는 향상되었지만 재현율은 저하되었다. 

LightGBM의 경우 재현율이 향상되었다.

위 수행 결과로 레이블이 극도로 불균일한 데이터 세트에서 로지스틱 회귀는 데이터 변환 시 약간은 불안정한 성능 결과를 보여주고 있음을 알 수 있다.

 

이상치 데이터 제거 후 모델 학습/예측/평가

 

이상치 데이터(Outlier)는 전체 데이터의 패턴에서 벗어난 이상 값을 가진 데이터이다.

이상치를 찾는 여러 방법 중, 사분위(Quantile) 값의 편차를 이용하는 IQR을 이용한다.

사분위란, 전체 데이터를 값이 높은 순으로 정렬하고 25%씩으로 구간을 분할하는 것이다. 이들 중 25%구간인 Q1~에서 75% 구간인 Q3의 범위를 IQR이라 한다.

사분위와 IQR

이 IQR에 1.5를 곱해서 생성된 범위를 벗어난 데이터를 이상치로 간주한다.(1.5 이외에 다른 값도 적용 가능)

 

이상치 데이터를 IQR을 제거할 때는, 결정값과 상관성이 높은 피처들을 위주로 이상치를 검출한다.

DataFrame의 corr()을 이용해 각 피처별로 상관도를 구한 뒤 seaborn의 heatmap을 통해 시각화 한다.

import seaborn as sns

plt.figure(figsize=(9, 9))
corr = card_df.corr()
sns.heatmap(corr, cmap='RdBu')

cmap='RdBu'로 설정해 양의 상관관계가 높을수록 색깔이 진한 파란색에 가까워지고, 음의 상관관계가 높을수록 색이 진한 빨간색에 가까워진다.

여기서 음의 상관관계가 높은 V14와 V17 피처에 대해서 이상치를 찾아 제거한다.

IQR을 이용해 이상치를 검출한 후 삭제하는 함수를 생성한다.

import numpy as np

def get_outlier(df=None, column=None, weight=1.5):
    # fraud에 해당하는 column 데이터만 추출, 1/4 분위와 3/4 분위 지점을 np.percentile로 구함. 
    fraud = df[df['Class']==1][column]
    quantile_25 = np.percentile(fraud.values, 25)
    quantile_75 = np.percentile(fraud.values, 75)
    # IQR을 구하고, IQR에 1.5를 곱하여 최대값과 최소값 지점 구함. 
    iqr = quantile_75 - quantile_25
    iqr_weight = iqr * weight
    lowest_val = quantile_25 - iqr_weight
    highest_val = quantile_75 + iqr_weight
    # 최대값 보다 크거나, 최소값 보다 작은 값을 아웃라이어로 설정하고 DataFrame index 반환. 
    outlier_index = fraud[(fraud < lowest_val) | (fraud > highest_val)].index
    return outlier_index

넘파이의 percentile()을 이용해 분위를 구한 후, 1.5를 곱해 범위를 지정하여 이상치를 검출한다.

outlier_index = get_outlier(df=card_df, column='V14', weight=1.5)
print('이상치 데이터 인덱스:', outlier_index)
이상치 데이터 인덱스: Int64Index([8296, 8615, 9035, 9252], dtype='int64')

 

이제 위 get_processed_df()를 로그 변환 후 이상치 데이터를 삭제하는 함수로 변경 후 다시 알고리즘 모델을 적용한다.

# get_processed_df( )를 로그 변환 후 V14 피처의 이상치 데이터를 삭제하는 로직으로 변경. 
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)
    # 이상치 데이터 삭제하는 로직 추가
    outlier_index = get_outlier(df=df_copy, column='V14', weight=1.5)
    df_copy.drop(outlier_index, axis=0, inplace=True)
    return df_copy

X_train, X_test, y_train, y_test = get_train_test_dataset(card_df)
print('### 로지스틱 회귀 예측 성능 ###')
get_model_train_eval(lr_clf, ftr_train=X_train, ftr_test=X_test, tgt_train=y_train, tgt_test=y_test)
print('### LightGBM 예측 성능 ###')
get_model_train_eval(lgbm_clf, ftr_train=X_train, ftr_test=X_test, tgt_train=y_train, tgt_test=y_test)
### 로지스틱 회귀 예측 성능 ###
오차 행렬
[[85281    14]
 [   48    98]]
정확도: 0.9993, 정밀도: 0.8750, 재현율: 0.6712,    F1: 0.7597, AUC:0.9743
### LightGBM 예측 성능 ###
오차 행렬
[[85290     5]
 [   25   121]]
정확도: 0.9996, 정밀도: 0.9603, 재현율: 0.8288,    F1: 0.8897, AUC:0.9780

이전 결과는 생략했지만...모두 예측 성능이 크게 향상되었다.

 

SMOTE 오버 샘플링 적용 후 모델 학습/예측/평가

 

SMOTE를 적용할 때는 반드시 학습 데이터 세트만 오버 샘플링을 해야 한다. 올바른 검증/테스트를 수행해야 하므로 원본 데이터 세트에서는 적용하면 안된다.

 

앞 예제의 학습 피처/레이블 데이터에 SMOTE 객체의 fit_resample() 메서드를 이용해 데이터를 증식한다.

from imblearn.over_sampling import SMOTE

smote = SMOTE(random_state=0)
X_train_over, y_train_over = smote.fit_resample(X_train, y_train)
print('SMOTE 적용 전 학습용 피처/레이블 데이터 세트: ', X_train.shape, y_train.shape)
print('SMOTE 적용 후 학습용 피처/레이블 데이터 세트: ', X_train_over.shape, y_train_over.shape)
print('SMOTE 적용 후 레이블 값 분포: \n', pd.Series(y_train_over).value_counts())
SMOTE 적용 전 학습용 피처/레이블 데이터 세트:  (199362, 29) (199362,)
SMOTE 적용 후 학습용 피처/레이블 데이터 세트:  (398040, 29) (398040,)
SMOTE 적용 후 레이블 값 분포: 
 0    199020
1    199020
Name: Class, dtype: int64

 

일반적으로 SMOTE를 적용하면 재현율은 높아지나 정밀도는 낮아진다.

따라서 정밀도 지표보다는 재현율 지표를 높이는 것이 머신러닝 모델의 주요한 목표인 경우에 SMOTE를 적용한다.

데이터 가공 결과

 


# 회고

 

더 다양한 데이터 전처리 방법을 알아봤다.

전체적인 프로세스는 비슷한데, 데이터 세트를 분석하는 능력이 훨씬 중요한 것 같다.

AI 연구쪽이 아니라면 생각보다 수학적 능력이 안중요한거같음. 아직 안배워서 그런가..

 

 

데이터 과학 및 캐글 입문자를 위한 캐글 필사 알아보기 (modulabs.co.kr)

 

데이터 과학 및 캐글 입문자를 위한 캐글 필사 알아보기

데이터 과학에 입문하기 전 캐글 필사에 대해 알아보고 필요한 것들이 무엇이 있는지 알아보는 시간을 가져봅시다. 캐글은 무엇일까요? 필사는 왜 해야하며 이것을 통해 얻을 수 있는 것들은 어

modulabs.co.kr

동아리에서 실습 전에 읽어보라고 한 사이트.

시간 될때마다 캐글 필사를 해야겠다. 

'Euron > 정리' 카테고리의 다른 글

[Week5] 05. 회귀(2)  (1) 2023.10.08
[Week5] 05. 회귀(1)  (1) 2023.10.07
[Week3] 04. 분류(2)  (0) 2023.09.21
[Week2] 04. 분류(1)  (0) 2023.09.17
[Week1] 03. 평가  (0) 2023.09.12