본문 바로가기

Theory/DeepLearning

[Keras] 타이타닉 생존자 분석. 디카프리오는 정말 살 수 없었을까?

영화 타이타닉의 감동을 이번 공부를 하면서 느꼈네요. 머신러닝을 공부하는 분들이 항상 수행하는 연습문제로 MNIST 필기 숫자 인식과 함께 타이타닉 생존자 분석이 있습니다. 저도 역시 공부하는 과정에서 거쳤구요. 이번에는 최근 그 매력에 빠진 케라스(Keras)로 수행해보려고 합니다. 그리고, Keras로 수행한 후 레오나르도 디카프리오가 연기한 그 역할의 남자 주인공의 생존 가능성을 한 번 확인해 보려고 했습니다.^^

언제나 그랬지만, 오늘도 역시 혼자힘으로는 못하고 많은 분의 도움을 받았습니다. 일부 내용은 그대로 따라했구요. 먼저 Pandas를 이용한 분석과 다양한 데이터를 들여다보고 시각화하는 것은 Matt dePero님의 Titanic Machine Learning from Disaster[바로가기]로 학습했구요. 여러 용어 정리와 기초 지식은 Ritchie Ng의 Titanic Survival Data Exploration[바로가기]. 비록 tflearn이라는 모듈을 사용해서 제가 실행은 못해봤지만, 그래도 그 내용 특히 결론 부분의 아이디어를 제공해준 tflearn의 tutorial 문서[바로가기]를 참조했습니다. 그리고 무엇보다 김태영님의 Keras 강좌[바로가기]가 큰 도움이 되고 있습니다.

본론으로 들어갈까요?^^

타이타닉

타이타닉에 대해 그래도 좀 알아야하지 않을까하고 인터넷을 기웃거려 보았습니다. 그러나 그 글들을 제가 여기서 단순 복사해봐야 의미가 없으니, 정말 심플하게 개요만 이야기를 하죠.

타이타닉호의 실제 모습. 출처 : 위키백과

1912년 4월 10일 영국 Southampton에서 출발하고 5일 후, 1912년 4월 15일 침몰했습니다.

Southampton에서 출발한 후 New York으로 가던 중이었다고 하네요.

타이타닉의 절단면 구조도. 출처 : 위키백과

정말 웅장한 크기의 배입니다.

타이타닉 내부에 있었다는 당시 최신 운동기구. 출처:위키백과

1912년에 저런 운동기구도 있었다고 하네요.ㅠㅠ.

E·J·스미스 선장. 출처:위키백과

당시 배를 이끌던 스미스 선장님이라고 합니다. 선원들과 승객들에게 인기가 많아서 어떤 승객은 스미스 선장이 책임지는 배만 탔다고 합니다. 원래 은퇴하려고 했는데, 타이타닉의 첫 항해에 선장을 맡아 달라는 회사의 부탁을 거절하지 못하고 마지막 항해로 배를 책임졌다고 합니다.ㅠㅠ. 배의 완전 침몰 직전까지도 승객의 대피와 구조 요청등을 지휘하다가 배와 함께 운명을 했다고 합니다.

저렇게 구명보트에 옮겨타고 탈출할 수 있었던 상황도 제한적이었다고 합니다. 일단 당시 규정 위반은 아니었지만, 승객수에 비해 구명보트가 부족했다고 합니다. 그래도 나중에 데이터로도 나오지만, 선장과 선원들은 여성과 아이들을 먼저 구명보트에 태우기 위해 애를 썼다고 합니다. 뉴욕의 유명한 백화점을 소유한 노부부 중 남편의 구명보트 탑승을 선원이 거절하자 부인은 하녀에게 자신의 모피코트를 입히고 부부가 같이 배에 남았다는 일화도 있구요. 아무튼 이런 타이타닉호의 분석을 하기 앞서 먼저 고인들의 명복을 빕니다.

타이타닉 데이터 관찰

이제... 인터넷에서 쉽게 얻을 수 있는 타이타닉 탑승자 명단을 가지고,

titanic.xls

