codestates / ds-blog

blog sharing for data science bootcamp course
2 stars 4 forks source link

[이재우] Kaggle 타이타닉 데이터 생존여부 예측 Challenge_세번째 이야기 #209

Open jingwoo4710 opened 3 years ago

jingwoo4710 commented 3 years ago

첫번째, 그리고 두번째 이야기( #168, #192 )

캐글에서의 타이타닉 데이터를 통해 생존 여부를 예측하고 있다. 자세한 내용은 첫번째, 두번째 이야기를 참조하면 도움이 될 듯하다. 두번째 이야기에서의 결론은 나는 왜 0.7의 벽을 넘지 못하는것인가? 내가 분석한 이유는 다음과 같다.

일반적으로 지금까지 경험을 토대로 말하면, RandomForest model보다 Boosting계열 모델이 좀 더 좋은 성능을 보여줬다. 과연 이 타이타닉 데이터에서도 동일하게 적용이 되는지 확인해보자.

Data

데이터 전처리 과정은 이미 첫번째, 두번째 이야기에서 깊이 다룬부분이다. 따라서, 첫번째 그리고 두번째 이야기에서 전처리 된 데이터를 바로 가져와서 사용할 것이다. 자세한 전처리 과정은 첫번째, 두번째 이야기에서 확인하면 좋을것 같다. 데이터 전처리가 끝나고, train 데이터에서 학습할 feature가 저장된(X_train)와 우리의 target(y_train)을 정의를 다음과 같이 하였다.

# X / y
target = 'Survived'
features = train.drop(columns=[target, 'PassengerId']).columns.tolist()

X_train = train[features]
y_train = train[target]
X_test = test[features]
X_train.head()
  Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked Family
3 Mr male 22.0 1 0 A/5 21171 7.2500 1 S 1
1 Mrs female 38.0 1 0 PC 17599 71.2833 1 C 1
3 Miss female 26.0 0 0 STON/O2. 3101282 7.9250 1 S 0
1 Mrs female 35.0 1 0 113803 53.1000 1 S 1
3 Mr male 35.0 0 0 373450 8.0500 1 S 0

위의 데이터프레임을 보게되면 새롭게 추가 또는 수정 된 feature들이 존재한다. Feature Engineering 과정에서 간단히 추가되거나 수정된 부분을 소개하면 다음과 같다.

Model

예측모델에 학습을 시키기 위해서는, NaN값과 범주형 데이터가 없어야 한다. 이전까지는, pipeline을 통해서 한 번에 해결하였지만, 이번 프로젝트에서는 다양한 모델을 활용하기 위해서, 미리 OrdinalEncoderKNNImputer를 통해서 data preprocessing을 해주었다.

from sklearn.pipeline import make_pipeline
from category_encoders import OrdinalEncoder
from sklearn.impute import SimpleImputer
from sklearn.impute import KNNImputer

# Encoding
enc = OrdinalEncoder()
X_train_enc = enc.fit_transform(X_train)
X_test_enc = enc.transform(X_test)

# Imputing
imp = KNNImputer()
X_train_imp = imp.fit_transform(X_train_enc)
X_test_imp = imp.transform(X_test_enc)

이번 프로젝트에서 사용한 Imputer는 KNNImputer이다. KNNImputer는 각 샘플의 결측치를 제외한 feature를 참고하여, 가장 비슷한 feature를 보유한 샘플을 5개 찾아 비교하여 결측치를 5개의 평균으로 대치한다. SimpleImputer를 통해서, 충분하다고 생각이 되지만 다양한 라이브러리를 사용하는것도 경험이라 생각하여 사용해보았다.

# Preprocessing result
X_train_imp
# output
array([[3., 1., 1., ..., 1., 1., 1.],
       [1., 2., 2., ..., 1., 2., 1.],
       [3., 3., 2., ..., 1., 1., 0.],
       ...,
       [3., 3., 2., ..., 1., 1., 3.],
       [1., 1., 1., ..., 1., 2., 0.],
       [3., 1., 1., ..., 1., 3., 0.]])

1. RandomForest

두번째 이야기에서 최적화 된 RandomForestClassifier 모델을 그대로 가져와서 Baseline 모델 역할을 하도록 한다.

from sklearn.pipeline import make_pipeline
from category_encoders import OrdinalEncoder
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import RandomizedSearchCV

rf = RandomForestClassifier(random_state = 1, n_jobs = -1)

random_grid = {'bootstrap': [True, False],
               'max_depth': [10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110],
               'max_features': ['auto', 'sqrt'],
               'min_samples_leaf': [1, 2, 4],
               'min_samples_split': [2, 5, 10],
               'n_estimators': [10, 25, 50, 100, 200]}

# Optimizer
clf = RandomizedSearchCV(
    rf,
    param_distributions=random_grid,
    n_iter=50, 
    cv=3, 
    scoring='f1',  
    verbose=50,
    n_jobs=-1
)

# Optimization result
clf.fit(X_train_imp, y_train)
```py
from sklearn.metrics import roc_auc_score, f1_score, classification_report, confusion_matrix
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score

#Random Forest
rf = clf.best_estimator_
rf.fit(X_train_imp, y_train)

# 예측
y_pred_rf = rf.predict(X_val_imp)
y_proba_rf = rf.predict_proba(X_val_imp)[:,1]

# Randomforest 결과
print('RandomForest AUC : ', roc_auc_score(y_val, y_proba_rf))
print('RandomForest f1_score : ', f1_score(y_val, y_pred_rf))

RandomForest AUC : 0.8938076416337285 RandomForest f1_score : 0.8 이번 프로젝트에서 베이스라인 모델이 될, RandomForest모델에서의 성능결과를 확인 할 수 있다.

2. GradientBoosting

from sklearn.ensemble import GradientBoostingClassifier
# 모델 선언
gb = GradientBoostingClassifier(max_depth=2, random_state=1)

# 학습
gb.fit(X_train_imp,y_train)

# 예측
y_pred_gb = gb.predict(X_val_imp)
y_proba_gb = gb.predict_proba(X_val_imp)[:,1]

# GradientBoosting 결과
print('GradientBoosting AUC : ', roc_auc_score(y_val, y_proba_gb))
print('GradientBoosting f1_score : ', f1_score(y_val, y_pred_gb))

GradientBoosting AUC : 0.8988142292490118 GradientBoosting f1_score : 0.8091603053435115

첫번째 Boosting 모델인 Gradient Boosting 모델이다. Loss function의 gradient를 최소하는것이 특징이다. 한눈에 보아도, 베이스라인 모델보다는 좋은 성능을 보여준다.

3. Xgb

from xgboost import XGBClassifier
# ratio
print(train.Survived.value_counts())

# 모델 선언
xgb = XGBClassifier (max_depth=12, subsample=0.7, scale_pos_weight = 342/549, n_jobs=-1)

# 학습
xgb.fit(X_train_imp,y_train, eval_set=[(X_train_imp, y_train)], early_stopping_rounds=20)

# 예측
y_pred_xgb = xgb.predict(X_val_imp)
y_proba_xgb = xgb.predict_proba(X_val_imp)[:,1]

# XgbBoosting 결과
print('XgbBoosting AUC : ', roc_auc_score(y_val, y_proba_xgb))
print('XgbBoosting f1_score : ', f1_score(y_val, y_pred_xgb)))

XgbBoosting AUC : 0.8826745718050065 XgbBoosting f1_score : 0.8028169014084506

2번째 모델인, XgBoosting 모델의 결과이다. 베이스라인 모델보다 다소 떨어지는 성능을 확인 할 수 있다.

4. Lgbm

from lightgbm import LGBMClassifier
# 모델
lgb = LGBMClassifier(is_unbalance=True, random_state=1, n_jobs=-1, objective='binary')

# 학습
lgb= lgb.fit(X_train_imp, y_train, eval_metric='f1',
             eval_set=[(X_train_imp, y_train), (X_val_imp, y_val)], verbose=50,
            early_stopping_rounds= 50)

# 예측
y_pred_lgb = lgb.predict(X_val_imp)
y_proba_lgb = lgb.predict_proba(X_val_imp)[:,1]

# LGBM 결과
print('LGBM AUC : ', roc_auc_score(y_val, y_proba_lgb))
print('LGBM f1_score : ', f1_score(y_val, y_pred_lgb))

LGBM AUC : 0.9006587615283268 LGBM f1_score : 0.8088235294117647

세번째 Boosting 모델인, LGBM Boosting 모델의 결과이다. 가장 좋은 AUC score를 보여준다!

5. Catboost

from catboost import CatBoostClassifier
# 범주형
cat_col = train.select_dtypes('object').columns

# 모델
cat = CatBoostClassifier(iterations= 500)

# 학습
cat = cat.fit(X_train, y_train, cat_features=cat_col, eval_set=[(X_train, y_train), (X_val, y_val)],early_stopping_rounds=70,verbose=50)

# 예측
y_pred_cat = cat.predict(X_val)
y_proba_cat = cat.predict_proba(X_val)[:,1]

# Catboost 결과
print('Catboost AUC : ', roc_auc_score(y_val, y_proba_cat))
print('Catboost f1_score : ', f1_score(y_val, y_pred_cat))

Catboost AUC : 0.8999999999999999 Catboost f1_score : 0.8030303030303031

마지막 Boosting model은 CatBoost 모델이다. Xgb와 LGBM 모델의 사이정도의 성능을 보여준다.

모델 비교

위의 모든 결과를 보다 쉽게 한눈에 이해하기 위해서 시각화를 해보았다.

1. AUC

# Define variable for plot
model_list = ['RandomForest', 'GradientBoosting', 'Xgb', 'Lgbm', 'Catboost']
AUC_list = [roc_auc_score(y_val, y_proba_rf)*100,
            roc_auc_score(y_val, y_proba_gb)*100,
            roc_auc_score(y_val, y_proba_xgb)*100,
            roc_auc_score(y_val, y_proba_lgb)*100,
            roc_auc_score(y_val, y_proba_cat)*100]

f1_list = [f1_score(y_val, y_pred_rf)*100*100,
            f1_score(y_val, y_pred_gb)*100*100,
            f1_score(y_val, y_pred_xgb)*100*100,
            f1_score(y_val, y_pred_lgb)*100,
            f1_score(y_val, y_pred_cat)*100]

# plot
plt.rcParams['figure.figsize']=20,8
sns.set_style('darkgrid')
ax = sns.barplot(x=model_list, y=AUC_list, palette = "husl", saturation =2.0)
plt.xlabel('Classifier Models', fontsize = 20 )
plt.ylabel('% of AUC', fontsize = 20)
plt.title('AUC score of different Classifier Models', fontsize = 20)
plt.xticks(fontsize = 12, horizontalalignment = 'center', rotation = 8)
plt.yticks(fontsize = 12)
for i in ax.patches:
    width, height = i.get_width(), i.get_height()
    x, y = i.get_xy() 
    ax.annotate(f'{round(height,2)}%', (x + width/2, y + height*1.02), ha='center', fontsize = 'x-large')
plt.show()

auc

위의 그래프는 x축은 예측에 사용한 모델명, y축은 AUC score를 percentage로 나타내었다. 첫번째 bar를 나타내는 RandomForest모델을 시작으로 지금까지 사용한 Boosting 모델의 결과를 나타내었다. 기대와 달리 오직 Xgboosting 모델이 베이스라인 모델보도 안 좋은 성능을 보여주었다. 가장 높은 AUC score를 보여주는 모델을 Catboost모델이고, 다음으로 Lgbm모델이다.

2. f1

plt.rcParams['figure.figsize']=20,8
sns.set_style('darkgrid')
ax = sns.barplot(x=model_list, y=f1_list, palette = "husl", saturation =2.0)
plt.xlabel('Classifier Models', fontsize = 20 )
plt.ylabel('% of f1', fontsize = 20)
plt.title('f1 score of different Classifier Models', fontsize = 20)
plt.xticks(fontsize = 12, horizontalalignment = 'center', rotation = 8)
plt.yticks(fontsize = 12)
for i in ax.patches:
    width, height = i.get_width(), i.get_height()
    x, y = i.get_xy() 
    ax.annotate(f'{round(height,2)}%', (x + width/2, y + height*1.02), ha='center', fontsize = 'x-large')
plt.show()

f1

f1_score를 통해서 모델을 비교한 바 그래프를 위에서 볼 수 있다. AUC score와 달리, 가장 높은 f1 score를 보여주는 모델은 LGBM이다. Imbalanced 데이터셋에서 주로 사용하는 평가지수를 f1 score를 많이 사용한다고 한다. 따라서, 두 그래프의 결과를 종합하여 AUC score는 Catboost 모델보다 조금 낮지만, 더 좋은 f1 score를 보여주는 LGBM을 최종 예측모델로 설정하자.

제출

스크린샷 2020-10-30 오후 5 09 20

역시 예상한대로, 성능의 상승을 확인 할 수 있었다.

결론

총 3번의 이야기를 진행하면서, 성능 개선의 주된 요인이 있다.

  1. 데이터의 알맞은 전처리 : 깔끔한 데이터는 깔끔한 성능으로 보답해준다.
  2. 알맞은 예측 모델 설정 : 가장 큰 성능 개선의 요인은 여러가지 모델을 학습해보고, 예측결과를 확인 해보는것 이었다.

다양한 모델에 적용할 수 있도록 하려면, 데이터 전처리가 올바르게 진행이 되어야 하고 다양한 모델에 적용시켜보면서 성능을 확인하고 최적의 모델을 선택하는것이 하나의 방법이지 않을까! 만약 타이타닉 데이터가아닌 다른 feature들이 너무 많은 경우에는 과연 어떻게 데이터 전처리를 보여줘야 하나라는 의문이 들면서 이 글을 마친다.

Maiven commented 3 years ago

재우님 안녕하세요, 글에 대한 코멘트 드립니다.

고생하셨습니다.