AI/머신러닝 - 예제

[머신러닝 - 예제] 타이타닉 데이터셋 - 캐글 데이터셋

caramel-bottle 2023. 12. 25.

1. 타이타닉 데이터셋

타이타닉 데이터셋은 캐글(Kaggle)에서 다운받을 수 있다.

https://www.kaggle.com/

 

Kaggle: Your Machine Learning and Data Science Community

Kaggle is the world’s largest data science community with powerful tools and resources to help you achieve your data science goals.

www.kaggle.com

1912년 타이타닉호 침몰시 생존한 승객과 사망한 승객의 정보가 담겨있다.

각 승객의 어떤 정보가 생존여부와 관련이 있는지 데이터 분석을 해보자.


2. CSV파일 열기

제공된 데이터는 csv형식이기 때문에 pandas의 read_csv 메서드를 사용하여 csv파일을 DataFrame으로 만든다.

import numpy as np
import pandas as pd

df = pd.read_csv('https://bit.ly/fc-ml-titanic')

 

pandas의 read_csv 메서드는 해당 경로에 있는 csv파일을 읽어서 DataFrame형식으로 변환하여 반환한다. 변환 형식은 매개변수 옵션으로 조정이 가능하다.

 

얻은 DataFrame은 다음과 같다

12개의 column과 891개의 row로 이루어져있다.

 

각 컬럼에 해당하는 내용은 다음과 같다

  • PassengerId: 승객 아이디
  • Survived: 생존 여부(0: 사망, 1: 생존)
  • Pclass: 좌석 등급
  • Name: 아름
  • Sex: 성별
  • Age: 나이
  • SibSp: 형제, 자매, 배우자 수
  • Parch: 부모, 자식 수
  • Ticket: 티켓 번호
  • Fare: 요금
  • Cabin: 선실
  • Embarked: 탑승 항구

3. 데이터 전처리

필요없는 데이터를 지우고, null값이 있는 행을 처리하고, 정규화/표준화 등의 작업들을 하는 것을 데이터 전처리라고 한다.

데이터 전처리는 머신러닝에 상당한 비중을 차지하기 때문에 시간이 오래 걸릴 수 있다.


3-1. 독립변수와 종속변수 나누기

어떤 데이터가 생존여부와 관련이 있을지가 알려져있지 않다면 직접 확인해야한다.

 

독립변수와 종속변수는  column을 알기 때문에 독립변수로 사용할 column과 종속변수로 사용할 column을 변수로 만들어놓을 수 있다.

유명한 데이터셋이니, AI의 도움을 얻어보자.

성별, 나이, 승선클래스가 생존 여부에 비교적 많은 관련이 있는 것으로 보인다. 

우리는 독립변수가 될 column을 정하고 미리 리스트로 만들어둘 수 있다. 아래는 그 예시이다.

feature = ['Pclass', 'Sex', 'Age', 'Fare'] # 독립변수
label = ['Survived'] # 종속변수|

3-2. 결측치 처리

결측치 처리란 Null이 포함된 데이터를 제거하거나 적절하게 수정하는 것을 의미한다.

결측치를 확인하기 위해 info()를 통해 데이터 정보를 확인한다.

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Survived     891 non-null    int64  
 2   Pclass       891 non-null    int64  
 3   Name         891 non-null    object 
 4   Sex          891 non-null    object 
 5   Age          714 non-null    float64
 6   SibSp        891 non-null    int64  
 7   Parch        891 non-null    int64  
 8   Ticket       891 non-null    object 
 9   Fare         891 non-null    float64
 10  Cabin        204 non-null    object 
 11  Embarked     889 non-null    object 
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
 

Non-Null Count를 보니 Age, Cabin, Embarked 항목에 null값이 존재하는 것으로 보인다.

isnull()과 sum()메서드를 사용하여 null값의 개수만 볼 수 있다.

df.isnull().sum()

 

isnull()은 df의 필드중 null인 부분은 true, 아닌 부분은 false로 bool 타입으로 바꿔서 반환한다. 

sum()은 각 column의 합을 반환한다. true는 1과 같으니 true의 개수를 반환한다.