데이터 분석을 시작해 보겠습니다.

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

%matplotlib inline

raw_data = pd.read_excel('titanic.xls')
raw_data.info()

먼저 필요한 모듈을 import하고 xls파일을 pandas로 읽어 보았습니다.

먼저, 위 용어들을 알아야겠네요.

  • pclass : 객실 등급
  • survived : 생존 유무
  • sex : 성별
  • age : 나이
  • sibsp : 형제 혹은 부부의 수
  • parch : 부모, 혹은 자녀의 수
  • fare : 지불한 운임
  • boat : 탈출한 보트가 있다면 boat 번호

우리에게 필요한 데이터는 차차 정리해 보도록 하죠. 먼저 pandas가 제공하는 describe을 사용했습니다.

raw_data.describe()

음...

위 결과에서는 0과 1로 표현된 survived로 인해 생존률이 38.2%라는 것을 알 수 있네요. 뭔가 생존률도 느낌이 안오니 시각화해서 볼까요

f,ax=plt.subplots(1,2,figsize=(12,6))

raw_data['survived'].value_counts().plot.pie(explode=[0,0.1],autopct='%1.2f%%',ax=ax[0])
ax[0].set_title('Survived')
ax[0].set_ylabel('')

sns.countplot('survived',data=raw_data,ax=ax[1])
ax[1].set_title('Survived')
plt.show()

이 결과는...

이렇습니다. 넵.. 생존률이 너무 낮네요. 500명 정도가 구조된 것으로 보입니다. 탑승한 사람들의 전체 연령은

raw_data['age'].hist(bins=20,figsize=(18,8),grid=False);

위 코드로 확인하면

입니다. 갓난아기들이 생각보다 많았네요. 그들의 생사는 어떻게 되었을까요.ㅠㅠ. groupby 명령을 사용해서 선실 등급별 상황도 볼 수 있습니다.

raw_data.groupby('pclass').mean()

음... 1등실의 생존률이 높네요. 갓난 아이들은 3등실에 많았던 모양입니다. 나이 평균이 낮네요. 에휴.ㅠㅠ. 좀 더 자세히 들어가보죠..

데이터의 통계적 기초 분석

먼저... 서로 연관있어 보이는 데이터가 무엇인지 상관계수를 슬쩍 찾아보죠.

plt.figure(figsize=(10, 10))
sns.heatmap(raw_data.corr(), linewidths=0.01, square=True,
            annot=True, cmap=plt.cm.viridis, linecolor="white")
plt.title('Correlation between features')
plt.show()

결과를 보면

이렇습니다. 일단 음이든 양의 상관관계든, survived는 pclass와 fare와 관계가 있어보이긴 합니다. 그렇다고 그것만 데이터로 보면 너무 정확도가 안나올게 뻔하겠죠.  그리고 나이를 조금 나눠서

raw_data['age_cat'] = pd.cut(raw_data['age'], bins=[0, 10, 20, 50, 100], 
                             include_lowest=True, labels=['baby', 'teenage', 'adult', 'old'])
plt.figure(figsize=[12,4])
plt.subplot(131)
sns.barplot('pclass', 'survived', data=raw_data)
plt.subplot(132)
sns.barplot('age_cat', 'survived', data=raw_data)
plt.subplot(133)
sns.barplot('sex', 'survived', data=raw_data)
plt.subplots_adjust(top=1, bottom=0.1, left=0.10, right=1, hspace=0.5, wspace=0.5)
plt.show()

분석해보면

이렇게 볼 수 있겠습니다. pclass는 1등석인 경우 확실히 생존률이 높았네요. 이 부분은 위키백과에서 몇몇 설명을 하고 있습니다. 제가 약간 뭉클한 부분은 바로 0세에서 10세로 구분한 baby 등급의 연령대 아이들의 생존률이 다른 연령대보다 높았다는 것과 남성보다 여성의 생존률이 높았다는 겁니다. 죽음의 공포를 이기고 양보한 너무나 위대한 작은 영웅들이 분명 있었을 겁니다. 나이 부분의 분포 곡선을 확인하기 위해

