서론
이번시간에는 Kaggle의 완전 기초, 시작단계(Getting Started) 컴피티션에 놓여져있는
Spaceship Titanic 데이터에 대해서 개인적으로 분석한 머신러닝 사례를
진행해 보고자 합니다.
먼저, 원본 데이터 링크입니다.
https://www.kaggle.com/competitions/spaceship-titanic
Spaceship Titanic | Kaggle
www.kaggle.com
제 노트북은 아래에서 보실 수 있습니다. 빠르게 요약된 버전을 보시려면, 아래 링크를
보셔도 무방합니다! (처음에 파일 로드할 시의 경로만 다릅니다)
https://www.kaggle.com/code/apatheia0/space-titanic-lightgbm-test/notebook
먼저 몇가지 결론들을 미리 알려드리자면,
사용한 머신러닝 알고리즘은 GBM기반 모델중 하나인 LightGBM 중 Classifier를
사용했습니다.
목표는 Spaceship 승선원들의 Transported가 True인지 확인하는데요.
즉, 잘 전송되었는지 여부를 분석하는 것입니다. 다시말해 종속변수가 Transported입니다.
시각화의 단계는 auc_roc 함수에 대한 혼동행렬(confusion matrix)을
heatmap으로 표현했고, 변수중요도(feature important)를
barplot으로 표현했습니다.
특별히 데이터에 대해서 생소하신 분들을 위해서 잠깐 설명드리자면,
spaceship_titanic은 현실의 데이터는 아니고,
유명한 데이터셋 중 하나인 타이타닉 생존자 파일의 'SF버전' 미래우주 시나리오입니다.
머나먼 미래, 인류는 이제 다른 성단간의 운행이 가능한 기술에 이르렀습니다.
이제 많은 사람들이 지구를 떠나 다른 성단으로도 여행을 떠날수도 있습니다!
스페이스 타이타닉호는 이름은 불길하지만, 아무튼 한 달전쯤에 13,000명 이상의
승객을 태우고 초고속 이동을 떠났습니다. 하지만 중간에 일련의 사고로 인해
탑승원들의 일부가 다른 차원(an alternate dimension)으로 Transported(전송)되고 말았습니다!
이걸 좋은 일로 받아들여야 할지, 나쁜 일로 받아들여야 할지 모르겠네요.
위에서 언급한 바와 같이, 다른 차원으로 전송된 사람들의 상황을 분석해야 합니다.
자, Dataset의 내용 자체는 꽤 흥미롭습니다.
※ 번역에 오류나 오판이 있을 수 있는점 양해 부탁드리며, 댓글 등으로 오류를 짚어 주시면 바로 수정하겠습니다.
※ 대부분 colab에서 데이터프레임을 표현할때는 원문을 가져왔으나, 데이터가 너무 큰 경우,
부득이하게 이미지로 캡쳐했습니다.
이제 시작하겠습니다!
# 분석 알고리즘
# 12.27 ver1_LightGBM 사용
필요한 패키지를 호출하는 것부터 시작합니다.
# 라이브러리 호출
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import sklearn
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, roc_auc_score
필자는 미리 데이터를 train, test, sample_submission 모두 다운받아서
쓰고 있습니다.
df = pd.read_csv('C:/myPyCode/data/spaceship-titanic/train.csv', index_col = 0)
df
우주로 떠난 8693명의 정보입니다.
df.head()
이부분에서 각 컬럼들의 상관관계도 확인하려 했습니다.
sns.heatmap(df.corr(), annot=True, vmax=1, vmin=-1, cmap = 'coolwarm')
데이터프레임의 기본적인 사항을 보았습니다.
df.info()
# 종속변수는 Transported
# 독립변수는 Transported 외 필요한 데이터로 하기로 함
# PassengerId 필요, Transported 필요
<class 'pandas.core.frame.DataFrame'>
Index: 8693 entries, 0001_01 to 9280_02
Data columns (total 13 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 HomePlanet 8492 non-null object
1 CryoSleep 8476 non-null object
2 Cabin 8494 non-null object
3 Destination 8511 non-null object
4 Age 8514 non-null float64
5 VIP 8490 non-null object
6 RoomService 8512 non-null float64
7 FoodCourt 8510 non-null float64
8 ShoppingMall 8485 non-null float64
9 Spa 8510 non-null float64
10 VRDeck 8505 non-null float64
11 Name 8493 non-null object
12 Transported 8693 non-null bool
dtypes: bool(1), float64(6), object(6)
memory usage: 891.4+ KB
필자는 여기서 필요한 열과 불필요한 열을 구분해서 분석하려 했습니다.
일단 실수가 아닌, object로 이루어진 데이터 부분입니다.
# 불필요한 데이터 분석
# Name, Cabin(좌석 번호는 랜덤),
# df.Name
# df.Cabin
df.describe(include='O')
# df.HomePlanet.value_counts()
# df.Destination.unique
HomePlanet CryoSleep Cabin Destination VIP Name
count 8492 8476 8494 8511 8490 8493
unique 3 2 6560 3 2 8473
top Earth False G/734/S TRAPPIST-1e False Gollux Reedall
freq 4602 5439 8 5915 8291 2
VIP가 무엇을 의미하는지 보았습니다.
그냥 true/false더군요.
df.VIP.unique
<bound method Series.unique of PassengerId
0001_01 False
0002_01 False
0003_01 True
0003_02 False
0004_01 False
...
9276_01 True
9278_01 False
9279_01 False
9280_01 False
9280_02 False
Name: VIP, Length: 8693, dtype: object>
기본적인 사항들을 보았습니다.
df.describe()
Age RoomService FoodCourt ShoppingMall Spa VRDeck
count 8514.000000 8512.000000 8510.000000 8485.000000 8510.000000 8505.000000
mean 28.827930 224.687617 458.077203 173.729169 311.138778 304.854791
std 14.489021 666.717663 1611.489240 604.696458 1136.705535 1145.717189
min 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000
25% 19.000000 0.000000 0.000000 0.000000 0.000000 0.000000
50% 27.000000 0.000000 0.000000 0.000000 0.000000 0.000000
75% 38.000000 47.000000 76.000000 27.000000 59.000000 46.000000
max 79.000000 14327.000000 29813.000000 23492.000000 22408.000000 24133.000000
나름대로 잠깐 요약했습니다.
# 필요한 데이터 : PassengerId(아이디), HomePlanet(지구, 유로파, 화성 3종류), cryosleep(냉동수면y/n), VIP(y/n)
혹시 몰라서 데이터를 복사했습니다.
df2 = df.copy()
df2.head()
HomePlanet CryoSleep Cabin Destination Age VIP RoomService FoodCourt ShoppingMall Spa VRDeck Name Transported
PassengerId
0001_01 Europa False B/0/P TRAPPIST-1e 39.0 False 0.0 0.0 0.0 0.0 0.0 Maham Ofracculy False
0002_01 Earth False F/0/S TRAPPIST-1e 24.0 False 109.0 9.0 25.0 549.0 44.0 Juanna Vines True
0003_01 Europa False A/0/S TRAPPIST-1e 58.0 True 43.0 3576.0 0.0 6715.0 49.0 Altark Susent False
0003_02 Europa False A/0/S TRAPPIST-1e 33.0 False 0.0 1283.0 371.0 3329.0 193.0 Solam Susent False
0004_01 Earth False F/1/S TRAPPIST-1e 16.0 False 303.0 70.0 151.0 565.0 2.0 Willy Santantines True
기본적으로 필요한 결측치를 계산하고
일단 어떻게 처리해야 할지 고민했습니다.
거의 대부분 결측치가 있더군요.
# 결측치 확인
df.isna().mean()
# 룸서비스, 푸드코트, 쇼핑몰, 스파, vr덱 결측치는 평균값으로 계산
# 홈플래닛은 최빈값, Age는 평균값으로 처리, VIP는 F로 처리, 냉동수면은 F, 데스티네이션은 최빈값으로 처리
HomePlanet 0.023122
CryoSleep 0.024963
Cabin 0.022892
Destination 0.020936
Age 0.020591
VIP 0.023352
RoomService 0.020821
FoodCourt 0.021051
ShoppingMall 0.023927
Spa 0.021051
VRDeck 0.021627
Name 0.023007
Transported 0.000000
dtype: float64
앞으로도 절대 필요 없을것 같은 이름과, 선실 번호는 지웠습니다.
# 열 삭제 주의
df.drop(['Name','Cabin'], axis = 1, inplace =True)
df
homeplanet은 어떻게 할까요?
일단 최빈값으로 진행하려 합니다.
#결측치 처리 - homeplanet : 최빈값
df.HomePlanet.value_counts().plot(kind='bar')
진행했을 시 데이터프레임 원본이 날아가는 (얕은 복사)의 코드는
주의표시를 달아놨습니다.
# 홈플래닛 최빈값 처리 완료
# 주의
df.HomePlanet = df.HomePlanet.fillna('Earth')
df.HomePlanet.value_counts().plot(kind='bar')
데이터프레임을 안전 보관한 것입니다.
넘어갑니다.
df3 = df.copy()
룸서비스, 푸드코트, 쇼핑몰, 스파, VR덱(VRDeck)의 결측치는
모두 평균으로 묶었습니다.
혹시 추가로 합리적인 방법이 있다면 추천해주시면 감사하겠습니다.
# 결측치 처리 - 룸서비스, 푸드코트, 쇼핑몰, 스파, vr덱 : 평균
df.RoomService.fillna(df.RoomService.mean(), inplace=True)
df.FoodCourt.fillna(df.FoodCourt.mean(), inplace=True)
df.ShoppingMall.fillna(df.ShoppingMall.mean(), inplace=True)
df.Spa.fillna(df.Spa.mean(), inplace=True)
df.VRDeck.fillna(df.VRDeck.mean(), inplace=True)
결측치 재확인합니다.
df.isnull().sum()
HomePlanet 0
CryoSleep 217
Destination 182
Age 179
VIP 203
RoomService 0
FoodCourt 0
ShoppingMall 0
Spa 0
VRDeck 0
Transported 0
dtype: int64
나이의 결측치는 최빈값으로 처리했습니다.
Titanic에서도 유사하게 했던 내용입니다.
# 결측치 - age : 최빈값처리
df.Age.fillna(df.Age.median(), inplace = True)
결측치는 거의다 종료되어 더미 데이터를 남기려고 했습니다.
- 원핫 인코딩을 하기 위함이에요.
현재 정보입니다.
df.info()
<class 'pandas.core.frame.DataFrame'>
Index: 4277 entries, 0013_01 to 9277_01
Data columns (total 12 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Age 4277 non-null float64
1 RoomService 4277 non-null float64
2 FoodCourt 4277 non-null float64
3 ShoppingMall 4277 non-null float64
4 Spa 4277 non-null float64
5 VRDeck 4277 non-null float64
6 CryoSleep_True 4277 non-null uint8
7 Destination_PSO J318.5-22 4277 non-null uint8
8 Destination_TRAPPIST-1e 4277 non-null uint8
9 VIP_True 4277 non-null uint8
10 HomePlanet_Europa 4277 non-null uint8
11 HomePlanet_Mars 4277 non-null uint8
dtypes: float64(6), uint8(6)
memory usage: 259.0+ KB
seaborn이나 matplotlib를 써도 되지만 일단 판다스의 기능인
plot.pie를 써서 전송된 사람/ 안된 사람의 비율을 나눴습니다.
1이 전송 성공, 0이 실패한 사람입니다.
# 건너간사람과 건너가지 못한 사람의 비율
plt.figure(figsize=(7,7))
df['Transported'].value_counts().plot.pie(
explode=[0.1,0.1], autopct='%1.1f%%')
50%가 넘는군요
개별적으로 전처리는 이정도로 끝냈습니다.
다음 시간에는 spaceship의 test 파일을 가져와서 똑같이 전처리할 예정입니다😥
훈련셋과 시험셋을 분리해줍니다.
물론 우리가 필요한 종속변수는 Transported 입니다.
# 데이터 전처리 종료
# 훈련셋/ 시험셋 분리 시작
X = df.drop('Transported', axis = 1)
y = df['Transported']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.25, random_state = 100)
필요한 도구인 LightGBM을 꺼내줍니다.
LightGBM은 짧게 설명하자면 Gradient Boosting 모델의 심화된 버전이죠?
XGBoost와 함께 분류, 회귀 문제에서 간판격으로 많이 사용되고 있습니다.
다만, 데이터셋의 크기가 작을 경우 over-fitting의 가능성이 있다는 단점도 있습니다
▼ 자세한 설명
https://nurilee.com/2020/04/03/lightgbm-definition-parameter-tuning/
LightGBM 이란? 그리고 Parameter 튜닝하기
LightGBM에 관한 좋은 medium 포스트가 있어서 한글로 번역한 내용을 공유드려봅니다 :) Pushkar Mandot의 원문 바로가기: 안녕하세요, 머신러닝은 이 세상에서 가장 빠르게 성장하는 분야입니다. 매일
nurilee.com
# 필요한 lightgbm 패키지 호출
import lightgbm as lgb
학습을 진행하겠습니다. 랜덤값은 100입니다.
# LightGBM 분류기 정의
model = lgb.LGBMClassifier(random_state = 100)
model
LGBMClassifier(random_state=100)
# Lightgbm 학습 시작
model.fit(X_train, y_train)
pred = model.predict(X_test)
시험셋에 대한 예측값을 보여줍니다.
# 예측값
pred
array([0, 0, 1, ..., 1, 1, 1], dtype=uint8)
해당 학습에 대한 정확도 점수를 뽑아보았는데, 크게 하이퍼패러미터를
조절하지 않았기 때문에 높진 않습니다.
# 정확도 점수
accuracy_score(y_test, pred) # 낮은 점수로 인해 패러미터 튜닝 시작 필요
0.7897884084636615
RandomSearchCV를 사용할 지, GridSearchCV를 사용할 지
두 검증도구를 두고 고민했는데요.
시간이 더 짧게 소요된다는 추천을 받고 RandomSearchCV를 골랐습니다.
# RandomSearchCV 이용
from sklearn.model_selection import RandomizedSearchCV
# # 사용할 params 딕셔너리 정의
params = {"n_estimators" : [100, 500, 1000],"learning_rate" : [0.01, 0.05, 0.1, 0.3]
,"lambda_l1" : [0, 10, 20],"lambda_l2" : [0, 10, 20],"max_depth" : [5, 10, 15, 20],"subsample": [0.6, 0.8, 1]}
# params = {"n_estimators" : [1000],"learning_rate" : [0.3]
# ,"lambda_l1" : [10],"lambda_l2" : [10],"max_depth" : [20],"subsample": [1]}
밑에서 주석으로 처리한 코드는 실험한 부분입니다.
# LigthGBM 사용
model_2 = lgb.LGBMClassifier(random_state = 100)
lg = RandomizedSearchCV(model_2,
param_distributions = params,
n_iter = 30, scoring = 'roc_auc',
random_state=100, n_jobs = -1)
lg
Random search CV를 적용해줍니다.
RandomizedSearchCV(estimator=LGBMClassifier(random_state=100), n_iter=30,
n_jobs=-1,
param_distributions={'lambda_l1': [0, 10, 20],
'lambda_l2': [0, 10, 20],
'learning_rate': [0.01, 0.05, 0.1, 0.3],
'max_depth': [5, 10, 15, 20],
'n_estimators': [100, 500, 1000],
'subsample': [0.6, 0.8, 1]},
random_state=100, scoring='roc_auc')
학습시키는데 시간이 걸렸습니다만, colab보다 쥬피터가 더 짧게 걸립니다.
lg.fit(X_train, y_train) # 30초 가량 소요
RandomizedSearchCV(estimator=LGBMClassifier(random_state=100), n_iter=30,
n_jobs=-1,
param_distributions={'lambda_l1': [0, 10, 20],
'lambda_l2': [0, 10, 20],
'learning_rate': [0.01, 0.05, 0.1, 0.3],
'max_depth': [5, 10, 15, 20],
'n_estimators': [100, 500, 1000],
'subsample': [0.6, 0.8, 1]},
random_state=100, scoring='roc_auc')
학습됬다고 문구가 따로 뜨게 됩니다.
최적의 점수를 내는 패러미터입니다.
lg.best_params_ # 최적의 패러미터를 계산
{'subsample': 0.8,
'n_estimators': 500,
'max_depth': 20,
'learning_rate': 0.01,
'lambda_l2': 0,
'lambda_l1': 0}
최고점 입니다.
lg.best_score_ # 최적의 점수계산
0.8765142818325253
각각의 확률입니다.
lg_proba = lg.predict_proba(X_test)
lg_proba
array([[0.94023679, 0.05976321],
[0.81805936, 0.18194064],
[0.10973714, 0.89026286],
...,
[0.29261386, 0.70738614],
[0.16440701, 0.83559299],
[0.37558404, 0.62441596]])
auc 점수도 도출해보겠습니다.
k = roc_auc_score(y_test, lg_proba[:,1]) # roc_auc 점수계산
k
0.8704691174776167
87점까지 기록하고 있습니다.
이번에는 혼동 행렬(confusion_matrix)와 분류 리포트(classification_report)를 호출해서
f1-score를 뽑아보겠습니다.
# 혼동행렬 호출
from sklearn.metrics import confusion_matrix, classification_report
def confusion_matrix_view(y_test, pred):
plt.figure(figsize = (10,6))
cf_matrix = confusion_matrix(y_test, pred)
print(cf_matrix)
group_names = ['TN','FP','FN','TP']
group_counts = ['{0:0.0f}'.format(value) for value in
cf_matrix.flatten()] # 평평화
group_percentages = ['{0:.2%}'.format(value) for value in
cf_matrix.flatten()/np.sum(cf_matrix)]
labels = [f'{v1}\n{v2}\n{v3}' for v1, v2, v3 in
zip(group_names, group_counts, group_percentages)]
labels = np.asarray(labels).reshape(2,2)
sns.heatmap(cf_matrix, annot = labels, fmt='', cmap='coolwarm')
plt.ylabel('True')
plt.xlabel('Predicted')
plt.show()
# 분류 리포트 제출
print(classification_report(y_test, pred))
따로 컨퓨전 매트릭스와 분류 리포트를 함께 뽑는 함수를 정의했습니다.
confusion_matrix_view(y_test, lg.predict(X_test))
[[834 288]
[159 893]]
precision recall f1-score support
0 0.84 0.74 0.79 1122
1 0.76 0.85 0.80 1052
accuracy 0.79 2174
macro avg 0.80 0.80 0.79 2174
weighted avg 0.80 0.79 0.79 2174
위의 보고서에서 점수가 반환되게 됩니다.
높은 편은 아닙니다.
남아있는 feature들 중 중요도를 내림차순으로 전부 반환했습니다.
# 변수 중요도 확인
feature_important = pd.DataFrame({'features':X_train.columns,
'values':model.feature_importances_})
plt.figure(figsize=(10,9))
sns.barplot(x='values', y='features', data=feature_important.sort_values
(by='values',ascending = False))
plt.show()
Age, FoodCourt, VRDeck가 1위, 2위, 3위를 기록했네요. 😎
다음 시간에는 직접 test.csv와 sample submission파일을 불러와서
실제로 타이타닉 우주선의 전송된 승객을 예측하는 시간을 갖겠습니다.
'머신러닝 > 지도학습' 카테고리의 다른 글
[Machine Learning] Kaggle_데이터셋 분석_Video_Games_Sales.1 (0) | 2022.12.29 |
---|---|
[Machine Learning] Kaggle_연습사례 분석_Spaceship_titanic_2 (0) | 2022.12.28 |
[Machine Learning] Naive_Bayes 모델_ep.2 colab 검색기 (0) | 2022.12.26 |
[Machine Learning] Naive_Bayes 모델 사례_spam mail 분석 (5) | 2022.12.25 |
[Machine Learning] 결정트리_회귀(Regression) 실습 사례- 보험비 (0) | 2022.12.22 |