본문 바로가기
Python/Sklearn

[Python]Santander Customer Satisfaction by XGBoost

by 씩2 2022. 4. 7.

XGBoost를 이용하여 kaggle의 데이터셋인 산탄데르 은행의 만족도를 바탕으로 예측을 진행해 보도록 하겠습니다.

이용되는 패키지는 다음과 같습니다.

 

클래스 테이블명은 TARGET이고, 이 값이 1을 가지면 불만족을 가진 고객, 0을가지면 만족한 고객으로 예측이 진행 됩니다.

모델의 성능평가는 대부분 만족인 고객일 것이고, 소수만 불만족일 것 이기 때문에 ROC-AUC커브를 이용하여 성능 평가를 진행 하도록 하겠습니다.

 

cust_df = pd.read_csv("/Users/sik/Desktop/santander/train_santander.csv",encoding='latin-1')
print('dataset shape:', cust_df.shape)
cust_df.head(3)
cust_df.info()

 

데이터셋을 로드하고, 데이터를 살펴보도록 하겠습니다.

dataset shape: (76020, 371)
cust_df.head(3)
   ID  var3  var15  ...  saldo_medio_var44_ult3     var38  TARGET
0   1     2     23  ...                     0.0  39205.17       0
1   3     2     34  ...                     0.0  49278.03       0
2   4     2     23  ...                     0.0  67333.77       0
[3 rows x 371 columns]
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

TARGET을 포함하여 총 371개의 피쳐가 존재합니다.

111개의 피쳐가 float형으로 이루어져있고, 260개의 피쳐는 정수형으로 이루어져 있는 것을 확인 할 수 있습니다.

print(cust_df['TARGET'].value_counts())
unsatisfied_cnt = cust_df[cust_df['TARGET'] == 1]['TARGET'].count()
total_cnt = cust_df['TARGET'].count()
print('불만족 고객 비율은 {0:.2f}'.format((unsatisfied_cnt / total_cnt)))

TARGET의 값을 분리하여 불만족한 고객의 비율이 얼마나 되는지 확인해 보도록 하겠습니다.

TARGET의 값이 1인경우 불만족한 고객이기 때문에 unsatisfied_cnt  변수에 카운트하여 저장하도록 하겠습니다.

나머지는 전체 개수에서 불만족한 고객의 수를 나눠 비율을 확인하도록 하겠습니다.

불만족 고객 비율은 0.04

불만족 고객비율은 4%입니다.

 

print(cust_df['var3'].value_counts( )[:10])
 2         74165
 8           138
-999999      116
 9           110
 3           108
 1           105
 13           98
 7            97
 4            86
 12           85
Name: var3, dtype: int64

var3 칼럼의 경우 다음과 같은 분포를 보입니다.

숫자형 데이터이기때문에 더많은 가중치가 적용될 수 있는 여지가 있어서 가장 많은 값은 2로 변환하도록 하겠습니다.

cust_df['var3'].replace(-999999, 2, inplace=True)
cust_df.drop('ID',axis=1 , inplace=True)
X_features = cust_df.iloc[:, :-1]
y_labels = cust_df.iloc[:, -1]
print('피처 데이터 shape:{0}'.format(X_features.shape))

 

다음은 Train셋과 Test셋을 분리 해보도록 하겠습니다.

 

X_train, X_test, y_train, y_test = train_test_split(X_features, y_labels,
                                                    test_size=0.2, random_state=3)
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)
 학습 세트 레이블 값 분포 비율
print(y_train.value_counts()/train_cnt)
0    0.960701
1    0.039299
Name: TARGET, dtype: float64
print('\n 테스트 세트 레이블 값 분포 비율')
 테스트 세트 레이블 값 분포 비율
print(y_test.value_counts()/test_cnt)
0    0.959353
1    0.040647
Name: TARGET, dtype: float64

 

Train셋과 Test셋이 잘 분리 된 것을 확인 할 수 있습니다.

 

이제 XGBoost를 이용하여 예측 해보도록 하겠습니다.

원래의 경우 평가데이터 세트를 트레이닝세트에서 분리한 테스트 셋을 이용하게 되면 과적합이날 우려가있습니다.

따라서 분리되어있는 테스트 데이터를 이용해야 하지만 실습의 시간이 과하게 소요되는 경향이 있어서 부득이 진행하게되었습니다.

 