f,ax = plt.subplots(figsize=(12,6))
g = sns.kdeplot(raw_data["age"][(raw_data["survived"] == 0) & (raw_data["age"].notnull())], 
                ax = ax, color="Blue", shade = True)
g = sns.kdeplot(raw_data["age"][(raw_data["survived"] == 1) & (raw_data["age"].notnull())], 
                ax =g, color="Green", shade= True)
g.set_xlabel("Age")
g.set_ylabel("Frequency")
g = g.legend(["Not Survived","Survived"])

보면

네.. 확실히 어린 아이들의 생존률이 상대적으로 높습니다. 남여 비율에 대한 부분도 본다면

f,ax=plt.subplots(1,2,figsize=(12,6))
sns.countplot('sex',data=raw_data, ax=ax[0])
ax[0].set_title('Count of Passengers by Sex')

sns.countplot('sex',hue='survived',data=raw_data, ax=ax[1])
ax[1].set_title('Sex:Survived vs Dead')
plt.show()

결과는

인데요. 두배쯤 많은 남성 승객수에도 불구하고, 구조된 여성의 수가 두배쯤 더 많다는 사실이 확실히 나타납니다. 당시 타이타닉에 탑승했던, 그리고 죽음의 위기에 처했던 사람들 중 아이와 여성에게 삶을 양보한 사람들이 분명 많았다는 거겠죠. 그리고, 구조 보트에 탑승한 사람들 중에는

boat_survivors = raw_data[raw_data['boat'].notnull()]
f,ax=plt.subplots(1,2,figsize=(12,6))

boat_survivors['survived'].value_counts().plot.pie(explode=[0,0.1],autopct='%1.2f%%',ax=ax[0])
ax[0].set_title('Survived')
ax[0].set_ylabel('')

sns.countplot('survived',data=boat_survivors,ax=ax[1])
ax[1].set_title('Survived')
plt.show()

확실히...

생존자가 많았습니다. 구조 보트에 탐승하고도 사망한 비율이 1.85%이니, 타이타닉이 대서양 망망대해서 침몰했지만, 그 후 구조하러 온 다른 배들도 많았다는 것일까요....

딥러닝을 이용한 생존 가능성 예측

이번에는 제목에 있듯이 Keras를 이용해서 머신러닝으로 생존 가능성을 확인해보는 과정으로 들어가죠.

tmp = []
for each in raw_data['sex']:
    if each == 'female':
        tmp.append(1)
    elif each == 'male':
        tmp.append(0)
    else:
        tmp.append(np.nan)

raw_data['sex'] = tmp

raw_data['survived'] = raw_data['survived'].astype('float')
raw_data['pclass'] = raw_data['pclass'].astype('float')
raw_data['sex'] = raw_data['sex'].astype('float')
raw_data['sibsp'] = raw_data['sibsp'].astype('float')
raw_data['parch'] = raw_data['parch'].astype('float')
raw_data['fare'] = raw_data['fare'].astype('float')

raw_data = raw_data[raw_data['age'].notnull()]
raw_data = raw_data[raw_data['sibsp'].notnull()]
raw_data = raw_data[raw_data['parch'].notnull()]
raw_data = raw_data[raw_data['fare'].notnull()]

raw_data.info()

좀 지루하지만, 데이터에서 여성을 1로 남성을 0으로 두고, 몇몇 데이터를 float형으로 선언하고, nan이 있는 데이터는 제거했습니다. 그랬더니...

데이터가 1045개로 줄었네요ㅠㅠ. 그 와중에 다시'

x_data = raw_data.values[:, [0,3,4,5,6,8]]
y_data = raw_data.values[:, [1]]

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(x_data, y_data, 
                                                    test_size=0.1, random_state=7)

이렇게 신경망에 돌릴 데이터를 선별하고, 또 그 와중에... 10%는 test용으로 남겨두었습니다. 그리고...

import tensorflow as tf
import keras
from keras.models import Sequential
from keras.layers.core import Dense
np.random.seed(7)

