Kaggle - 타이타닉 생존여부 예측 모델 생성 (2)
import numpy as np
import pandas as pd
from pandas import Series
import matplotlib.pyplot as plt
import seaborn as sns
plt.style.use('seaborn')
sns.set(font_scale=2.5)
# 위 두 줄은 알아두는 것이 좋습니다. matplotlib의 기본 scheme 말고 seaborn scheme을 세팅합니다.
# 일일이 graph의 font size를 지정할 필요 없이 seaborn의 font_scale을 사용하면 편합니다.
# import plotly.offline as py
# py.init_notebook_mode(connected=True)
# import plotly.graph_objs as go
# import plotly.tools as tls
# 위의 4줄의 코드는 pip install plotly가 필요한 모듈인거 같습니다.
# 하지만, 제가 참고한 코드엔 포함되어 있으니 주석처리를 해놓겠습니다.
# ignore warnings
import warnings
warnings.filterwarnings('ignore')
%matplotlib inline
df_train = pd.read_csv('input/train.csv')
df_test = pd.read_csv('input/test.csv')
df_train['FamilySize'] = df_train['SibSp'] + df_train['Parch'] + 1
df_test['FamilySize'] = df_test['SibSp'] + df_test['Parch'] + 1
# 자신을 포함해야하니 1을 더합니다.
df_test.loc[df_test.Fare.isnull(), 'Fare'] = df_test['Fare'].mean()
# testset에 있는 NaN 값을 평균값으로 치환합니다.
df_train['Fare'] = df_train['Fare'].map(lambda i: np.log(i) if i > 0 else 0)
df_test['Fare'] = df_test['Fare'].map(lambda i: np.log(i) if i > 0 else 0)
# 'Fare'값을 그냥 사용하면 모델로 사용하기 부적합하기에 log를 씌어서 평평화?를 해주었습니다.
3. Feature engineering
본격적으로 feature engineering을 시작해봅시다 ! 가장 먼저, dataset에 존재하는 null data를 채우도록 합시다. 아무 숫자로 채우면 좀 그러니까, null data를 포함하는 feature의 statistics를 참고하거나, 다른 아이디어를 짜내어 채울 수 있습니다.
null data를 어떻게 채우느냐에 따라서 모델의 성능이 좌지우지될 수 있기 때문에, 신경을 많이 써야합니다.
Feature engineering은 실제 모델의 학습에 쓰려고 하는 것이므로, train 뿐만 아니라 test 도 똑같이 적용해주어야 합니다. 잊지마세요 !!
3.1 Fill Null
3.1.1 Fill Null in Age using title
Age에는 null data가 177개나 있습니다.
df_train['PassengerId'].count() - df_train['Age'].count()
# 결과는 177입니다.
이를 채울 수 있는 여러 아이디어가 있을 건데, 여기서 우리는 title + statistics를 사용해보도록 하겠습니다. 영어에서는 Miss, Mrr, Mrs 같은 title이 존재합니다. 각 탑승객의 이름에는 꼭 이런 title이 들어가게 되는데 이를 사용해보도록 하겠습니다.
pandas series에는 data를 string으로 바꿔주는 str method, 거기에 정규표현식을 적용하게 해주는 extract method가 있습니다. 이를 사용하여 title을 쉽게 추출할 수 있습니다. title을 Initial column에 저장하겠습니다.
정규표현식에서 쓰는 . 하나는 모든 문자를 의미하는데, 근데 저희가 뽑고 싶은건 Mr.에서의 '.'을 뽑고 싶은 거니까 '\.'을 사용하여 문자그대로 찾아오려합니다. 그래서 정규표현식을 사용했습니다.
소괄호는 정규표현식에 캡쳐그룹이라고 있습니다. 정규표현식을 쓰다보면 긴 문자열 중 여러개의 패턴이 뽑히게 되는데, 그거의 인덱스 별로 매핑을 해줄 수 있습니다. 예를 들어서 2019/06/07을 => ['2019', '06', '07']으로 매핑해준다는 뜻입니다.
+는 1개 이상, *는 0개 이상의 문자를 명칭합니다.
즉, 대문자, 소문자 알파벳만으로 구성된 한 개 이상의 문자에다가 '.' 붙는 애들을 가져온다는 뜻입니다.
df_train['Initial'] = df_train.Name.str.extract('([A-Za-z]+)\.') # Lets extract the Salutations
df_test['Initial'] = df_test.Name.str.extract('([A-Za-z]+)\.') # Lets extract the Salutations
pandas의 crosstab을 이용하여 우리가 추출한 Initial과 Sex 간의 count를 살펴보도록 합시다.
pd.crosstab(df_train['Initial'], df_train['Sex']).T.style.background_gradient(
cmap='summer_r') # Checking the Initials with the Sex
위 Table을 참고하여 남자, 여자가 쓰는 initial을 구분해 보겠습니다. replace 메소드를 사용하면, 특정 데이터 값을 원하는 값으로 치환해줍니다.
df_train['Initial'].replace(['Mlle', 'Mme', 'Ms', 'Dr', 'Major', 'Lady', 'Countess', 'Jonkheer',
'Col', 'Rev', 'Capt', 'Sir', 'Don', 'Dona'],
['Miss', 'Miss', 'Miss', 'Mr', 'Mr', 'Mrs', 'Mrs', 'Other', 'Other', 'Other',
'Mr', 'Mr', 'Mr', 'Mr'], inplace = Ture)
df_test['Initial'].replace(['Mlle', 'Mme', 'Ms', 'Dr', 'Major', 'Lady', 'Countess', 'Jonkheer',
'Col', 'Rev', 'Capt', 'Sir', 'Don', 'Dona'],
['Miss', 'Miss', 'Miss', 'Mr', 'Mr', 'Mrs', 'Mrs', 'Other', 'Other', 'Other',
'Mr', 'Mr', 'Mr', 'Mr'], inplace = Ture)
df_train.groupby('Initial').mean()
여성과 관계가 있는 Miss와 Mrs의 생존율이 높은 것을 볼 수 있습니다.
df_train.groupby('Initial')['Survived'].mean().plot.bar()
여기서도 한 눈에 Miss, Mrs의 생존율이 높은 것을 확인할 수 있습니다.
이제 본격적으로 Null 을 채워봅시다. null data를 채우는 방법은 정말 많이 존재합니다. statistics를 활용하는 방법도 있고, null data가 없는 데이터를 기반으로 새로운 머신러닝 알고리즘을 만들어 예측해서 채워넣는 방식도 있습니다. 여기서는 statistics를 활용해보도록 합시다.
여기서 statistics는 train data의 것을 의미합니다. 우리는 언제나 test를 unseen으로 둔 상태로 놔둬야하며, train에서 얻은 statistics를 기반으로 test의 null data를 채워야 합니다.
df_train.groupby('Initial').mean()
Age의 평균을 이용하여 Null value를 모두 채우도록 하겠습니다.
pandas dataframe을 다룰 때에는 boolean array를 이용해 indexing 하는 방법이 엄청 편합니다.
아래 코드 첫줄을 해석하자면, isnull() 이면서 Initiall이 Mr인 조건을 만족하는 row(탑승객)의 'age'의 값을 33으로 치환한다는 뜻입니다.
loc + boolean + column을 사용해 값을 치환하는 방법은 자주 쓰이므로 꼭 익숙해지도록 합시다.
참고로 데이터프레임(dataframe, df)에서 특정한 데이터만 골라내는 것을 인덱싱(indexing)이라고 합니다. Pandas는 numpy 행렬과 같이 쉼표를 사용한 (행 인덱스, 열 인덱스) 형식의 2차원 인덱싱을 지원하기 위해 다음과 다음과 같은 특별한 인덱서(indexer) 속성도 제공합니다.
- loc : 라벨값 기반의 2차원 인덱싱
- iloc : 순서를 나타내는 정수 기반의 2차원 인덱싱
- at : 라벨값 기반의 2차원 인덱싱 (한 개의 스칼라 값만 찾는다)
- iat : 순서를 나타내는 정수 기반의 2차원 인덱싱 (한 개의 스칼라 값만 찾는다)
자세한 정보는 https://datascienceschool.net/view-notebook/704731b41f794b8ea00768f5b0904512/ 이 곳을 참고합시다.
df_train.loc[(df_train.Age.isnull())&(df_train.Initial=='Mr'),'Age'] = 33
df_train.loc[(df_train.Age.isnull())&(df_train.Initial=='Mrs'),'Age'] = 36
df_train.loc[(df_train.Age.isnull())&(df_train.Initial=='Master'),'Age'] = 5
df_train.loc[(df_train.Age.isnull())&(df_train.Initial=='Miss'),'Age'] = 22
df_train.loc[(df_train.Age.isnull())&(df_train.Initial=='Other'),'Age'] = 46
# test의 값은 train 값을 기준으로 바꿔줍시다.
df_test.loc[(df_test.Age.isnull())&(df_test.Initial=='Mr'),'Age'] = 33
df_test.loc[(df_test.Age.isnull())&(df_test.Initial=='Mrs'),'Age'] = 36
df_test.loc[(df_test.Age.isnull())&(df_test.Initial=='Master'),'Age'] = 5
df_test.loc[(df_test.Age.isnull())&(df_test.Initial=='Miss'),'Age'] = 22
df_test.loc[(df_test.Age.isnull())&(df_test.Initial=='Other'),'Age'] = 46
# df.loc['A', 'B']가 있다면
# 'A'는 행을 나타내고 'B'는 열(Label)을 나타냅니다.
여기선 간단하게 Null을 채웠지만, 좀 더 다양한 방법을 쓴 예시들이 다른 커널에 존재합니다. 이 외에도 다른 캐글러들의 커널을 보며 여러 참신한 아이디어를 살펴봅시다.
3.1.2 Fill Null in Embarked
print('Embarked has ', sum(df_train['Embarked'].isnull()), ' Null values')
Embarked는 Null value가 2개이고, S에서 가장 많은 탑승객이 있었으므로, 간단하게 Null을 'S'로 채우겠습니다.
dataframe의 fillna method를 이용하면 쉽게 채울 수 있습니다. 여기서 inplace = True로 하면 df_train에 fillna를 실제로 적용하게 됩니다.
df_train['Embarked'].fillna('S', inplace=True)
3.2 Change Age(continuous to categorical)
Age는 현재 continuous feature입니다. 이대로 써도 모델을 세울 수 있지만, Age를 몇 개의 group으로 나누어 category 화 시켜줄 수도 있습니다. continuous를 categorical로 바꾸면 information loss가 생길 수 있지만, 본 글에선 다양한 방법을 적용하는 것이 목적이므로 진행을 해봅시다.
방법은 여러가지 있습니다. dataframe의 indexing 방법인 loc를 사용하여 직접해줄 수 있고, 아니면 apply를 사용하여 함수를 넣어줄 수도 잇습니다.
loc를 사용해보도록 하겠습니다.(loc가 자주쓰게 되므로 그 사용법을 숙지하는 것이 중요합니다.
나이는 10살 간격으로 나누어 볼까요?
df_train['Age_cat'] = 0
df_train.loc[df_train['Age'] < 10, 'Age_cat'] = 0
df_train.loc[(10 <= df_train['Age']) & (df_train['Age'] < 20), 'Age_cat'] = 1
df_train.loc[(20 <= df_train['Age']) & (df_train['Age'] < 30), 'Age_cat'] = 2
df_train.loc[(30 <= df_train['Age']) & (df_train['Age'] < 40), 'Age_cat'] = 3
df_train.loc[(40 <= df_train['Age']) & (df_train['Age'] < 50), 'Age_cat'] = 4
df_train.loc[(50 <= df_train['Age']) & (df_train['Age'] < 60), 'Age_cat'] = 5
df_train.loc[(60 <= df_train['Age']) & (df_train['Age'] < 70), 'Age_cat'] = 6
df_train.loc[70 <= df_train['Age'], 'Age_cat'] = 7
df_test['Age_cat'] = 0
df_test.loc[df_test['Age'] < 10, 'Age_cat'] = 0
df_test.loc[(10 <= df_test['Age']) & (df_test['Age'] < 20), 'Age_cat'] = 1
df_test.loc[(20 <= df_test['Age']) & (df_test['Age'] < 30), 'Age_cat'] = 2
df_test.loc[(30 <= df_test['Age']) & (df_test['Age'] < 40), 'Age_cat'] = 3
df_test.loc[(40 <= df_test['Age']) & (df_test['Age'] < 50), 'Age_cat'] = 4
df_test.loc[(50 <= df_test['Age']) & (df_test['Age'] < 60), 'Age_cat'] = 5
df_test.loc[(60 <= df_test['Age']) & (df_test['Age'] < 70), 'Age_cat'] = 6
df_test.loc[70 <= df_test['Age'], 'Age_cat'] = 7
두번째로는 간단한 함수를 만들어서 apply 메소드에 넣어주는 방법입니다. 훨씬 수월합니다.
def category_age(x):
if x < 10:
return 0
elif x < 20:
return 1
elif x < 30:
return 2
elif x < 40:
return 3
elif x < 50:
return 4
elif x < 60:
return 5
elif x < 70:
return 6
else:
return 7
df_train['Age_cat_2'] = df_train['Age'].apply(category_age)
두 가지 방법이 잘 적용되었다면, 둘 다 같은 결과를 내야합니다. 이를 확인하기 위해 Series 간 boolean 비교 후 all() 메소드를 사용합시다. all() 메소드는 모든 값이 True면 True 하나라도 False가 있으면 False를 줍니다.
print('1번 방법, 2번 방법 둘 다 같은 결과를 내면 True를 줍니다. => ', (
df_train['Age_cat'] == df_train['Age_cat_2']).all())
보시다시피 True입니다. 둘 중 편한 걸 선택하셔서 사용하시면 됩니다.
이제 중복되는 Age_cat 컬럼과 원래 Age를 제거하면 됩니다 !
df_train.drop(['Age', 'Age_cat_2'], axis = 1, inplace = True)
df_test.drop(['Age'], axis = 1, inplace = True)
3.3 Change Initial, Embarked and Sex (string to numerical)
현재 Initial은 Mr, Mrs, Miss, Master, Other 총 5개로 이루어져 있습니다. 이런 카테고리로 표현되어져 있는 데이터를 모델에 인풋으로 넣어줄 때 우리가 해야할 것은 먼저 컴퓨터가 인식할 수 있도록 수치화하는 것입니다. map method를 가지고 간단히 할 수 있습니다. 사전 순서대로 mapping을 하도록 합시다.
df_train['Initial'] = df_train['Initial'].map({'Master':0, 'Miss':1, 'Mr':2, 'Mrs':3, 'Other':4})
df_test['Initial'] = df_test['Initial'].map({'Master':0,'Miss':1,'Mr':2,'Mrs':3,'Other':4})
Embarked 도 C, Q, S로 이루어져 있으니 map을 이용해서 바꾸어 봅시다. 그러기 앞서서, 특정 column에 어떤 값들이 있는 지 확인해보는 방법을 잠깐 살펴보도록 하겠습니다. 간단히 unique() 메소드를 쓰거나, value_counts()를 써서 count 까지 보는 방법이 있습니다.
df_train['Embarked'].unique()
df_train['Embarked'].value_counts()
위 두 방법을 사용해 Embarked가 S, C, Q 세가지로 이루어진 것을 볼 수 있습니다. 이제 map을 사용해 봅시다.
df_train['Embarked'] = df_train['Embarked'].map({'C':0, 'Q':1, 'S':2})
df_test['Embarked'] = df_test['Embarked'].map({'C':0, 'Q':1, 'S':2})
한번 Null이 사라졌는지도 확인해볼까요? Embarked Column만 가져온 것은 하나의 pandas의 Series 객체므로, isnull() 메소드를 사용해 Series의 값들이 null 인지 아닌지에 대한 boolean 값을 얻을 수 있습니다. 그리고 이것에 any()를 사용하여, True가 단 하나라도 있을 시(Null이 한 개라도 있을 시) True를 반환해주게 됩니다. 우리는 Null을 S로 다 바꿔주었으므로 False를 얻게 됩니다.
df_train['Embarked'].isnull().any()
Sex도 Femail, male로 이루어져 있는데, map을 이용해 바꿔볼까요?
df_train['Sex'] = df_train['Sex'].map({'female':0, 'male':1})
df_test['Sex'] = df_test['Sex'].map({'female':0, 'male':1})
이제 각 feature 간의 상관관계를 살펴봅시다. 두 변수간의 Pearson correlation을 구하면 (-1, 1) 사이의 값을 얻을 수 있습니다. -1로 갈수록 음의 상관관계를 의미하며, 1로 갈수록 양의 상관관계를 의미하며, 0은 상관관계가 없다는 것을 의미합니다. 구하는 수식은 아래와 같습니다.
$$r_{xy} = \frac{Cov(x,y)}{S_{x}S_{y}} = \frac{\frac{1}{n-1}\sum_{i=1}^n(x_{i}-\bar x)(y_{i}-\bar y)}{S_{x}S_{y}}$$
통계학에서, 피어슨 상관 계수란 두 변수 X와 Y간의 선형 상관 관계를 계량화한 수치입니다. 피어슨 상관 계수는 코시-슈바르츠 부등식에 의해 +1과 -1 사이의 값을 가지며, +1은 완벽한 양의 선형 상관 관계, 0은 선형 상관 관계 없음, -1은 완벽한 음의 선형 상관 관계를 의미합니다.
피어슨 상관 계수는 두 변수의 공분산을 표준 편차의 곱으로 나눈 값입니다. (무슨 소리인지 저도 모르겠습니다.)
우리는 여러 feature를 가지고 있으니 이를 하나의 matrix 형태로 보면 편할 텐데, 이를 heatmap plot이라고 하며, dataframe의 corr() 메소드와 seaborn을 가지고 편하게 그릴 수 있습니다.
heatmap_data = df_train[['Survived', 'Pclass', 'Sex', 'Fare', 'Embarked',
'FamilySize', 'Initial', 'Age_cat']]
colormap = plt.cm.RdBu
plt.figure(figsize=(14, 12))
plt.title('Pearson Correlation of Features', y=1.05, size=15)
sns.heatmap(heatmap_data.astype(float).corr(), linewidths=0.1, vmax=1.0,
square=True, cmap=colormap, linecolor='white', annot=True,annot_kws={"size":16})
del heatmap_data
우리가 EDA(exploratory data analysis, 탐색적 자료 분석)에서 살펴봤듯이, Sex와 Pclass가 Survived에 상관관계가 어느 정도 있음을 알 수 있습니다.
생각보다 fare와 Embarked도 상관관계가 있음을 알 수 있습니다.
또한, 우리가 여기서 얻을 수 있는 정보는 서로 강한 상관관계를 가지는 feature들이 없다는 것입니다. 이 것은 우리가 모델을 학습시킬 때, 불필요한(redundant, superfluous) feature가 없다는 것을 의미합니다. 1 또는 -1의 상관 관계를 가진 feature A, B가 있다면, 우리가 얻을 수 있는 정보는 사실 하나일 거니까요.
이제 실제로 모델을 학습시키기 앞서서 data preprocessing (전처리)을 진행해보겠습니다.
3.4 One-hot encoding on Initial and Embarked
수치화시킨 카테고리 데이터를 그대로 넣어도 되지만, 모델의 성능을 높이기 위해 one-hot encoding을 해줄 수 있습니다. 수치화는 간단히 Master == 0, Miss == 1, Mr == 2, Mrs == 3, Other == 4로 매핑해주는 것을 말합니다.
One-hot encoding은 위 카테고리를 아래와 같이 (0, 1)로 이루어진 5차원 벡터를 나타내는 것을 말합니다.
Initial_Master | Initial_Miss | Initial_Mr | Initial_Mrs | Initial_Other | |
Master | 1 | 0 | 0 | 0 | 0 |
Miss | 0 | 1 | 0 | 0 | 0 |
Mr | 0 | 0 | 1 | 0 | 0 |
Mrs | 0 | 0 | 0 | 1 | 0 |
Other | 0 | 0 | 0 | 0 | 1 |
위와 같은 작업을 직접 코딩할 수도 있지만, pandas의 get_dummies를 사용하여 쉽게 해결 할 수 있습니다.
총 5개의 카테고리니, one-hot encoding을 하고 나면 새로운 5개의 column이 생겨납니다.
Initial 을 prefix로 두어서 구분이 쉽게 만들어 줍니다.
df_train = pd.get_dummies(df_train, columns=['Initial'], prefix='Initial')
df_test = pd.get_dummies(df_test, columns=['Initial'], prefix='Initial')
df_train.head()
보다시피 오른쪽에 우리가 만들려 했던 one-ht encoded columns가 생성된 것이 보입니다. Embarked에도 적용해보도록 하겠습니다. Initial 때와 마찬가지로 one-hot encoding을 사용해 표현해보겠습니다.
df_train = pd.get_dummies(df_train, columns=['Embarked'], prefix='Embarked')
df_test = pd.get_dummies(df_test, columns=['Embarked'], prefix='Embarked')
아주 쉽게 one-hot encoding을 적용했습니다. sklearn로 Labelencoder + OneHotencoder 이용해도 one-hot encoding이 가능합니다.
다른 튜토리얼에서 사용을 해보겠습니다. 여기서는 get_dummies로 충분히 가능하기 때문에 get_dummies 만으로 끝내도록 하겠습니다.
가끔 category가 100개가 넘어가는 경우가 있습니다. 이 때 one-hot encoding을 사용하면 column이 100개가 생겨, 학습에 매우 버거울 경우가 있습니다. 이런 경우는 다른 방법을 사용하는데, 이는 다음에 다뤄보겠습니다.
3.5 Drop columns
이제 engineering을 끝냈고, 책상을 치워보겠습니다. 필요한 columns만 남기고 다 지우도록 하겠습니다.
df_train.drop(['PassengerId', 'Name', 'SibSp', 'Parch', 'Ticket', 'Cabin'], axis = 1, inplace=True)
df_test.drop(['PassengerId', 'Name', 'SibSp', 'Parch', 'Ticket', 'Cabin'], axis = 1, inplace=True)
df_train.head()
df_test.head()
위 두 줄로 train의 Survived feature(target class)를 빼면 train, test 둘 다 같은 columns를 가진것을 확인할 수 있습니다.
4. Building machine learning model and prediction using the trained model
이제 준비가 다 되었으니 sklearn을 사용해 본격적으로 머신러닝 모델을 만들어 봅시다.
#importing all the required ML packages
from sklearn.ensemble import RandomForestClassifier # 유명한 randomforestclassfier 입니다.
from sklearn import metrics # 모델의 평가를 위해서 씁니다.
from sklearn.model_selection import train_test_split # training set을 쉽게 나눠주는 함수입니다.
sklearn은 머신러닝의 처음부터 끝까지가 다 있습니다. feature engineering, preprocessing, 지도 학습 알고리즘, 비지도 학습 알고리즘, 모델 평가, 파이프라인 등 머신러닝에 관련된 모든 작업들이 손쉬운 인터페이스로 구현되어 있습니다. 데이터 분석 + 머신러닝을 하고 싶다면, 이 라이브러리는 반드시 숙지해야합니다.
파이썬 라이브러리를 활용한 머신러닝 (Introduction to machine learning with Python) 책을 사서 공부하시길 매우 추천드립니다.
지금 타이타닉 문제는 traget class(survived)가 있으며, target class는 0, 1로 이루어져 있으므로(binary) binary classification 문제이니다. 우리가 지금 가지고 있는 train set의 survived를 제외한 input을 가지고 모델을 최적화시켜서 각 샘플(탑승객)의 생존유무를 판단하는 모델을 만들어 냅니다.
그 후 모델이 학습하지 않았던 test set을 input으로 주어서 test set의 각 샘플(탑승객)의 생존 유무를 예측해봅니다.
4.1 Preparation - Split dataset into train, valid, test set
가장 먼저 학습에 쓰일 데이터와 traget label(Survived)를 분리합니다. drop을 사용해 간단히 할 수 있습니다.
X_train = df_train.drop('Survived', axis=1).values
# X_train에 'Survived' Column을 제외한 나머지들을 전부 values로 집어 넣는 것입니다.
# values를 처리하면 array 형태로 변환됩니다.
target_label = df_train['Survived'].values
# target_label에 'Survived' values의 값만 넣는 것입니다.
X_test = df_test.values
보통 train, test만 언급되지만, 실제 좋은 모델을 만들기 위해서 우리는 valid set을 따로 만들어서 모델 평가를 합니다. 마치 축구대표팀이 팀훈련(train)을 하고 월드컵(test)으로 나가는 것이 아니라, 팀훈련(train)을 한 다음 평가전(valid)를 거쳐 팀의 훈련 정도(학습정도)를 확인하고 월드컵(test)에 나가는 것과 비슷합니다.
train_test_split을 사용하여 쉽게 train 셋을 분리할 수 있습니다.
X_tr, X_vld, y_tr, y_vld = train_test_split(X_train, target_label, test_size=0.3, random_state=2018)
sklearn에서는 여러 머신러닝 알고리즘을 지원해줍니다. 열거하기엔 너무 많으므로, 직접 documentation에 들어가 보시길 추천합니다. https://scikit-learn.org/stable/
본 글에서는 랜덤 포레스트 모델을 사용하도록 하겠습니다. 랜덤포레스트는 Decision Tree 모델이며, 여러 Decision Tree를 앙상블한 모델입니다. 더 구체적인 모델 설명은 여러 블로그들을 참고하시면 될 것입니다.
각 머신러닝 알고리즘엔 여러 파라미터들이 있습니다. 랜덤포레스트분류기도 n_estimators, max_features, max_depth, min_samples_split, min_samples,leaf 등 여러 파라미터들이 존재합니다. 이것들이 어떻게 세팅되냐에 따라 같은 데이터셋이라 하더라도 모델의 성능이 달라집니다.
파라미터 튜닝은 시간, 경험, 알고리즘에 대한 이해 등이 필요합니다. 결국 많이 써봐야 모델도 잘 세울 수 있는 것이죠. 그래서 캐글을 추천합니다 ! 여러 데이터셋을 가지고 모델을 이리저리 써봐야 튜닝하는 감이 생기니까요 !
일단 지금은 파라미터 튜닝은 뒤로 미루고, 기본 default 세팅으로 진행하겠습니다.
모델 객체를 만들고, fit 메소드로 학습시킵니다. 그런 후 valid set input을 넣어주어 예측값(X_vld sample(탑승객)의 생존여부)를 얻습니다.
4.2 Model generation and prediction
model = RandomForestClassifier()
model.fit(X_tr, y_tr)
prediction = model.predict(X_vld)
단, 세 줄로 모델을 세우고, 예측까지 해보았습니다. 그렇다면 모델의 성능을 한번 살펴보도록 하겠습니다.
print('총 {}명 중 {:.2f}% 정확도로 생존을 맞춤'.format(y_vld.shape[0], 100 * metrics.accuracy_score(prediction, y_vld)))
아무런 파라미터 튜닝을 하지 않았는데 82.84% 정확도가 나왔습니다 !
4.3 Feature importance
학습된 모델은 feature importance를 가지게 되는데, 우리는 이것을 확인하여 지금 만든 모델이 어떤 feature에 영향을 많이 받았는 지 확인할 수 있습니다.
쉽게 말해, 10 = 4*(x1) + 2*(x2) + 1*(x3)을 생각한다면 우리는 x1이 결과값에 큰 영향을 준다고 생각할 수 있습니다. feature importance는 4, 2, 1을 이야기하며, x1이 가장 큰 값(4)을 가지므로, 이 모델에 가장 큰 영향을 미친다고 말할 수 있습니다.
학습된 모델은 기본적으로 feature importances를 가지고 있어서 쉽게 그 수치를 얻을 수 있습니다.
pandas series를 이용하면 쉽게 sorting을 하여 그래프를 그릴 수 있습니다.
from pandas import Series
feature_importance = model.feature_importances_
Series_feat_imp = Series(feature_importance, index=df_test.columns)
plt.figure(figsize=(8, 8))
Series_feat_imp.sort_values(ascending=Ture).plot.barh()
plt.xlabel('Feature importance')
plt.ylabel('Feature')
plt.show()
우리가 얻은 모델에서는 Fare가 가장 큰 영향력을 가지며, 그 뒤로 Initial_2, Pclass, Age_cat 순으로 중요도를 가집니다. 사실 feature importance는 지금 모델에서의 importance를 나타냅니다. 즉, 모델이 바뀐다면 feature importance가 다르게 나올 수 있습니다.
이 feature importance를 보고 실제로 Fare가 중요한 feature일 수 있다고 판단을 내릴 수는 있지만, 이것은 모델에 귀속되는 하나의 결론이므로 통계적으로 좀 더 살펴보긴 해야합니다.
feature importance를 가지고 좀 더 정확도가 높은 모델을 얻기 위해 feature selction을 할 수도 있고, 좀 더 빠른 모델을 위해 feature를 제거할 수 있습니다.
4.4 Prediction on Test set
모델이 학습하지 않았던(보지 않앗던) 테스트 셋을 모델에 주어서, 생존여부를 예측해보겠습니다. 이 결과는 실제로 submission(제출용) 이므로 결과는 leaderboard에서 확인할 수 있습니다.
캐글에서 준 파일, gender_submission.csv 파일을 읽어서 제출 준비를 하겠습니다.
submission = pd.read_csv('input/gender_submission.csv')
submission.head()
이제 testset에 대하여 예측을 하고, 결과를 csv 파일로 저장해보도록 하겠습니다.
prediction = model.predict(X_test)
submission['Survived'] = prediction
submission.to_csv('./my_first_submission.csv', index=False)
이제 캐글에 제출을 해보도록 하겠습니다.
5. Conclusion
수고하셨습니다. titanic dataset을 가지고 data science를 경험해봤습니다. 앞으로 배울 것이 너무나도 무궁무진하다는 것이 걱정이 될 수 있습니다. 좀 더 참신한 feature engineering, 머신러닝 모델 hyperparameter tunning, ensembling 등 무궁무진합니다.
포기하지 않고 즐기시면 괜찮습니다. 꾸진히 커널공부를 하시면 실력은 향상됩니다.
이상 Kaggle - 타이타닉 생존여부 예측 모델 생성 (2) 였습니다 ^_^
'Machine, Deep Learning > Machine, Deep Learning 실습' 카테고리의 다른 글
Kaggle - Heart Disease Dataset (2) (2) | 2019.06.15 |
---|---|
Kaggle - Heart Disease Dataset (1) (0) | 2019.06.14 |
Kaggle - MINST 예측 모델 생성 by Keras (2) (2) | 2019.06.10 |
Kaggle - MINST 예측 모델 생성 by Keras (1) (0) | 2019.06.09 |
Kaggle - 타이타닉 생존여부 예측 모델 생성 (1) (3) | 2019.06.07 |