xgb_clf = XGBClassifier(n_estimators=500, random_state=3)
xgb_clf.fit(X_train, y_train, early_stopping_rounds=100,
            eval_metric="auc", eval_set=[(X_train, y_train), (X_test, y_test)])

xgb_roc_score = roc_auc_score(y_test, xgb_clf.predict_proba(X_test)[:,1],average='macro')
print('ROC AUC: {0:.4f}'.format(xgb_roc_score))

estimators는 500으로 설정하였습니다. 조기중단은 100으로 설정된것도 확인 할수 있습니다.

예측도를 보겠습니다.

ROC AUC: 0.8428

ROC AUC의 예측은 84.28%가 나왔음을 확인 할 수 있습니다.

 

이제 조금더 예측을 잘하기위해서 하이퍼 파라미터 튜닝을 진행해 보도록 하겠습니다.

xgb_clf = XGBClassifier(n_estimators=100)
params = {'max_depth':[5, 7] , 'min_child_weight':[1,3] ,'colsample_bytree':[0.5, 0.75] }
gridcv = GridSearchCV(xgb_clf, param_grid=params)
gridcv.fit(X_train, y_train, early_stopping_rounds=30, eval_metric="auc",
           eval_set=[(X_train, y_train), (X_test, y_test)])
print('GridSearchCV 최적 파라미터:',gridcv.best_params_)
xgb_roc_score = roc_auc_score(y_test, gridcv.predict_proba(X_test)[:,1], average='macro')
print('ROC AUC: {0:.4f}'.format(xgb_roc_score))
GridSearchCV 최적 파라미터: {'colsample_bytree': 0.75, 'max_depth': 7, 'min_child_weight': 1}
ROC AUC: 0.8448

estimators를 더 많이 설정 할 수록 시간이 오래 걸림을 참고하시길 바랍니다.

84.48로 이전보다 약간 상향됬음을 알 수 있습니다.

 

이제 자주 사용된 features들을 확인해 보도록 하겠습니다.

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

var38 features가 4077회로 가장많이 사용됬음을 확인할수 있습니다.

이어서 LightGBM을 이용하여 학습하고 튜닝해보도록 하겠습니다

 

lgbm_clf = LGBMClassifier(n_estimators=500)
evals = [(X_test, y_test)]
lgbm_clf.fit(X_train, y_train, early_stopping_rounds=100, eval_metric="auc", eval_set=evals,
                verbose=True)
lgbm_roc_score = roc_auc_score(y_test, lgbm_clf.predict_proba(X_test)[:,1],average='macro')
print('ROC AUC: {0:.4f}'.format(lgbm_roc_score))
ROC AUC: 0.8396

위코드로 진행을 하였더니 83.96%의 예측도를 보여줍니다.

이제 하이퍼 파라미터 튜닝을통해 예측 정확도를 높여보도록 하겠습니다


LGBM_clf = LGBMClassifier(n_estimators=200)
params = {'num_leaves': [32, 64 ],
          'max_depth':[128, 160],
          'min_child_samples':[60, 100],
          'subsample':[0.8, 1]}
gridcv = GridSearchCV(lgbm_clf, param_grid=params)
gridcv.fit(X_train, y_train, early_stopping_rounds=30, eval_metric="auc",
           eval_set=[(X_train, y_train), (X_test, y_test)])
print('GridSearchCV 최적 파라미터:', gridcv.best_params_)
lgbm_roc_score = roc_auc_score(y_test, gridcv.predict_proba(X_test)[:,1], average='macro')
print('ROC AUC: {0:.4f}'.format(lgbm_roc_score))

GridSearchCV 최적 파라미터: {'max_depth': 128, 'min_child_samples': 100, 'num_leaves': 32, 'subsample': 0.8}
ROC AUC: 0.8442

약 0.5% 향상된 성능을 보여줬습니다.

XGBoost나 LightGBM모델의 경우는 성능이 뛰어나기때문에 하이퍼 파라미터 튜닝을 하는것보다 데이터의 전처리를 더 꼼꼼히하여 성능을 향상시키는 것이 훨씬 좋습니다.

이것으로 포스팅을 마무리 하도록 하겠습니다.