안 쓰던 블로그

kaggle - Santander Customer Satisfaction 예측 본문

머신러닝/머신러닝

kaggle - Santander Customer Satisfaction 예측

proqk 2021. 2. 8. 16:12
반응형

문제

www.kaggle.com/c/santander-customer-satisfaction

 

Santander Customer Satisfaction

Which customers are happy customers?

www.kaggle.com

고객 만족도는 기업에게 중요한 수치이다. 불만족스러운 고객이 있다면 그런 불만족한 점을 해결하도록 노력해야 한다.

산탄데르 은행은 불만족스러운 고객을 초기에 식별할 수 있도록 도와달라고 요청했다. 너무 늦기 전에 고객의 불만족을 개선하기 위한 사전 조치를 취할 수 있도록 해야 한다.

수백 개의 익명 피처가 주어질 때 고객이 은행 업무 경험에 만족하는지 또는 불만족하는지 예측한다.

성능 평가는 area under the ROC curve 로 한다.


풀이-xgboost이용

import numpy as np 
import pandas as pd 
import matplotlib.pyplot as plt
import matplotlib

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

numpy, pandas, matplotlib 임포트

read_csv로 데이터를 가져와서 shape를 한 번 본다

맨 뒤에 TARGET값이 있는데 0은 만족, 1은 불만족이다

 

cust_df.info()

info로 데이터 정보를 본다

NULL값이 나와야 하는데 데이터가 너무 많아서 isNULL이 안 나오고 위같이 간략한 타입으로 표시된다

 

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

이진 분류에서는 분류할 값인 TARGET값이 불균형한지를 먼저 확인해야 한다

데이터 프레임의 TARGET을 value_counts로 센다

불만족 데이터 건수를 unsatisfied_cnt 변수에 넣고 전체 데이터 건수로 나눠서 평균을 구한다

불만족이 4%로 굉장히 많다

 

cust_df.describe()

각 피처들의 분포도를 확인한다

데이터가 너무 많지만 특이한 점이 보인다

var3의 min값이 -999999.. 같은 게 나오니 이게 NULL값일 가능성이 높다(나머지는 2.000인데 비해)

 

print(cust_df['var3'].value_counts( )[:10])

var3의 값들의 분포를 10개만 출력해 본다

대부분이 2값인데 -999999가 약 100개가 있다

다른 값들이랑 너무 큰 차이가 나서 에러날 수도 있으니 가장 많은 값인 2값으로 변경해 준다

 

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

replace로 2로 바꿔주고 ID는 이제 필요없으니까 drop한다

iloc로 feature와 label 세트를 분리한다

feature는 : 모든 행의 :-1 첫 번째부터 마지막까지로, label은 -1 맨 마지막이니까 TARGET값이다

각각 76020, 369개의 칼럼이 있다

 

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)

학습 세트와 테스트 세트를 split으로 나누는데, 분포가 비슷한 수준으로 유지되어야 하니까 비율을 본다

잘 쪼개졌는지 shape를 출력해 본다

학습 세트는 0이 96%, 1이 3%정도

테스트 세트는 0이 95%, 1이 4%정도

비슷한 비율을 하고 있음을 확인할 수 있다

 

X_train, X_test, y_train, y_test = train_test_split(X_features, y_labels,
                                                    test_size=0.2, random_state=0, stratify=y_labels)

더 정확하게 하려면 stratify를 주어서 알아서 분포를 맞춰주게 한다

근데 참고한 강의(머신러닝 완벽 가이드)의 강사는 이걸 쓰지 않는 편이라고 한다

 

from xgboost import XGBClassifier
from sklearn.metrics import roc_auc_score

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

# 성능 평가 지표를 auc로, 조기 중단 파라미터는 100으로 설정하고 학습 수행. 
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))

XGBClassifier로 학습을 한다

하이퍼 파라미터 n_estimators는 500, random_state는 156로 설정한다. 랜덤을 설정하는 이유는 동일한 예측 결과를 위해 고정시킨다

성능 평가를 문제에서 auc로 한다고 했으니까 eval_metric="auc"로 해 준다

eval_set으ㅡ로 학습과 테스트 세트를 다 넣었는데 보통은 검증할 때 이렇게 하면 안 되지만, 데이터 세트의 1인 값이 4%밖에 되지 않기 때문에 테스트 세트로 검증을 했다 

이후 roc_auc를 평가 지표로 수행한다

평가는 0.8413이 나왔다

 

from sklearn.model_selection import GridSearchCV

# 하이퍼 파라미터 테스트의 수행 속도를 향상시키기 위해 n_estimators를 100으로 감소
xgb_clf = XGBClassifier(n_estimators=100)

