안 쓰던 블로그
kaggle - Santander Customer Satisfaction 예측 본문
문제
www.kaggle.com/c/santander-customer-satisfaction
고객 만족도는 기업에게 중요한 수치이다. 불만족스러운 고객이 있다면 그런 불만족한 점을 해결하도록 노력해야 한다.
산탄데르 은행은 불만족스러운 고객을 초기에 식별할 수 있도록 도와달라고 요청했다. 너무 늦기 전에 고객의 불만족을 개선하기 위한 사전 조치를 취할 수 있도록 해야 한다.
수백 개의 익명 피처가 주어질 때 고객이 은행 업무 경험에 만족하는지 또는 불만족하는지 예측한다.
성능 평가는 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을 받았다
근데 다시 직접 해 보라하면 못 할 것 같아서 나중에 또 복습해야겠다
'머신러닝 > 머신러닝' 카테고리의 다른 글
이진 분류: 케라스로 영화 리뷰 분류하기 (0) | 2021.04.04 |
---|---|
kaggle - Predict survival on the Titanic with DecisionTreeClassifier (0) | 2021.02.08 |
XGBoost과 LightGBM 개요 (0) | 2021.02.07 |
부스팅 알고리즘 (0) | 2021.02.07 |
배깅, 랜덤 포레스트 (0) | 2021.02.07 |