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

데이터 세트에서 feature는 370개로 구성되며, 클래스 레이블 명은 TARGET이다. 

TARGET=1 → 불만 고객

TARGET=0 → 만족 고객

 

이 데이터 세트는 불균형 데이터 세트이므로 모델의 성능 평가는 정확도가 아닌 roc_auc가 더 적합하다.


1. Data load and EDA

●필요한 모듈 및 패키지 불러오기

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib
import warnings
warnings.filterwarnings('ignore')

●데이터 로드

cust_df = pd.read_csv(r"C:\Users\82102\PerfectGuide\4장\train_santander.csv", encoding='latin-1')
cust_df.head()
cust_df.shape

cust_df.info()

cust_df.isnull().sum()

 

sum(cust_df.isnull().sum()==1)

  • 370개의 feature, 1개의 class 컬럼으로 구성
  • 111개의 float형 feature, 260개의 int형 feature로 모든 feature가 숫자형
  • 결측값은 없음

●클래스 비율 확인

cust_df['TARGET'].value_counts()

print('만족 비율:', cust_df['TARGET'].value_counts()[0] / cust_df.shape[0])
print('불만족 비율:', cust_df['TARGET'].value_counts()[1] / cust_df.shape[0])

  • 대부분이 만족이며 불만족인 고객은 약 4%에 불과 → 불균형 데이터 세트

●각 피처의 분포 확인

cust_df.describe()

var3 컬럼의 경우 min 값이 -999999이다. 이 값은 NaN이나 특정 예외 값을 -999999로 변환한 것으로 보인다.

2. Preprocessing

●위에서 문제가 있던 var3 컬럼의 값 분포 확인

cust_df['var3'].value_counts()

-999999 값이 116개가 존재하고 다른 값에 비해서 편차도 심함 → 가장 값이 많은 2로 변환하기로 결정

●var3 컬럼 -999999 값을 가장 값이 많은 2로 변환

cust_df['var3'].replace(-999999, 2, inplace=True)

●ID 피처는 단순 식별자에 불과하므로 드롭

cust_df.drop('ID', axis=1, inplace=True)

●피처와 클래스 분리

X_features = cust_df.iloc[:, :-1]
y_labels = cust_df.iloc[:, -1]

●학습 데이터 세트와 테스트 데이터 세트로 분리(불균형 데이터 세트이므로 stratify를 이용해 학습 데이터 세트와 테스트 데이터 세트의 클래스 비율을 원본 데이터의 클래스 비율과 유사하게 맞춰주기로 결정)

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, stratify=y_labels, random_state=0)
# 학습 데이터, 테스트 데이터 target 값 분포 확인
print('학습 데이터 세트 target 값 분포')
print(y_train.value_counts() / y_train.shape[0])
print('\n')
print('테스트 데이터 세트 target 값 분포')
print(y_test.value_counts() / y_test.shape[0])

  • 학습과 테스트 데이터 세트 target의 분포가 비슷하게 추출됐고, 이 분포는 원본 데이터와 유사하게 추출됨

● XGBoost, LightGBM의 early stopping을 사용하기 위해 X_train, y_train을 다시 학습과 검증 데이터 세트로 분리

(마찬가지로 stratify를 이용하기로 결정)

# X_train, y_train을 다시 학습과 검증 데이터 세트로 분리
X_tr, X_val, y_tr, y_val = train_test_split(X_train, y_train, test_size=0.3, stratify=y_train, random_state=0)

3. XGBoost Model

●대략적인 모델의 성능을 확인해보자

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)

# 성능 평가 기준이 roc_auc이므로 eval_metric은 '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))

●HyperOpt를 이용해 XGBoost의 하이퍼 파라미터 튜닝

# 1. 검색 공간 설정
from hyperopt import hp

