반응형
- 이 공부는 다음 멋진 책과 함께 합니다. 위키북스와 저자님 너무나도 감사합니다.
kaggle
- kaggle은 오픈된 데이터를 기반으로 누구나 자신의 데이터 분석 실력을 보여줄 수 있는 포털입니다.
- 저도 코드 공부할때 많이 참고합니다.
- 여기서 제공하는 유명한 데이터 셋 중에 타이타닉 생존자 데이터가 있습니다.
- 이번 포스팅에서는 사이킷런으로 타이타닉 생존자를 예측해보겠습니다.
데이터 다운로드
- https://www.kaggle.com/c/titanic/data 에 접속하여 데이터를 다운로드합니다.
- 여러분들이 사용하는 주피터 노트북이나 코랩 등에 데이터를 저장합니다.
- 저는 train.csv / test.csv / gender_submission.csv로 저장하였습니다.
라이브러리 및 데이터 불러오기
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
titanic_df = pd.read_csv('data/ML_6_Titanic/train.csv')
titanic_df.head(2)
- 데이터의 컬럼의 의미를 파악해봅시다.
- PassengerId : 탑승자 데이터 일련번호
- Survived : 생존여부(0 = 사망, 1 = 생존)
- Pclass : 티켓의 선실 등급(1, 2, 3)
- Name : 탑승자 이름
- Sex : 탑승자 성별
- Age : 탑승자 나이
- SibSp : 탑승자와 함께 탑승한 형제자매 또는 배우자 인원수
- Parch : 같이 탑승한 부모님 또는 어린이 인원수
- Ticket : 티켓번호
- Fare : 요금
- Cabin : 선실 번호
- Embarked : 중간 정착 항구(C, Q, S) - info()를 통해 데이터에 대한 더 많은 정보를 확인할 수 있습니다.
- 이 데이터는 891개의 row와 12개의 column으로 구성되어 있습니다.
- 12개의 컬럼은 2개의 float64, 5개의 int64, 5개의 object(string타입으로 생각하기)타입으로 구성되어 있습니다.
- Age, Cabin, Embarked는 null값도 존재합니다.
데이터 가공하기 - 결측치 처리
- 숫자형 데이터 Age는 평균나이로 결측치를 처리합니다.
- 범주형 데이터 Cabin, Embarked는 N으로 결측치를 처리합니다.
# Age - 평균나이
titanic_df['Age'].fillna(titanic_df['Age'].mean(), inplace=True)
# Cabin, Embarked - N
titanic_df['Cabin'].fillna('N', inplace=True)
titanic_df['Embarked'].fillna('N', inplace=True)
- 결측치 확인 코드는 아래와 같습니다. 원하는 출력형태로 쓰시면 됩니다.
데이터 가공하기 - 범주형 데이터 결측치 처리
- Name, Ticket, PassengerID는 불필요한 피처이므로 제거합니다.
- 각자의 고유값이라 학습의미가 없습니다. ☞ 패턴이 나오지 않습니다.
- value_counts()로 분류를 간단하게 확인할 수 있습니다.
print(titanic_df['Sex'].value_counts())
print('\n', titanic_df['Cabin'].value_counts())
print('\n', titanic_df['Embarked'].value_counts())
- Cabin의 경우 속성값을 정리해야 할 필요가 있습니다. 맨 앞 알파벳이 중요한 것으로 보이니, 앞 문자만 추출하겠습니다.
# Cabin에서 앞 알파벳만 추출하기
titanic_df['Cabin'] = titanic_df['Cabin'].str[:1]
titanic_df['Cabin'].head(3)
탐색적 데이터 분석
- 사고가 날 경우 여성과 어린이, 노약자가 제일 먼저 구조 대상입니다.
- 3등실에 탄 가난한 이들은 우선 순위에서 밀렸을 것입니다.
- 성별이 생존 확률과 어떤 관련이 있는지 살펴보겠습니다.
titanic_df.groupby(['Sex', 'Survived'])['Survived'].count()
# 시각화
sns.barplot(data = titanic_df, x='Sex', y='Survived')
- 탑승객은 남자가 더 많았지만 생존자는 여자가 더 많습니다. 여자는 약 74.2% 생존, 남자는 약 18.8% 생존했습니다.
- 객실 등급 별 생존 확률을 살펴보겠습니다.
sns.barplot(data = titanic_df, x = 'Pclass' , y = 'Survived', hue='Sex')
- 여자의 경우 1, 2등실에 따른 생존 확률의 차이가 크지 않지만 3등실일 때 생존 확률이 상대적으로 많이 감소합니다.
- 남자의 경우 1등실의 생존 확률이 2, 3등실의 경우보다 상대적으로 높습니다.
- 이제 나이에 따른 생존 확률을 확인하겠습니다.
- 나이를 범위로 분류한 후 생존 확률을 확인하겠습니다.
# 입력 age에 따라 구분 값을 반환하는 함수 설정
def get_category(age):
cat = ''
if age <= -1 : cat = 'Unknown'
elif age <= 5 : cat = 'Baby'
elif age <= 12 : cat = 'Child'
elif age <= 18 : cat = 'Teenager'
elif age <= 25 : cat = 'Student'
elif age <= 35 : cat = 'Young Adult'
elif age <= 60 : cat = 'Adult'
else : cat = 'Elderly'
return cat
# x축 값을 순차적으로 표시하기 위한 설정
group_names = ['Unknown', 'Baby', 'Child', 'Teenager', 'Student', 'Young Adult', 'Adult']
# lambda 식에 위에서 만든 함수를 반환값으로 지정
titanic_df['Age_cat'] = titanic_df['Age'].apply(lambda x : get_category(x))
print(titanic_df[['Age', 'Age_cat']].head(1))
- 이렇게 print()로 중간 점검을 했다면(생략해도 됩니다.) 시각화를 진행하겠습니다.
fig, ax = plt.subplots(figsize = (10, 6))
sns.barplot(data = titanic_df, x = 'Age_cat', y = 'Survived', hue = 'Sex', order = group_names)
plt.show()
- 여자 Child는 다른 연령대에 비해 생존 확률이 낮습니다.
- 지금까지 분석한 결과 Sex, Age, PClass 등이 생존을 좌우하는 피처임을 확인할 수 있었습니다.
머신러닝 - 레이블 인코딩
- 레이블 인코딩을 적용하여 문자열 카테고리 피처를 숫자형으로 변환하겠습니다.
from sklearn.preprocessing import LabelEncoder
def encode_features(dataDF):
features = ['Cabin', 'Sex', 'Embarked']
for feature in features:
le = LabelEncoder()
le = le.fit(dataDF[feature])
dataDF[feature] = le.transform(dataDF[feature])
return dataDF
titanic_df = encode_features(titanic_df)
titanic_df.head(3)
- 지금까지 피처를 가공한 내역을 정리하고 이를 함수로 만들어 재사용할 수 있도록 만들겠습니다.
- 함수만드는 것이 편해지면 코딩할 때 유용하게 사용할 수 있습니다.(재사용이 얼마나 좋은걸요~)
# Null 처리
def fillna(df):
df['Age'].fillna(df['Age'].mean(), inplace = True)
df['Cabin'].fillna('N', inplace = True)
df['Embarked'].fillna('N', inplace = True)
df['Fare'].fillna(0, inplace = True)
return df
# 머신러닝 알고리즘에 불필요한 피처 제거
def drop_features(df):
df.drop(['PassengerId', 'Name', 'Ticket'], axis = 1, inplace = True)
return df
# 레이블 인코딩 수행
def format_features(df):
df['Cabin'] = df['Cabin'].str[:1]
features = ['Cabin', 'Sex', 'Embarked']
for feature in features:
le = LabelEncoder()
le = le.fit(df[feature])
df[feature] = le.transform(df[feature])
return df
# 앞에서 설정한 데이터 전처리 함수 호출
def transform_features(df):
df = fillna(df)
df = drop_features(df)
df = format_features(df)
return df
머신러닝 - 데이터 준비
- 원본 데이터를 재로딩하여 transform_features()함수로 다시 가공하겠습니다.
- 그리고 나서 Survived 속성만 별도로 분리하여 클래스 결정값 데이터 세트로 만들겠습니다.
# 원본 데이터 재로딩 후 Survived 속성만 분리, 피처 데이터 세트와 레이블 데이터 세트 추출
df = pd.read_csv('data/ML_6_Titanic/train.csv')
y_df = df['Survived']
X_df = df.drop('Survived', axis = 1)
X_df = transform_features(X_df)
- 다음과 같이 항상 두 shape이 일치하는지 확인합니다.
- X_df는 다음과 같습니다.
- 훈련 데이터와 테스트 데이터(전체의 20%)로 분리합니다.
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
X_df,
y_df,
test_size = 0.2,
random_state = 11
)
- 분리가 잘 되었는지 확인합니다.
머신러닝 - 알고리즘 호출
- 알고리즘으로 타이타닉 생존자를 예측해보겠습니다.
- train_test_split() ▶ fit(학습) ▶ predict(예측) ▶ accuracy_score()(평가 척도는 정확도)
- LogisticRegression의 생성 인자로 입력된 solver = 'liblinear'는 로지스틱 회귀의 최적화 알고리즘을 liblinear로 설정하는 것입니다. 일반적으로 작은 데이터 세트에서의 이진 분류는 liblinear가 성능이 약간 더 좋은 경향이 있습니다.
- random)state=11은 예제를 수행할 때마다 같은 결과를 출력하기 위한 용도이므로 실제 사례에서는 제거해도 됩니다.
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
# 결정트리, 랜덤포레스트, 로지스틱 회귀를 위한 사이킷런 Classifier 클래스 생성
dt_clf = DecisionTreeClassifier(random_state=11)
rf_clf = RandomForestClassifier(random_state=11)
lf_clf = LogisticRegression(solver = 'liblinear')
# DecisionTreeClassifier 학습/예측/평가
dt_clf.fit(X_train, y_train)
dt_pred = dt_clf.predict(X_test)
print('DecisionTreeClassifier 정확도: {0:.4f}'.format(accuracy_score(y_test, dt_pred)))
# RandomForestClassifier 학습/예측/평가
rf_clf.fit(X_train, y_train)
rf_pred = rf_clf.predict(X_test)
print('RandomForestClassifier 정확도: {0:.4f}'.format(accuracy_score(y_test, rf_pred)))
# LogisticRegression 학습/예측/평가
lf_clf.fit(X_train, y_train)
lf_pred = lf_clf.predict(X_test)
print('LogisticRegression 정확도: {0:.4f}'.format(accuracy_score(y_test, lf_pred)))
- 3개 알고리즘 중에서는 로지스틱 회귀가 가장 높은 정확도를 나타내고 있습니다.
- 아직 최적화 작업을 수행하지 않았고, 데이터 양도 충분하지 않아서 어느 알고리즘의 성능이 가장 좋다고 평가하기는 이릅니다.
- 참고로 https://scikit-learn.org/stable/supervised_learning.html사이트에 들어가면 여러 알고리즘을 소개하고 있습니다. 다음과 같이 정확도를 확인할 수 있습니다. 여러분들도 해보세요!
머신러닝 - 교차 검증
- KFold 클래스를 이용하여 교차 검증을 수행하겠습니다.
from sklearn.model_selection import KFold
def exec_kfold(clf, folds=5):
# 폴드 세트가 5개인 KFold 객체 생성, 폴드 수만큼 예측 결과 저장을 위한 리스트 객체 생성
kfold = KFold(n_splits = folds)
scores = []
# KFold 교차 검증 수행
for iter_count, (train_index, test_index) in enumerate(kfold.split(X_df)):
# X_df 데이터에서 교차 검증별로 학습과 검증 데이터를 가리키는 index 생성
X_train, X_test = X_df.values[train_index], X_df.values[test_index]
y_train, y_test = y_df.values[train_index], y_df.values[test_index]
# Classifier 학습, 예측, 정확도 계산
clf.fit(X_train, y_train)
predictions = clf.predict(X_test)
accuracy = accuracy_score(y_test, predictions)
scores.append(accuracy)
print("교차 검층 {0} 정확도: {1:.4f}".format(iter_count, accuracy))
# 5개 fold에서의 평균 정확도 계산
mean_score = np.mean(scores)
print("평균 정확도: {0:.4f}".format(mean_score))
# exec_kfold 호출
exec_kfold(dt_clf, folds=5)
- 이번에는 cross_val_score()를 이용해 수행하겠습니다.
from sklearn.model_selection import cross_val_score
scores = cross_val_score(dt_clf, X_df, y_df, cv=5)
for iter_count, accuracy in enumerate(scores):
print("교차 검층 {0} 정확도: {1:.4f}".format(iter_count, accuracy))
print("평균 정확도: {0:.4f}".format(np.mean(scores)))
- 위 두 교차 검증의 평균 정확도가 약간 다른 것은, cross_val_score()가 StratifiedKFold를 이용해 폴드 세트를 분할하기 때문입니다.(평균 정확도가 약간 더 높지요?)
머신러닝 - 최적 하이퍼 파라미터 찾기(GridSearchCV)
- 마지막으로 GridSearchCV를 이용하여 DecisionTressClassifier의 최적 하이퍼 파라미터를 찾고 예측 성능을 측정해보겠습니다.
from sklearn.model_selection import GridSearchCV
parameters = {'max_depth': [2, 3, 5, 10],
'min_samples_split': [2, 3, 5],
'min_samples_leaf': [1, 5, 8]}
grid_dclf = GridSearchCV(dt_clf, param_grid = parameters, scoring = 'accuracy', cv = 5)
grid_dclf.fit(X_train, y_train)
print('GridSearchCV 최적 하이퍼 파라미터: ', grid_dclf.best_params_)
print("GridSearchCV 최고 정확도: {0:.4f}".format(grid_dclf.best_score_))
best_dclf = grid_dclf.best_estimator_
# GridSearchCV의 최적 하이퍼 파라미터로 학습된 Estimator로 예측 및 평가 수행
dpredictions = best_dclf.predict(X_test)
accuracy = accuracy_score(y_test, dpredictions)
print("테스트 세트에서의 DecisionTreeClassifier 정확도: {0:.4f}".format(accuracy))
- 최적 하이퍼 파라미터로 DecisionTreeClassifier로 학습시킨 후 예측 정확도가 약 87.15%로 향상되었습니다.
- 원래보다 8%이상 증가했는데 아마 테스트용 데이터 세트가 작기 때문에 수치상으로 예측 성능이 많이 증가한 것처럼 보입니다.
지금까지 머신 러닝 프로세스의 모든 단계를 타이타닉 데이터 셋을 이용하여 구현해보았습니다.
우선 흐름을 익히면 실전에서도 충분히 적용할 수 있을 것입니다.
오늘도 감사합니다.
'Python > Machine Learning' 카테고리의 다른 글
[ML]_분류 모형의 평가 지표 - Confusion Matrix, ROC 곡선 (2) | 2023.10.14 |
---|---|
[ML]_Feature Scaling : 변수 값의 범위 맞추기 (0) | 2023.10.01 |
[ML]_데이터 전처리 >> 데이터 인코딩(Label & One-Hot) (0) | 2023.08.28 |
[ML]_하이퍼 파라미터 튜닝(GridSeachCV) (0) | 2023.08.27 |
[ML]_파라미터도 있는데 하이퍼 파라미터는 또 뭐야? (0) | 2023.08.27 |