본문 바로가기

NA Project

Spaceship Titanic Kaggle Competition

2022.11.29 ~ 2022.11.30


프로젝트 개요

  • Stapceship Titanic 생존자 예측
  • 데이터 전처리 및 다양한 ML 알고리즘 적용
  • 알고리즘 파라미터 최적화 작업 및 정확도 개선

 

Team 3

 

김혜인

나정민

손동환

이민호

최근도


2번째 프로젝트

Spaceship Titanic 사라진 승객 예측이다.

 

데이터 전처리 및 알고리즘 선택 및 파라미터 튜닝까지 해보았다.

 

주어진 연습용 csv파일의 독립변수종속변수이다.

 


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

 

데이터 전처리와 시각화를 위한 

기본 import이다.

 


train_url = 'https://raw.githubusercontent.com/Ruinenhaft/Project_space_titanic/main/data/train.csv'
train_raw = pd.read_csv(train_url)
# test_raw = pd.read_csv('/content/drive/MyDrive/spaceship-titanic/test.csv')
train_raw.head(10)

pandas 라이브러리 read_csv를 이용하여

해당 데이터를 상위 10줄 출력해보았다.

 

 


✨ 데이터 확인 ✨

train_raw.info()

해당 데이터의 간략한 정보를 보았다.

 

총 8693의 데이터 중

결측치는 PassengerId, Transported를 제외하고 존재하고

데이터 타입연속형범주형이 섞여있는것을 확인 할 수 있다.

 

train_raw.describe()

연속형 데이터의 이상치를 확인하기위해

.describe()를 적용해 보았다.

 

 

최소,평균 값에 비해 유독 최대값이 높은것으로 보아

데이터에 이상치가 있다는 점을 확인할 수 있다.

 


train_raw.describe(include='O')

범주형 데이터는 어떨까?

.describe(include='O')로 확인해 보았다.

 

 

PassengerId의 경우 고유 번호 이므로

count와 unique가 같은것을 확인 할 수 있다.

 

HomePlanet, Destination의 경우3종류가 있으며 

 

CryoSleep, VIP는 해당 여부인 bull값이므로2종류인것을 확인할 수 있다. 


train_raw.isna().sum()

각 row별 결측치 개수를 sum()으로 확인해보면

 

위와 같다.

 

특이점은 나이에도 결측치가 있고이름에도 결측치가 있는데

 

두 컬럼의 결측치 개수가 다르다는 점이다.

 


✨ Data 시각화를 활용한 아이디어 도출 ✨

 

✔ Homeplanet

plt.figure(figsize=(15, 5))
plt.subplot(1, 3, 1)
plt.title('Number of passengers by HomePlanet')
value_counts = train_raw.HomePlanet.value_counts()
sns.barplot(x= value_counts.index, y =value_counts)

각 출신행성별 승객 수를 bar차트로 표현해

Earth출신의 승객이 많다는 점을 도출했다.

plt.subplot(1, 3, 2)
plt.title('Number of Transported passengers by HomePlanet')
value_counts1 = train_raw['HomePlanet'][(train_raw['Transported']== True)].value_counts()
sns.barplot(x= value_counts.index, y =value_counts1)

출신 행성별 사라진 승객 수 차트이다.

별 다른 이상한 점은 없다.

plt.subplot(1, 3, 3)
plt.title('HomePlanet - Transported ratio')
value_counts2 = train_raw.groupby(['HomePlanet'])['Transported'].mean()
sns.barplot(x= value_counts.index, y =value_counts2)
plt.tight_layout()
plt.show()

출신 행성별 사라진 승객수 평균이다.

Earth의 평균이 더 높을것 같았지만

Europa가 가장 높고 Mars의 평균이 그 다음으로 높았다.

 

이는 종속변수에 영향을 미칠것이라 판단했다.


 CryoSleep

sns.barplot(y='Transported', x='CryoSleep', data=train_raw)
plt.show()

냉동 수면 여부별 사라진 승객 수이다.

냉동 수면을 한 사람이 더 많이 사라진것을 확인할 수 있다.

 


 Destination

 

plt.figure(figsize=(15, 5))
plt.subplot(1, 3, 1)
plt.title('Number of passengers by Destination')
value_counts = train_raw.Destination.value_counts()
sns.barplot(x= value_counts.index, y =value_counts)

목적지 별 승객 수이다.

TRAPPIST-1e 행성이 가장 많은 것을 확인할 수 있다.

 

plt.subplot(1, 3, 2)
plt.title('Number of Transported passengers by Destination')
value_counts1 = train_raw['Destination'][( train_raw['Transported']== True)].value_counts()
sns.barplot(x= value_counts.index, y =value_counts1)

목적지별 사라진 승객수이다.

역시 가장 많은 승객이 있었던

TRAPPIST-1e 행성이 가장 많은 것을 확인할 수 있다. 

