'll Hacker
[ML 실습] 10일차 복습- 사이킷런으로 타이타닉 생존자 예측모델 만들기(Kaggle) 본문
EDA
내려받은 타이타닉 탑승자 데이터 확인(칼럼=피처)
- Passengerid : 탑승자 데이터 일련번호
- survives: 생존여부, 0=사망, 1=생존
- pclass: 티켓의 선실 등급, 1=일등석, 2=이등석, 3=삼등석
- sex: 탑승자 성별
- name: 탑승자 이름
- Age: 탑승자 나이
- sibsp: 같이 탑승한 형제자매 또는 배우자 인원수
- parch: 같이 탑승한 부모님 또는 어린이 인원수
- ticket: 티켓 번호
- fare: 요금
- cabin: 선실 번호
- embarked: 중간 정착 항구 C=Cherourg, Q=Queenstown, S=southapton
import sklearn
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
titanic_df = pd.read_csv(r'.\ML\titanic_dataset\titanic_train.csv')
titanic_df.head(3)
로딩된 데이터 칼럼 타입 확인
print('\n ###학습 데이터 정보 ###\n')
print(titanic_df.info())
확인 결과,
RangeIndex는 열을 의미함. 891개의 행이 존재한다. (row=행, column=열), 12개의 컬럼이 있는 것을 볼 수 있다.
int64타입은 5개, object는 5개, float64는 2개가 있다. object타입은 string타입으로 봐도 된다.
'Age', 'Cabin', 'Embarked'는 891개가 아니다. 이 말은 즉슨, 결측치가 존재한다는 뜻이다. 따라서 'Age'는 177개, 'Cabin'는 608개, 'Embarked'는 2개의 결측치가 존재한다.
데이터 전처리
결측치 처리
DataFrame의 fillna()를 사용해 간단하게 NUALL값을 평균 또는 고정값으로 변경⏬
titanic_df['Age'].fillna(titanic_df['Age'].mean(), inplace=True)
titanic_df['Cabin'].fillna('N',inplace=True)
titanic_df['Embarked'].fillna('N',inplace=True)
print('데이터 세트 NULL값 개수',titanic_df.isnull().sum().sum())
데이터 속성 설정 오류 처리
print('Sex값 분포:\n',titanic_df['Sex'].value_counts())
print('\n Cabin 값 분포:\n',titanic_df['Cabin'].value_counts())
print('\n Embarked 값 분포:\n',titanic_df['Embarked'].value_counts())
Sex, Embarke값은 별 문제가 없지만, Cabin은 N이 687건으로 가장 많은 것도 그렇고, 속성값이 제대로 정리가 안되어있다. 예를 들어서, C23, C25, C27과 같이 여러 Cabin이 묶여있는 경우가 있다. Cabin은 선실 번호 중 선실 등급을 나타내는 첫 번째 알파벳이 중요해보인다. 그래서 앞 문자만 추출해보자.
titanic_df['Cabin']=titanic_df['Cabin'].str[:1]
print(titanic_df['Cabin'].head(3))
본격적 EDA
1. 어떤 유형의 승객이 생존 확률이 높았는가
▶️여성과 아이들, 노약자 다음 부자나 유명인
2. 성별이 생존 확률에 어떤 영향이 미쳤는지
titanic_df.groupby(['Sex','Survived'])['Survived'].count()
'Survived'는 target변수인 것 같다. 생존은 1, 사망은 0이다. 위 코드의 결과를 보니까 탑승객은 남자가 더 많고, 여성이 생존할 확률이 높다. 이것을 시각화해보면 다음과 같다.
sns.barplot(x='Sex',y='Survived',data=titanic_df)
male은 0.2에 못미치고, female은 0.7보다 높게 나왔다. 시각화는 Seaborn 패키지를 이용하였다. 이 패키지는 기본적으로 맷플롯립에 기반하고 있지만, 좀 더 세련된 비주얼과 쉬운 API, 편리한 판다스 DataFrame과의 연동 등으로 데이터 분석을 위한 시각화로 애용되고 있는 패키지이다.
3. 가난한 사람과 부자인 사람 간의 생존 확률
부를 측정할 수 있는 칼럼은 '객실 등급'일 것이다.
sns.barplot(x='Pclass',y='Survived',hue='Sex',data=titanic_df)
객실 등급과 성별을 함께 고려하여 시각화해봤다.
여성의 경우 1, 2등실에 따른 생존 확률의 차이가 크지않다. 하지만 3등실은 생존 확률이 상대적으로 적다. 남성의 경우는 1등실이 2,3등실보다 상대적으로 높다.
4. Age에 따른 생존 확률
이 컬럼은 값이 많기 때문에 카테고리별로 분류하여 값을 할당한다. 0~5세는 Baby, 6~12세는 Child, 13~18세는 Teenager, 19~25세는 Student, 26~35세는 Young Adult, 36~60세는 Adult, 61세 이상은 Elderly로 분류한다. 그리고 -1 이하의 오류값은 Unknown으로 분류한다.
# 입력 age에 따라 구분 값을 반환하는 함수 설정, DataFrame의 apply lambda 식에 사용
def get_category(age):
cat=''
if age<=-1: cat='Unknown'
elif age<=5:
cat='Baby'
elif age<=12:
cat='Child'
elif age<=18:
cat='Teenage'
elif age<=25:
cat='Student'
elif age<=35:
cat='Young Adult'
elif age<=60:
cat='Elderly'
return cat
#막대그래프의 크기 figure를 더 크게 설정
plt.figure(figsize=(10,6))
#X축의 값을 순차적으로 표시하기 위한 설정
group_names=['Unknown','Baby','Child','Teenage','Student','Young Adult','Adult','Elderly']
#lambda 식에 위에서 생성한 get_category() 함수를 반환값으로 지정
# get_category(x)는 입력값으로 'Age' 칼럼 값을 받아서 해당하는 cat 반환
titanic_df['Age_cat']=titanic_df['Age'].apply(lambda x:get_category(x))
sns.barplot(x='Age_cat',y='Survived',hue='Sex',data=titanic_df,order=group_names)
titanic_df.drop('Age_cat',axis=1,inplace=True)
여자 Baby같은 경우 비교적 생존 확률이 높았다. 여자 Elderly의 경우도 높았다. 여자 Child는 다른 연령대에 비해 생존 확률이 낮다.
분석한 토대로 Sex, Age, PClass 등이 중요하게 생존을 좌유하는 피처임을 알 수 있다.
인코딩
문자열 카테고리 피처를 숫자형 카테고리 피처로 변환한다. LabelEncoder객체는 카테고리 값의 유형 수에 따라 0~(카테고리 유형 수-1)까지의 숫자값으로 변환한다. 사이킷런의 전처리 모듈의 대부분 인코딩 API는 사이킷런의 기본 프레임워크 API인 fit(), transform()으로 데이터를 변환한다. 여러 칼럼을 encode_features() 함수를 새로 생성해 한번 변환한다.
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()
위 결과와 같이 Sex, Cabin, Embarked 속성이 숫자형으로 바뀌었다.
함수로 한번에 정리
데이터의 전처리를 전체적으로 호출하는 함수는 transform_features( ),
Null처리, 불필요한 피처 제거, 인코딩을 수행하는 함수로 구성. 불필요한 피처 제거는 drop_features(df)에서 PassengerId, Name, Ticket을 제거한다.
# 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
원본 CSV파일을 다시 로딩하고 타이타닉 생존자 데이터 세트의 레이블인 Survived 속성만 별도 분리하여 클레스 결정값 데이터 세트로 만든다. Survived 속성을 제거하여 피처 데이터 세트를 만든다. 이렇게 생성된 피처 데이터 세트에 transform_features()를 적용해 데이터를 가공한다.
# 원본 데이터를 재로딩하고, 피처 데이터 세트와 레이블 데이터 세트 추출
titanic_df = pd.read_csv(r'C:\Users\yunju\Documents\ML\titanic_dataset\titanic_train.csv')
y_titanic_df=titanic_df['Survived']
X_titanic_df=titanic_df.drop('Survived',axis=1)
X_titanic_df=transform_features(X_titanic_df)
모델학습
데이터 분리
내려받은 학습 데이터 세트를 기반으로 해서 train_test_split( ) API를 이용해 별도의 테스트 데이터 세트를 추출한다. 테스트 데이터 세트 크기는 전체의 20퍼센트로 한다.
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test=train_test_split(X_titanic_df,y_titanic_df,test_size=0.2,random_state=11)
모델 학습
Decision Tree, Random Forest, Logistic Regression을 이용한다. 이런 클래스들을 활용하여 ML 모델을 학습(fit)하고, 예측(predict)한다. 예측 성능 평가는 accuracy_score( ) API를 사용한다.
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
# 결정트리, Random Forest, 로지스틱 회구를 위한 사이킷런 Classifier 클래스 생성
df_clf=DecisionTreeClassifier(random_state=11)
rf_clf=RandomForestClassifier(random_state=11)
lr_clf=LogisticRegression(solver='liblinear')
# DecisionTreeClassifier 학습, 예측, 평가
df_clf.fit(X_train, y_train)
df_pred=df_clf.predict(X_test)
print('DecisionTreeClassifier 정확도:{0:.4f}'.format(accuracy_score(y_test,df_pred)))
# RandomForesetClassifier 학습, 예측, 평가
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)))
# LogisiticRegression 학습, 예측, 평가
lr_clf.fit(X_train,y_train)
lr_pred=lr_clf.predict(X_test)
print('LogisiticRegression 정확도:{0:.4f}'.format(accuracy_score(y_test,lr_pred)))
교차검증
사이킷런 model_selection 패키지의 KFold 클래스, cross_val_score( ), GridSearchCV 클래스를 모두 사용한다.
KFold 이용
# kfold로 교차 검증
import numpy as np
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_titanic_df)):
# X_titanic_df 데이터에서 교차 검증별로 학습과 검증 데이터를 가리키는 index 생성
X_train, X_test=X_titanic_df.values[train_index],X_titanic_df.values[test_index]
y_train, y_test=y_titanic_df.values[train_index],y_titanic_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(df_clf, folds=5)
cross_val_score( ) API 이용
# cross_val_score() 교차 검증
from sklearn.model_selection import cross_val_score
scores=cross_val_score(df_clf, X_titanic_df, y_titanic_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)))
최적 하이퍼파라미터 적용 - GridSearchCV
CV는 5개의 폴드 세트를 지정하고 하이퍼 파라미터는 max_depth, min_depth, sampels_split, min_samples_leaf를 변경하면서 성능 측정한다. 최적 하이퍼 파라미터와 그때의 예측을 출력하고, 최적 하이퍼 파라미터로 학습된 Estimator를 이용해 위의 train_test_split( )으로 분리된 테스트 데이터 세트에 예측을 수행해 예측 정확도를 출력한다.
# GridSearchCV 교차검증
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(df_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('테스트 세트에서의 DecisionTressClassifier 정확도:{0:.4f}'.format(accuracy))
87.15%로 약 8% 향상되었다. 일반적으로 하이퍼 파라미터를 튜닝하더라도 이 정도 수준으로 증가하기 어렵다고 한다.
'AI > 머신러닝' 카테고리의 다른 글
[ML 실습] 9일차 복습- 사이킷런 머신러닝 만들어보기(데이터 전처리) (0) | 2025.03.17 |
---|---|
[ML 실습] 8일차 복습- 사이킷런 기반 FrameWork 익히기 (1) | 2025.03.12 |
[ML 실습] 7일차 복습- 사이킷런 머신러닝 만들어보기(개요) (0) | 2025.03.12 |
[ML 실습] 6일차 복습- 판다스 DataFrame(3) (1) | 2025.01.28 |
[ML 실습] 5일차 복습- 판다스 DataFrame(2) (0) | 2025.01.28 |