Hyun's Wonderwall

[AIchemist] 파머완 - CH04. 분류 | 09. 캐글 산탄데르 고객 만족 예측 본문

Study/Python-Machine-Learning

[AIchemist] 파머완 - CH04. 분류 | 09. 캐글 산탄데르 고객 만족 예측

Hyun_! 2023. 10. 31. 05:55

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

- XGBoost와 LightGBM을 활용해 예측해 보자.

- train.csv 는 370개의 피처로 주어진 데이터 세트. (피처 이름은 모두 익명 처리)

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

- ROC-AUC(ROC 곡선 영역)으로 모델의 성능을 평가함

(갑자기 아나콘다가 안 열려서 Jupyter Notebook (anaconda3) 을 실행시켰더니 잘 되었다(?))

- 캐글에서 train.csv 다운로드 https://www.kaggle.com/c/santander-customer-satisfaction 

 

데이터 전처리

- 사이킷런 래퍼 XGBoost를 이용할 것임

 

# 필요한 모듈 로딩, 학습 데이터를 DataFrame으로 로딩

클래스 값 칼럼을 포함한 피처가 371개 존재.

 

피처의 타입과 Null 값 알아보기

111개의 피처가 float, 260개의 피처가 int.(모든 피처의 데이터 타입이 숫자형) Null 값은 없음.

 

전체 데이터에서 만족과 불만족 비율 살펴보기

- 레이블인 Target 속성 값의 분포를 알아보면 된다.

대부분이 만족, 불만족인 고객은 4%에 불과함.

 

# DataFrame의 describe() 메서드로 각 피처의 값 분포 확인

➔ var3칼럼의 min값이 -999999: NaN이나 특정 예외 값을 이로 변환했을 것임. print(cust_df.var3.value_counts()[:10])으로 var3의 값 조사해보면 이 값이 116개...

 

#  너무 편차가 심하므로, 가장 값이 많은 2로 변환함

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

#  클래스 데이터 세트와 피처 데이터 세트를 분리해 별도의 데이터 세트로 별도 저장

 

# 원본 데이터 세트에서 학습 데이터 세트와 테스트 데이터 세트 분리

- 원본이 비대칭적인 데이터 세트이므로 TARGET 값 분포도가 학습 데이터 세트와 테스트 데이터 세트에 모두 비슷하게 추출됐는지 확인함

- 학습(X_)과 성능 평가(y_) 진행할 것임

➔ TARGET 값 분포 원본 데이터와 유사하게 전체 4% 정도로 만들어짐

 

XGBoost의 조기 중단(기능)의 검증 데이터 세트로 사용할 것임

- X_train, y_train을 쪼개어 학습과 검증 데이터 세트로 만듬

 

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

XGBoost 학습모델 생성하고 예측 결과를 ROC-AUC로 평가해보자

 

# 사이킷런 래퍼 XGBClassifier를 기반으로 학습 수행

- (파라미터) n_estimators: 500, early_stopping_rounds: 100, eval_metric: auc, eval_set=[(X_tr, y_tr), (X_val, y_val)], 조기 중단은 100회로 설정

 

# HyperOpt 이용해 베이지안 최적화 기반으로 XGBoost의 하이퍼 파라미터 튜닝 수행

- max_depth, min_child_weight, colsample_bytree, learning_rate 조정해 하이퍼 파라미터 검색 공간 설정

# 목적 함수 만들기

- 3 Fold 교차 검증. 평균 ROC-AUC 값 반환하되 -1 곱해 최대 ROC-AUC 값이 최소 반환값이 되도록 함

- KFold 클래스를 이용해 학습과 검증 데이터 세트 추출, 교차 검증 횟수만큼 학습과 성능 평가 진행

(*수행 시간 줄이기 위해 임시로 estimators 100으로 줄이고, early_stopping_rounds도 30으로 줄여서 테스트. 하이퍼 파라미터 튜닝 완료 후 증가시킬 것)

# 최적의 하이퍼 파라미터 도출하기

- fmin() 함수 호출해 max_eval=50회만큼 반복

➔ 50회만큼 교차 검증 반복하여 학습과 평가 (약 10분 소요)
     colsamplbe_bytree: 약 0.5749, learning_rate: 약 0.1514, max_depth: 5.0, min_child_rate: 6.0 도출됨

 

# 이렇게 도출된 최적 하이퍼 파라미터를 기반으로 XGBClassifier를 재학습시키고 테스트 데이트 세트에서 ROC-AUC를 측정하기

n_estimators 500으로 증가시킴

# 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))
ROC AUC: 0.8457

개선되었다!

 

# 튜닝된 모델에서 각 피처의 중요도를 피처 중요도 그래프로 확인하기

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)

https://github.com/wikibook/pymlrev2/blob/main/4%EC%9E%A5/4.9_%20%EB%B6%84%EB%A5%98%EC%8B%A4%EC%8A%B5_%EC%82%B0%ED%83%84%EB%8D%B0%EB%A5%B4_%EA%B3%A0%EA%B0%9D%EB%A7%8C%EC%A1%B1%EC%98%88%EC%B8%A1.ipynb

➔ XGBoost의 예측 성능을 좌우하는 가장 중요한 피처는 var38, var15순임.

 

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

LightGBM으로 학습을 수행하고 ROC-AUC를 측정해 보자.

- (파라미터) n_estimators: 500, early_stopping_rounds: 100, eval_metric: auc, eval_set=[(X_tr, y_tr), (X_val, y_val)]

걸리는 시간 단축, 성능 조금 낮다..?

 

# HyperOpt 이용해 다양한 하이퍼 파라미터 튜닝 수행하기

- 튜닝 대상: num_leaves, max_depth, min_child_smaples, subsmaple, learning_rate, 이를 위한 하이퍼 파라미터 검색 공간 설정

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

#목적 함수 생성

- 앞 코드와 LGBMClassifier 객체 생성 부분만 차이

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'])
    # 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 학습 수행. 
        lgbm_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, lgbm_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)

# fmin 호출해 최적의 파라미터 도출

from hyperopt import fmin, tpe, Trials

trials = Trials()

# fmin()함수를 호출. max_evals지정된 횟수만큼 반복 후 목적함수의 최소값을 가지는 최적 입력값 추출. 
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)

learning_rate가 약 0.08592, max_depth가 121.0, min_child_samples가 69.0, num_leaves가 41.0, subsample이 0.91489로 도출됨.

 

# 이들 하이퍼 파라미터 이용해 LightGBM 학습후 데이터 세트에서 ROC-AUC 평가해보기

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))
ROC AUC: 0.8446

개선되었다! LightGBM은 학습 시간이 상대적으로 빠른 장점. 추가적인 하이퍼 파라미터를 적용해 수행해 보는 것도 좋다.