PassengerId      0
Survived         0
Pclass           0
Name             0
Sex              0
Age            177
SibSp            0
Parch            0
Ticket           0
Fare             0
Cabin          687
Embarked         2
dtype: int64
 

 

결측치를 처리하는 방법은 여러가지이다. 해당 row를 아예 지우거나, null인 부분을 해당 column의 평균으로 대체하거나, 중앙값으로 대체하거나 등등 적절하게 처리해야한다. 

Age를 평균값으로 대체해보자.

(Cabin은 학습용 데이터로 사용하지 않을 것이기 때문에 무시하고, Embarked의 경우 잠시 후에 라벨인코딩을 통해 조금 더 확인해보도록 한다.)

# 평균 나이를 구해서 데이터를 삽입 => fillna 사용
df['Age'] = df['Age'].fillna(df['Age'].mean())

 

fillna() 메서드는 NaN과 Null의 값을 다른 값으로 채워준다.

 

위처럼 함수를 사용하면 각 row마다 연산이 진행되므로 Age가 Null인 row의 'Age' column에 df['Age']의 평균값을 넣게 된다.

 

fillna()의 자세한 사용법은 pandas공식 홈페이지에서 볼 수 있다.

https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.fillna.html#pandas.DataFrame.fillna

 

pandas.DataFrame.fillna — pandas 2.1.4 documentation

If method is specified, this is the maximum number of consecutive NaN values to forward/backward fill. In other words, if there is a gap with more than this number of consecutive NaNs, it will only be partially filled. If method is not specified, this is t

pandas.pydata.org

 


3-3. 라벨 인코딩

머신러닝에는 숫자만 사용할 수 있다. 문자열이나 다른 데이터 타입의 경우 수치화 작업이 필요하다.

이를 라벨 인코딩이라고 한다. 우리가 사용할 독립변수중 숫자형이 아닌 데이터를 확인해보자.

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  891 non-null    int64  
 1   Survived     891 non-null    int64  
 2   Pclass       891 non-null    int64  
 3   Name         891 non-null    object 
 4   Sex          891 non-null    object 
 5   Age          891 non-null    float64
 6   SibSp        891 non-null    int64  
 7   Parch        891 non-null    int64  
 8   Ticket       891 non-null    object 
 9   Fare         891 non-null    float64
 10  Cabin        204 non-null    object 
 11  Embarked     889 non-null    object 
dtypes: float64(2), int64(5), object(5)
memory usage: 83.7+ KB
 

int나 float형식이 아닌 문자열 형식의 데이터가 몇몇 보인다. 이중 독립변수로 사용하기로 한 데이터는 성별(Sex)과 탑승 항구(Embarked)이다.

 

성별의 종류와 종류별 개수를 보기 위해 value_counts()메서드를 사용한다.

value_counts()는 서로 같은 값으로 구분된 행의 빈도수를 반환한다.

df['Sex'].value_counts()
male      577
female    314
Name: Sex, dtype: int64
 

male과 female로 구분된 데이터를 1과 0으로 변환해주는 작업을 해야한다.

 

apply()는 특정 컬럼의 모든 값을 변경하거나, 컬럼을 추가하고자 할 때 자주 사용되는 메서드이다.

apply()는 함수를 매개변수로 하여 각 행에 함수를 적용하고 값을 반환한다.

 

apply()에 사용될 간단한 함수를 만들어보자.

def convert_sex(data):
    if data == 'male':
        return 1
    elif data == 'female':
        return 0
        
        
        
df['Sex'] = df['Sex'].apply(convert_sex)

 

이러면 해당 컬럼의 모든 값에 convert_sex함수가 적용된 상태가 된다.

 

3-4. LabelEncoder()

다음은 sklearn.preprocessing의 LabelEncoder를 사용하여 라벨 인코딩을 해보자.

사용할 라이브러리를 import해주고 객체를 생성한다.

from sklearn.preprocessing import LabelEncoder

le = LabelEncoder()

 

라벨 인코딩을 하기 전에 성별과 마찬가지로 value_counts()를 찍어보자.

df['Embarked'].value_counts()
S    644
C    168
Q     77
Name: Embarked, dtype: int64
 