plt.subplot(1, 3, 3)
plt.title('Destination - Transported ratio')
value_counts2 =  train_raw.groupby(['Destination'])['Transported'].mean()
sns.barplot(x= value_counts.index, y =value_counts2)
plt.tight_layout()
plt.show()

목적지별 사라진 승객 수 평균이다.

역시 TRAPPIST-1e 행성이 가장 많은 것을 확인할 수 있다. 

 


✔ 편의시설 사용 여부

 

다음은 편의시설

('RoomService', 'FoodCourt', 'ShoppingMall', 'Spa', 'VRDeck')

사용 여부를 확인해보았다.

 

해당 아이디어는 상황상

'객실 외부에서 활동을 하고 있는 승객이 사라질 확률이 높지 않았을까?'

라는 생각에서 시도하게 되었다.

 

# RoomService, FoodCourt, ShoppingMall, Spa, VRDeck 컬럼 결측치에 0 입력
for feature in ['RoomService', 'FoodCourt', 'ShoppingMall', 'Spa', 'VRDeck'] :
    train_raw[feature].fillna(0, inplace=True)

우선 각각의 컬럼의 결측치0으로 채워 주었다.

 

# RoomService, FoodCourt, ShoppingMall, Spa, VRDeck컬럼에 값이 있으면 1 없으면 0인 컬럼 Spend 생성
train_raw['Spend'] = [0 if train_raw.iloc[i][['RoomService', 'FoodCourt', 'ShoppingMall',
	'Spa', 'VRDeck']].sum() == 0 else 1 for i in range(len(train_raw))]

마지막으로 'spend'라는 소비 여부를 뜻하는 컬럼을 만들기 위해

1,0으로 값을 치환해주는 코드를 적용해 주었다.

 

sns.barplot(y='Transported', x='Spend', data=train_raw)
plt.show()

이를 활용해 소비여부별 사라진 여부를 bar차트로 표현해 확인했다.

위를 통해 0(소비를 하지 않음)의 경우

더 많이 사라진것을 확인할 수 있다.

 

sns.barplot(y='Transported', x='VIP', data=train_raw)
plt.show()

추가적으로 VIP별 사라진 여부를 확인했다.

 


✔ 나이

 

plt.figure(figsize=(15, 5))
plt.subplot(1, 2, 1)
plt.title('Age Distribution - Transported : False')
sns.kdeplot(x="Age", data=train_raw[train_raw['Transported'] == False])
plt.subplot(1, 2, 2)
plt.title('Age Distribution - Transported : True')
sns.kdeplot(x='Age', data=train_raw[train_raw['Transported'] == True])
plt.show()

나이의 경우 연령대가 0세부터 79세까지 분포하기 때문에

10대, 20대와 같이 범위를 나누었다.

 

0~19세까지는 도착률이 더 높고,

20~39세는 도착률이 더 낮으며

40세 이후로는 도착률이 거의 동일한 것을 확인할 수 있다.

 


⛏ 훈련셋, 검증셋, 실험셋

# train/test/validation 셋 분리
from sklearn.model_selection import train_test_split

X = clean1.drop('transported', axis=1)
y = clean1['transported']

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=100)
X_tr, X_val, y_tr, y_val = train_test_split(X_train, y_train, test_size=0.2, random_state=100)

✨ Random Forest ✨

 

# Random Forest
from sklearn.ensemble import RandomForestClassifier

rfc_model1 = RandomForestClassifier(random_state = 100, max_depth = 10, n_estimators = 10)
rfc_model1.fit(X_train, y_train)
rfc_pred1 = rfc_model1.predict(X_test)

랜덤 포레스트 파라미터의 경우

 

random_state = 100

max_depth = 10

n_estimators = 10

 

설정하여 학습을 진행했다.

 


검증셋을 통한

정확도는 약 79%

ROC score는 약 0.88

 

accuracy_score(y_train, rfc_model1.predict(X_train))

최종 accuracy_score

84%로 과적합이 있다고 판단했다.

 


✨ LGBM ✨

from lightgbm import LGBMClassifier

lgbm_model1 = LGBMClassifier(n_estimators=1000, learning_rate=0.01)

evals = [(X_tr, y_tr), (X_val, y_val)]
lgbm_model1.fit(X_tr, y_tr, eval_set=evals, eval_metric='logloss', early_stopping_rounds=200, verbose=False)

lgbm_pred1 = lgbm_model1.predict(X_test)

부스팅 모델의 경우 첫 번째로

Lightgbm 라이브러리에 있는

LGBMClassifier 모델을 사용했다.

 

파라미터는

n_estimators = 1000

learning_rate = 0.01

 

로 설정했다.

 


검증셋을 통한

정확도는 약 80%

ROC score는 약 0.89

 

accuracy_score(y_train, lgbm_model1.predict(X_train))

최종 accuracy_score

84%로 과적합이 약간 있다고 판단했다.

 


✨ tree 기반 모델 feature 중요도 확인 ✨