params = {'max_depth':[5, 7] , 'min_child_weight':[1,3] ,'colsample_bytree':[0.5, 0.75] }

# 하이퍼 파라미터 테스트의 수행속도를 향상 시키기 위해 cv 를 지정하지 않음. 
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로 하이퍼 파라미터를 한다

이게 개인PC에서는 시간이 오래 걸려서 클라우드에서 하는 편이다. 일단은 값을 작게 해서 돌려본다

작은 값인 n_estimators=100 정도 잡는다

파라미터를 설정한다. max_depth는 트리의 최대 높이이다

min_child_weight는 관측치에 대한 가중치의 합의 최소(GBM의 min_samples_leaf와 비슷한데 GBM에선 관측치 수에 대한 최소임). 가중치에 따라서 자식을 만들거냐 말거냐를 결정

colsample_bytree는 트리를 만들 때 필요한 컬럼을 샘플링한다는 의미(GBM의 max_features와 비슷)이다. 컬럼이 많아서 컬럼을 몇 개만 뽑아서 감소시키면 더 빠를 것이라 생각하여 넣는다

아래 early_stopping_rounds도 30으로 작게 잡는다

cv를 지정하지 않아도 파라미터가 저렇게 있으니 2x2x2해서 8번 수행된다

최적 파라미터는 0.5, 5, 1일 때 0.8434로 나왔다(강의랑 좀 다르게 나오긴 했다..)

이 값을 보면 앞에서 했던 값인 0.8413랑 크게 다르지 않다

하이퍼 파라미터를 튜닝해도 엄청난 변화가 있지 않다. 그런 변화를 원하면 튜닝보단 차라리 피처 엔지니어링(이상치 없애기, 추가 컬럼을 믹스해서 만들기 등등)을 하는 게 좋다

 

# n_estimators는 1000으로 증가시키고, learning_rate=0.02로 감소, reg_alpha=0.03으로 추가함. 
xgb_clf = XGBClassifier(n_estimators=1000, random_state=156, learning_rate=0.02, max_depth=5,\
                        min_child_weight=1, colsample_bytree=0.5, reg_alpha=0.03)

# evaluation metric을 auc로, early stopping은 200 으로 설정하고 학습 수행. 
xgb_clf.fit(X_train, y_train, early_stopping_rounds=200, 
            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))

일단 하이퍼 파라미터가 나왔으니 적용해서 다시 학습한다

0.8457이 나왔다

 

# n_estimators는 1000으로 증가시키고, learning_rate=0.02로 감소, reg_alpha=0.03으로 추가함. 
xgb_clf = XGBClassifier(n_estimators=1000, random_state=156, learning_rate=0.02, max_depth=7,\
                        min_child_weight=1, colsample_bytree=0.75, reg_alpha=0.03)

# evaluation metric을 auc로, early stopping은 200 으로 설정하고 학습 수행. 
xgb_clf.fit(X_train, y_train, early_stopping_rounds=200, 
            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))

혹시 몰라 max_dapth=7, colsample_bytree는 0.75로 학습해 본다

아주 미세한 차이지만 아까보다 약간 작게 나왔다

 

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)

피처 중요도를 한 번 출력해 보았다

y축에 컬럼명, x축에 F score중요도가 나왔다

컬럼명이 나오는 이유는 넘파이가 아니라 판다스 데이터 프레임으로 했기 때문이다


강의에서 가이드해준 부분은 일단 여기까지, 그런데 실제로 캐글에 돌릴 때는 잘 되지 않아서 몇 가지 수정하여 제출했다

중간에 var3의 이상값을 제외한 부분 때문에 데이터 개수가 맞지 않아서 마지막 결과출력에서 문제가 생긴 듯 해서 그 부분을 주석처리 했다(근데 다른 사람들 해답을 보니까 이렇게들 했던데 why..)

그리고 직접 for문을 돌면서 1) 표준편차가 0인 열, 2) 중복되는 열 을 제거했다(drop)

물론 drop은 train과 test에 아래처럼 둘 다 했다

cust_train.drop(remove, axis=1, inplace=True)
df_test.drop(remove, axis=1, inplace=True)

그리고 학습에서 강사가 이거 원래 이렇게 하는 거 아닙니다 했던 부분을 정석(?)대로 돌렸다

xgb_clf.fit(X_train, y_train, early_stopping_rounds=100,
            eval_metric="auc", eval_set=[(X_test, y_test)])

ID는 그대로 drop해 주는데 그냥 드랍이 아니라 따로 저장해서, ID와 예측한 것을 데이터 프레임으로 묶어서 결과파일 생성, 제출했다

 

스코어는 0.83977을 받았다

근데 다시 직접 해 보라하면 못 할 것 같아서 나중에 또 복습해야겠다

반응형
Comments