print('tensorflow version : ', tf.__version__)
print('keras version : ', keras.__version__)

Keras import~^^ 근데.. 전 왜 이걸 실행하면...

저 워닝이 날까요?? 흠.. 뭐 아무튼. 저는 tensorflow 1.5, keras 2.1.5 버전에서 테스트 되고 있습니다. 

일단, 레이어는

model = Sequential()
model.add(Dense(255, input_shape=(6,), activation='relu'))
model.add(Dense((1), activation='sigmoid'))
model.compile(loss='mse', optimizer='Adam', metrics=['accuracy'])
model.summary()

좀 wide하게 벌리고, 설정을 이것 저것 변경해 보다가 저렇게 정했습니다.

summary는 저렇게 나오네요^^ model_to_dot을 이용해서

from IPython.display import SVG
from keras.utils.vis_utils import model_to_dot

SVG(model_to_dot(model, show_shapes=True).create(prog='dot', format='svg'))

보면...

모델 구조는 위와 같습니다. 그리고 즐겁디 즐거운 fit~~~

hist = model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=500)

plt.figure(figsize=(12,8))
plt.plot(hist.history['loss'])
plt.plot(hist.history['val_loss'])
plt.plot(hist.history['acc'])
plt.plot(hist.history['val_acc'])
plt.legend(['loss','val_loss', 'acc','val_acc'])
plt.show()

했더니...

라고 나타났습니다. 데이터가 없어요. 천개 정도... 만족하고 지나가죠...

디카프리오는 정말 살 수 없었을까?

dicaprio = np.array([3., 0., 19., 0., 0., 5.]).reshape(1,6)
winslet = np.array([1., 1., 17., 1., 2., 100.]).reshape(1,6)

뭘까요? 네.. 3등실 탑승, 남성, 19살, 형재없고, 부모나 자녀없음, 싼 가격으로 표를 삼... 넵.. 극중 레오나르도 디카프리오가 연기한 역할에 대한 데이터이구요. 1등실 탑승, 여성, 17살,  운임은 비싸게 주었고, 부모와 함께 탑승하고, 약혼자도 있었다는 데이터를 이용해서 디카프리오와 윈슬렛을 위와 같이 특정합니다.

그리고 예측~ 앗.. 이런.. 디카프리오는 생존 가능성이 11.92%로 나오네요.ㅠㅠ. 윈슬렛은 99.99%ㅠㅠ. 아.. 두 사람은 정말 이어질 수 없는 운명이었나 봅니다ㅠㅠ.

그냥 공부하다가 슬쩍 수행해본 정확하게는 따라해본 내용입니다. 너무 뭐라하지 말아주세요. 다시한번 불의의 사고로 운명하신 고인들의 명복을 빕니다. 마지막으로 타이타닉을 조사하며 인상깊었던 몇몇 에피소드의 제목만 적어봅니다.

  • 타이타닉이 침몰할때 가장 가까운 곳에 있던 배의 무선사가 무전기를 끄고 잠드는 바람에 가장 가까운 곳의 배는 빠르게 구조활동을 할 수 없었음. 그래서 이후 무선사는 교대로 24시간 무전대기하는 국제항해법이 생김
  • 타이타닉은 SOS 구조 신호를 최초로 사용한 선박이라고 함
  • 실제 주인공 이름과 같은 승객이 있었다고 함.
  • 기관장과 기관사 전원은 마지막까지 배의 동력 유지와 화재 진압에 애를 쓰면서 전원 순직함
  • 윌리스 하틀러를 포함한 선상 연주대 8인은 배가 침몰할때까지 연주를 계속했다고 함
  • 배의 설계자인 앤드류스도 그 배에 탑승해 있었는데, 배와 함께 침몰했다고 함.
  • 토머스 바일스 신부는 배에 남은 사람들에게 고해성사를 하며 끝까지 사람들과 함께 있었다고 함.
  • 여성과 아이들을 먼저 태우기 위한 선원들의 이성적인 통제와 이에 호응한 남성 승객들의 양보가 아주 많았다고 함


반응형