Permutation Feature Importance

 

import eli5
from eli5.sklearn import PermutationImportance

perm = PermutationImportance(lgbm_model1, random_state=42).fit(X_test, y_test)
eli5.show_weights(perm, feature_names = X_test.columns.tolist(), top=25)

 

clean2 = clean1.drop(['cabin_deck_b', 'destination_trappist-1e', 'cabin_deck_g', 'family_num', 'cabin_deck_f', 'cabin_deck_d',
                       'vip_true', 'cabin_deck_t', 'cabin_side_unknown', 'cabin_deck_unknown', 'homeplanet_europa',
                       'destination_pso j318.5-22', 'age', 'group_num'], axis=1)
clean2.columns

 

위의 결과를 토대로

중요하지 않은 feature를 삭제했다.

 


✨ HyperOpt를 활용한 하이퍼 파라미터 튜닝 ✨

✔ Random Forest

from hyperopt import hp
# Random Forest
# 검색 공간 설정
rfc_search_space = {'max_depth': hp.quniform('max_depth', 5, 15, 1),
                    'min_samples_leaf': hp.quniform ('min_samples_leaf', 1, 4, 1),
                    'min_samples_split' : hp.quniform ('min_samples_split', 2, 4, 1),
                    'n_estimators' : hp.quniform('n_estimators', 10, 100, 10)}

랜덤 포레스트의 각 파라미터에

검색공간을 설정해 주었다.

 

from hyperopt import fmin, tpe, Trials
# 반복 결과를 저장할 변수 생성
trial_val = Trials()

# 100회 반복
best = fmin(fn = objective_func,
            space = rfc_search_space,
            algo = tpe.suggest,     # 최적화에 적용할 알고리즘 기본값
            max_evals = 100,
            trials = trial_val)

print('best :', best)

100회 반복으로 설정했다.

 

그 결과

max_depth = 8.0

min_samples_leaf = 3.0

min_samples_split = 2.0

n_estimators = 50

를 찾아냈다.

 


 

위의 파라미터로 학습시켰을 경우

검증셋을 통한 정확도향상 됬으며

 

accuracy_score(y_train, rfc_model2.predict(X_train))

과적합 또한 완화 되었다.

 


✔ LGBM

# LGBM
# 검색 공간 설정
lgbm_search_space = {'n_estimators' : hp.quniform('n_estimators', 100, 1000, 100),
                     'max_depth' : hp.quniform('max_depth', 5, 15, 1),                 # 5부터 20까지 간격 1
                     'num_leaves' : hp.quniform('num_leaves', 5, 15, 1),
                     'min_child_samples' : hp.quniform('min_child_samples', 10, 30, 2),    # 1부터 1까지 간격 1
                     'learning_rate' : hp.uniform('learning_rate', 0.01, 0.1),         # 0.01부터 0.2까지 균등분포
                     'colsample_bytree' : hp.uniform('colsample_bytree', 0.5, 1)}      # 0.5부터 1까지 균등분포

LGBM 또한 검색 공간을 설정하고

 

from hyperopt import fmin, tpe, Trials

trial_val = Trials()

# 100회 반복
best = fmin(fn = objective_func,
            space = lgbm_search_space,
            algo = tpe.suggest,     
            max_evals = 100,
            trials=trial_val)

print('best :', best)

100회 반복 하였다.

그 결과

 

colsample_bytree = 0.93

learning_rate = 0.035

max_depth = 8

min_child_samples = 12

n_estimators = 400

num_leaves = 8

 

위와 같은 파라미터를

확인했다.

 

검증셋의 정확도는 별 차이 없지만

 

accuracy_score(y_train, lgbm_model3.predict(X_train))

과적합완화 되었다.

 


✔ XGBoost

import xgboost as xgb

param_xgb = {
    'learning_rate': [0.01, 0.05, 0.1, 0.3], # 경사하강법 : '매개변수' -> 최소오차 -> 보폭 크기
    'max_depth': [3], # 트리의 깊이 (오버피팅 방지)
    'subsample': [0.3, 0.5, 0.7, 1], # 추출할 데이터 비율
    'n_estimators': [300, 600, 1000] # 트리 개수
}

추가적으로 XGBoost를 사용해보았는데

 

model_xgb = xgb.XGBClassifier()
gs_model_xgb = GridSearchCV(model_xgb, param_xgb, n_jobs=-1, scoring='f1', cv = 5)
gs_model_xgb.fit(X_train, y_train)

XGBoost의 경우

GridSearch를 활용하여 최적의 파라미터를 구하고학습을 진행하였다.

 

그 결과 검증셋의 정확도0.79

ROC score0.88

 

accuracy_score(y_train, xgb_model.predict(X_train))

최종 정확도는 0.80이 나왔다.

 


아래는 정리한 표이다.

 

 

 

 

 

'NA Project' 카테고리의 다른 글

주택 전세 가격과 경제 변수간 관계  (0) 2022.12.18