이전에 결측치를 처리하는 과정에서 Embarked항목에도 2개의 Na값이 존재함을 보았었다.

Na를 보기 위해 dropna옵션을 False로 하여 다시 출력해보자.

 

df['Embarked'].value_counts(dropna=False)
S      644
C      168
Q       77
NaN      2
Name: Embarked, dtype: int64

 

위 값들에 대해 LabelEncoder()를 사용하여 라벨 인코딩을 진행하기 위해 fit()과 transform()을 사용한다.

fit_embarked = le.fit(df['Embarked'])
transform_embarked = fit_embarked.transform(df['Embarked'])
print(transform_embarked)

fit()은 le를 특정 라벨 인코더의 기능을 하도록 학습시킨다.

transform()은 학습된 라벨 인코더가 학습한 대로 인코딩 변환을 하도록 한다.

 

학습 데이터와 변환할 데이터가 같다면 두 과정을 한번에 하는 fit_transform()을 사용할 수 있다.

embarked = le.fit_transform(df['Embarked'])
print(embarked)

 

학습된 변환 규칙은 다음과 같다.

print(le.classes_)
['C' 'Q' 'S' nan]

 

 

df에 적용하면

 

df['Embarked_num'] = LabelEncoder().fit_transform(df['Embarked'])
df.head()

Embarked_num이라는 새로운 column을 만들어서 라벨 인코딩 결과를 넣었다.


3-5. One Hot Encoding

모델이 학습하는 과정에서 데이터간에 연관성을 판단할 수 있다. 예를 들어 0, 1, 2, 3 총 4개의 데이터가 있을 때 각각의 연관성을

'3은 1과 2의 합이다'로 판단할 수 있다. 이를 방지하기 위한 것이 One Hot Encoding이다.

 

독립적인 데이터는 별도의 컬럼으로 분리하고 각각 컬럼에 해당 값에만 1, 나머지는 0으로 만드는 방법.

 

pandas의 get_dummies()를 사용하면 쉽게 One Hot Encoding을 할 수 있다.

 

pd.get_dummies(df['Embarked_num'])

 

 

DataFrame 내에서 특정 컬럼에 적용하는 방법도 있다.

df = pd.get_dummies(df, columns=['Embarked'])

아직 na값은 처리하지 않았다. 하지만 인코딩 결과를 보면 Embarked_C, Embarked_Q, Embarked_S 까지만 생성이 되었다.

na값은 어떻게 처리가 되었을지 확인하면

df.loc[df['Embarked_num'] == 3, ['Embarked_C', 'Embarked_Q', 'Embarked_S']]

 

get_dummies()의 옵션중 dummy_na라는 것이 있다. (2023년 12월 기준)

dummy_na의 기본값은 False이다.

지금은 na가 2개밖에 없으니 이 과정은 생략..


4. 학습, 예측

모델 학습을 위해 독립변수와 종속변수를 train_test_split으로 나눈다.

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(df[feature], df[label], test_size=0.2, random_state=2023)
# feature = ['Pclass', 'Sex', 'Age', 'Fare'] # 독립변수
# label = ['Survived'] # 종속변수|

 

학습과 예측을 진행한다.

보기는 2개이기 때문에 아이리스 데이터셋 예제에서 사용한 것과 같은 SVC를 사용하여 예측해보았다.

from sklearn.svm import SVC # classification class
from sklearn.metrics import accuracy_score

svc = SVC()

svc.fit(X_train, y_train)

y_pred = svc.predict(X_test)
print("정답률: ", accuracy_score(y_test, y_pred))
정답률:  0.6480446927374302

 

이번 모델은 10개중에 6개를 맞추는 정확도로 답안을 제출하였다.

더 많은 데이터와 독립변수를 사용한다면 정확도가 올라갈 것이다.

 


끝내며

데이터의 종류와 전처리 결과에 따라 적절한 머신러닝 방식을 사용하여 train과 test를 진행해야한다.

예제로서 아이리스 데이터셋과 같은 SVC로 진행했지만 더 좋은 결과를 내기 위한 방법을 나중에 찾아보도록 하자!

 

댓글