xgb_search_space = {'max_depth':hp.quniform('max_depth', 5, 15, 1), # 정수형 하이퍼 파라미터 → hp.quniform() 사용
                   'min_child_weight':hp.quniform('min_child_weight', 1, 6, 1), # 정수형 하이퍼 파라미터 → hp.quniform() 사용
                   'colsample_bytree':hp.uniform('colsample_bytree', 0.5, 0.95),
                   'learning_rate':hp.uniform('learning_rate', 0.01, 0.2)}
  • 교차검증 시 XGBoost, LightGBM의 조기 중단(early stopping)과 검증 데이터 성능 평가를 위해서 KFold를 이용해 직접 학습과 검증 데이터 세트를 추출하고 이를 교차검증 횟수만큼 학습과 성능 평가를 수행한다.(이전 글에서 말했듯이, XGBoost와 LightGBM은 교차검증 시 cross_val_score()를 사용하면 조기 중단이 지원되지 않음)
  • 수행 시간을 줄이기 위해 n_estimators는 100으로 줄이고, early_stopping_rounds도 30으로 줄여서 테스트한 뒤 나중에 하이퍼 파라미터 튜닝이 완료되면 다시 증가시켜 모델 학습 및 평가 진행하기로 함(수행 시간 때문에)
# 2. 목적 함수 설정
from sklearn.model_selection import KFold # 교차검증
from sklearn.metrics import roc_auc_score
## from hyperopt import STATUS_OK

def objective_func(search_space):
    xgb_clf = XGBClassifier(n_estimators=100,
                            max_depth=int(search_space['max_depth']), # 정수형 하이퍼 파라미터 형변환 필요:int형
                            min_child_weight=int(search_space['min_child_weight']), # 정수형 하이퍼 파라미터 형변환 필요:int형
                            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 값의 평균값을 반환하되 -1을 곱함
    return -1 * np.mean(roc_auc_list) # return {'loss':-1 * np.mean(roc_auc_list), 'status':STATUS_OK}
# 3. fmin()을 이용해 최적 하이퍼 파라미터 도출
from hyperopt import fmin, tpe, Trials

trials = Trials()

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)

추출된 최적 하이퍼 파라미터를 이용하여 XGBoost의 인자로 입력하여 학습 및 평가

  • n_estimators=500, early_stopping_rounds=100으로 증가시켜서 학습 및 평가 진행
# 도출된 최적 하이퍼 파라미터를 이용하여 모델 선언
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)
                       )
# 모델 학습
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))

●피처 중요도

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

fig, ax = plt.subplots(figsize=(10,8))
plot_importance(xgb_clf, ax=ax, max_num_features=20, height=0.4) # 상위 20개 피처

4. LightGBM Model

●대략적인 모델의 성능을 확인해보자

from lightgbm import LGBMClassifier

lgbm_clf = LGBMClassifier(n_estimators=500)

eval_set = [(X_tr, y_tr), (X_val, y_val)]
lgbm_clf.fit(X_tr, y_tr, early_stopping_rounds=100, eval_metric='auc', eval_set=eval_set)

lgbm_roc_score = roc_auc_score(y_test, lgbm_clf.predict_proba(X_test)[:, 1])
print('ROC AUC:{0:.4f}'.format(lgbm_roc_score))

●HyperOpt를 이용해 LightGBM의 하이퍼 파라미터 튜닝

# 1. 검색 공간 설정
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)
                    }
# 2. 목적 함수 설정
def objective_func(search_space):
    lgbm_clf = LGBMClassifier(n_estimators=100,
                             num_leaves=int(search_space['num_leaves']),
                              max_depth=int(search_space['max_depth']),
                              min_child_samples=int(search_space['min_child_samples']),
                              subsample=search_space['subsample'],
                              learning_rate=search_space['learning_rate'])
    
    roc_auc_list=[]
    
    kf = KFold(n_splits=3)
    for tr_index, val_index in kf.split(X_train):
        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]
        
        lgbm_clf.fit(X_tr, y_tr, early_stopping_rounds=30, eval_metric='auc',
                    eval_set=[(X_tr, y_tr), (X_val, y_val)])
        
        score = roc_auc_score(y_val, lgbm_clf.predict_proba(X_val)[:, 1])
        roc_auc_list.append(score)
        
    return -1 * np.mean(roc_auc_list)
# 3. fmin()을 이용해 최적 하이퍼 파라미터 도출
from hyperopt import fmin, tpe, Trials

trials = Trials()

best = fmin(fn=objective_func,
           space=lgbm_search_space,
           algo=tpe.suggest,
           max_evals=50,
           trials=trials,
           # rstate=np.random.default_rng(seed=30)
           )

print('best:', best)

추출된 최적 하이퍼 파라미터를 이용하여 LightGBM의 인자로 입력하여 학습 및 평가

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)
                         )

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))

 

+ Recent posts