머신러닝 대회 풀이

[1주차] Binary classification: Tabular data 1st level ( Titanic: Machine Learning from Disaster) - Tutoria

미역청 2023. 3. 18. 03:47

본 글은 이유한님의 Kaggle-KR 강의를 학습하여 작성하였습니다.

 

이유현님의 강의 링크

https://kaggle-kr.tistory.com/17?category=868316 

 

타이타닉 튜토리얼 1 - Exploratory data analysis, visualization, machine learning

My_kernel_chapter_data_check_EDA /*!** Twitter Bootstrap**//*! * Bootstrap v3.3.7 (http://getbootstrap.com) * Copyright 2011-2016 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) *//*! normalize.css v3.0.3 | MIT Li

kaggle-kr.tistory.com

Titanic: Machine Learning from Disaster 문제 링크

https://www.kaggle.com/competitions/titanic

 

Titanic - Machine Learning from Disaster | Kaggle

 

www.kaggle.com

목차

1. Machine Learning 과정

2. Library 소개

3. Titanic dataset 소개

4. 탐색적 데이터 분석

 

1. Machine Learning 과정

오늘은 튜토리얼이므로 화살표 표시된 부분만 다루어 보겠습니다.

2. Library 소개

Titanic의 dataset을 분석하기 위해 사용한 라이브러리

딥러닝 모델을 만들기 위해선 주어진 dataset을 분석해야한다.

- numpy 라이브러리: 행렬 등을 더 쉽게 계산하도록 돕는 라이브러리 (C++로 따지면 cmath정도 되겠습니다.)

- pandas 라이브러리: 주어진 dataset을 알맞게 처리하는 데이터분석 라이브러리

- matplotlib 라이브러리: data를 시각화할 수 있도록, 그래프를 그리게 해주는 라이브러리

- missingno 라이브러리: null data를 처리해주는 라이브러리

  (* null data의 처리: 대부분의 알고리즘은 null data를 입력받으면 오류가 발생합니다. 물론 null data를 사용하는 경우도 있으나, 찾아보기 어렵습니다. 따라서 missingno 라이브러리 내 메소드를 활용하여 null data를 모두 기준에 맞게 처리해주어야 합니다. 자세한 내용은 뒤에서!)

 

3. Titanic dataset 소개

  Titanic data 분석부터 시작해보겠습니다.

Kaggle사이트에서 다운받은 dataset

Kaggle 문제 페이지에 들어가면, 위와 같은 dataset들을 다운받을 수 있습니다.

보통 Kaggle에선 testset과 trainset 두 가지 데이터를 제공해줍니다. 우리는 trainset 내의 다양한 feature들을 개별적으로 분석한 뒤, 각 feature간의 상관관계를 확인합니다.

이 때, 이 데이터들을 그래프나 표로 나타내 관찰하며 통계적 insight를 얻을 수 있습니다.

분석한 데이터를 기반으로 머신러닝 모델을 만든 후에는 testset를 모델에 넣어보고, 나온 답을 실제 답안과 비교해보며 이 모델의 성능을 평가하면 됩니다.

 

traincase를 뜯어보면 아래와 같습니다.

그런데 이 데이터는 현재 바로 사용할 수 없습니다. 아직 null data를 처리해주지 않았기 때문입니다.

 

따라서 먼저 null data의 분포를 확인해준 뒤, missingno의 메소드를 활용하여 null data를 처리해주겠습니다.

for col in df_train.columns:
    msg = 'column:{:>10}\t Percent of NaN value: {:.2f}%'.format(col, 100*(df_train[col].isnull().sum() / df_train[col].shape[0]))
    print(msg)

0%가 나온 category feature에는 null 값이 없습니다.

Age, Cabin, Embarked feature에만 null data가 존재하는 것을 볼 수 있습니다. 이를 처리해주겠습니다.

처리가 끝났으면 이제 본격적으로 데이터를 분석해봅시다. 

 

Step 1. category feature을 확인합니다.

  이 데이터의 category feature은 Survival, Pclass, Sex, Age, Embarked, SibSp, ParCh, Ticket, Fare, Cabin입니다.

  (Pclass - 좌석 등급, Embarked - 승선위치, SibSp - 동승한 형제자매나 배우자의 수 , Parch - 동승한 부모나 자식의 수)

 

Step 2. 이 중 예측하고자 하는 feature를 골라냅니다.

  이 문제에서는 주어진 사람이 생존하였는가를 예측해야합니다. 즉, 예측하고자 하는 feature은 Survival입니다.

 

Step 3. Target Level 확인

  Target Level이란, 예측하고자 하는 category feature(=Target feature)에 따른 data의 분포도를 뜻합니다.  Target feature을 설정하면, Target Level을 가장 먼저 필수적으로 확인해주어야합니다.

f, ax = plt.subplots(1, 2, figsize=(18, 8))

df_train['Survived'].value_counts().plot.pie(explode=[0, 0.1], autopct='%1.1f%%', ax=ax[0], shadow=True)
ax[0].set_title('Pie plot - Survived')
ax[0].set_ylabel('')
sns.countplot('Survived', data=df_train, ax=ax[1])
ax[1].set_title('Count plot - Survived')
plt.show()

Survived의 분석 결과

전체 탑승자 중 38.4%만이 살아남았습니다. 즉, 예측하고자하는 feature값이 균일한 분포를 보이고 있습니다.

만약 traincase에서 예측하고자 하는 feature의 값이 한쪽으로 치우친, 불균등한 분포를 보인다면 이 점을 구현에 반영해주어야하기 때문에 이 과정은 필수입니다.

traincase에서 전원이 사망했다면, 각 category feature에 따라 다르게 예측하는 딥러닝 모델을 만들 필요 없이 모두가 죽었다고 출력하는 알고리즘만 만들어줘도 높은 예측률을 보일 것이기 때문입니다. 

 

Step 4. 남은 feature 중 예측을 하는 데에 유효한 feature을 골라냅니다.

  주어진 데이터를 표나 그래프로 시각화하여 주어진 feature 중 예측하는 데에 도움을 줄 feature을 추려내는 과정입니다. 이 과정에서 통계학을 배운 분이라면 통계학적 지식을 발휘해도 좋지만, 그렇지 않은 경우 단순한 직관을 활용해도 좋습니다.

  이번 주차는 튜토리얼인만큼, 유효한 feature를 공개하고 시작하겠습니다.

  예측하는 데에 유효한 feature: Pclass, sex, age, SibSp, ParCh, Fare

  

 

4. 탐색적 데이터 분

이제 각 category에 따라 데이터를 분석해보며, 이 머신러닝 모델의 개발 방향을 잡아보겠습니다.

(※ 이번 주차는 튜토리얼이므로 각 메소드, 코드의 분석보다는 category feature에 따른 결과의 변화를 확인하여 최종적으로 통계학적 통찰을 얻는 데에 방점을 두겠습니다.)

 

a) Pclass & Sex & Age

i. Pclass

  Pclass는 좌석등급으로, 1등급부터 점점 순위가 내려가는 서수형 데이터입니다.

y_position = 1.02
f , ax = plt.subplots(1,2, figsize=(18,8))
df_train['Pclass'].value_counts().plot.bar(color=['#CD7F32', '#FFDF00', '#D3D3D3'], ax=ax[0])
ax[0].set_title('Number of passengers By Pclass', y=y_position)
ax[0].set_ylabel('Count')
sns.countplot('Pclass', hue='Survived',data=df_train, ax=ax[1])
ax[1].set_title('Pclass: Survived vs Dead', y=y_position)
plt.show()

Pclass가 1일 때 가장 생존률이 높습니다.

ii) Sex

  성별입니다. Survived와 마찬가지로 데이터값이 오직 두 가지만 존재합니다.

f, ax = plt.subplots(1,2,figsize=(18,8))   #성별에 따른 생존률 확인
df_train[['Sex','Survived']].groupby(['Sex'], as_index=True).mean().plot.bar(ax=ax[0])
ax[0].set_title("Survived vs Sex")
sns.countplot('Sex', hue='Survived',data=df_train, ax=ax[1])
ax[1].set_title('Sex: Survived vs Dead')
plt.show()

여성 생존자가 압도적으로 많으며, 사망률 역시 남성이 더 높습니다.

 

iii) Age

  탑승자의 나이입니다.

  탑승자의 나이를 표로 나타내기 위해선, 우선 Age의 maximum data와 minimum data를 조사하여, 연령대의 range를 잡아야 할 것입니다.

 

print('제일 나이많은 탑승객: {:.1f} years'.format(df_train['Age'].max()))
print('제일 어린 탑승객: {:.1f} Years'.format(df_train['Age'].min()))
print('탑승객 평균 나이: {:.1f} Years'.format(df_train['Age'].mean()))

 

제일 나이많은 탑승객: 80.0 years

제일 어린 탑승객: 0.4 Years

탑승객 평균 나이: 29.7 Years

 

> maximum data는 80, minimum data는 0.4입니다.

이 range를 기반으로 분포곡선을 그려보겠습니다.

fig, ax = plt.subplots(1,1, figsize=(18,8))
sns.kdeplot(df_train[df_train['Survived']==1]['Age'], ax=ax)
sns.kdeplot(df_train[df_train['Survived']==0]['Age'], ax=ax)  
plt.legend(['Survived ==1', 'Survived==0'])
plt.show()

 

20대-40대가 가장 많이 탑승하였으며, 전체 연령대 대비 가장 많이 생존함과 동시에 가장 많이 사망했습니다.

 

IN SUM)  Pclass & Sex & Age

  violinplot메소드를 활용하여 Pclass, Age에 따른 생존률 / Sex, Age에 따른 생존률 비교를 위한 바이올린 그래프를 그렸습니다. 

여기서, line 2의 'scale' 값에는 주로 'count'나 'area'를 넣습니다.

'area'를 넣으면 각 바이올린 그래프가 동일한 면적으로 출력되기 때문에, 같은 그래프 내에서 비율 차이를 가늠하기 쉽습니다. 'count'를 넣으면 바이올린 그래프의 면적을 데이터값과 비례하도록 그리므로, 숫자의 차이를 가늠하기 쉽습니다.

우리는 각 feature에 따른 생존자 수의 차이를 확인해야하므로 'count'를 사용하겠습니다.

f,ax = plt.subplots(1,2, figsize=(18,8))
sns.violinplot('Pclass', 'Age',hue='Survived',data=df_train, scale='count', split=True, ax=ax[0])
ax[0].set_title('Pclass and Age vs Survived')
ax[0].set_yticks(range(0,110,10))
sns.violinplot('Sex', 'Age', hue='Survived', data=df_train, scale= 'count', split= True, ax=ax[1])
ax[1].set_title('Sex and Age vs Survived')
ax[1].set_yticks(range(0,110,10))
plt.show()

Pclass 각 연령대의 생존률, 성별과 연령대별로 생존률을 한눈에 볼 수 있습니다.

b) Embarked

  승선장소입니다. (다음 챕터에서 나오지만, 이 feature은 모델 성능 향상에 도움을 주지 않습니다.)

  이 feature는 S, Q, C 오직 3가지 값만을 갖습니다.

 

df_train[['Embarked', 'Survived']].groupby(['Embarked'], as_index=True).mean().sort_values(by='Survived')

 

Embarked feature에 따른 생존률을 출력해보았습니다.

 

Embarked와 다른 feature간의 관계를 파악하기 위해 가로축을 Embarked, 세로축을 Pclass, Sex, Age, Survived로 설정한 막대그래프를 출력합니다.

f, ax =plt.subplots(2,2,figsize=(20,15))
sns.countplot('Embarked', data=df_train, ax=ax[0,0])
ax[0,0].set_title('(1) No. Of passengers aboard')

sns.countplot('Embarked', hue='Sex', data= df_train, ax=ax[0,1])
ax[0,1].set_title('(2) Male-Female split for embarked')

sns.countplot('Embarked', hue ='Survived', data=df_train, ax=ax[1,0])
ax[1,0].set_title("(3) Embarked vs Survived")

sns.countplot('Embarked', hue = 'Pclass', data=df_train, ax= ax[1,1])
ax[1,1].set_title("(4) Embarked vs Pclass")

plt.subplots_adjust(wspace =0.2, hspace=0.5)
plt.show()

 

Embarked와 다른feature의 관계 그래프

 

c) Family, SibSp, ParCh

  탑승자의 가족 구성원 수를 나타냅니다.

  Age feature과 동일하게, 표를 그리기 전에 Family의 Maximum size와 minimum size를 구해야합니다. 

print("Maximum size of Family: ", df_train['FamilySize'].max())
print("Maximum size of Family: ", df_train['FamilySize'].min())

Maximum size of Family: 11

Maximum size of Family: 1

 

>> range(1, 11)을 기준으로 표를 그려보겠습니다. 

f, ax= plt.subplots(1,3,figsize=(40,10))
sns.countplot("FamilySize", data =df_train, ax=ax[0])
ax[0].set_title("(1) No. of Passenger Boarded", y=1.02)

sns.countplot("FamilySize", hue='Survived',data=df_train,ax=ax[1])
ax[1].set_title('(2) Survived countplot depending on FamilySize', y=1.02)

df_train[['FamilySize', 'Survived']].groupby(['FamilySize'], as_index=True).mean().sort_values(by='Survived', ascending=False).plot.bar(ax=ax[2])
ax[2].set_title("(3) Survived rate depending on FamilySize", y=1.02)
plt.subplots_adjust(wspace=0.2, hspace=0.5)
plt.show()

4인가족의 생존률이 가장 높습니다. 8인, 11인 가족은 전원사망했네요.

 

 

d) Fare

  운임입니다.

  여기서 displot 메소드의 skew()를 짚고 넘어가겠습니다.

  skew()는 Skewness 그래프를 출력하는 메소드입니다.

  Skewness(왜도): 분포곡선 그래프가 한 쪽으로 치우친 정도를 나타낸 척도.

  Skewness가 0보다 크면 해당 값의 분포곡선이 평균을 기준으로 오른쪽으로 더 치우쳤다는 뜻입니다. 0보다 작으면 왼쪽으로 치우친 것이구요.

  참고로, Skewness와 짝꿍으로 나오는 Kurtosis(첨도)라는 개념도 있습니다. 

  Kurtosis(첨도): 분포곡선그래프가 평균으로 몰린 정도를 나타낸 척도.

flg, ax = plt.subplots(1,1,figsize=(8,8))
g = sns.distplot(df_train['Fare'], color='b', label ='Skewness: {:.2f}'.format(df_train['Fare'].skew()), ax=ax)
g = g.legend(loc='best')

왜도가 양수값입니다. 평균운임보다 높은 운임을 낸 사람들이 더 많이 살아남았습니다.