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))
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 score는 0.88
accuracy_score(y_train, xgb_model.predict(X_train))
최종 정확도는 0.80이 나왔다.
아래는 정리한 표이다.
'NA Project' 카테고리의 다른 글
주택 전세 가격과 경제 변수간 관계 (0) | 2022.12.18 |
---|