개요
머신러닝 4차 과제의 주제인 타이타닉의 생존자 예측을 위한 모델 훈련 과정을 요악한 글입니다.
📘 Colab / Kaggle
우선 데이터 전처리 및 모델 훈련에 필요한 라이브러리를 import합니다.
1
2
3
4
5
6
7
8
9
10
| import numpy as np
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
from sklearn.linear_model import LogisticRegression # 로지스틱 회귀
from sklearn.svm import SVC # 서포트 벡터 머신
from sklearn.neighbors import KNeighborsClassifier # K-최근접 이웃 분류기
from sklearn.ensemble import RandomForestClassifier # 랜덤 포레스트 분류기
from sklearn.naive_bayes import GaussianNB # 나이브 베이즈 분류기
|
준비 운동
Kaggle 노트북을 생성했다면, 가장 먼저 할 일은 데이터셋을 적재하는 것입니다. CSV 파일은 여기에서 가져올 수 있습니다.
head()
메서드를 사용해 우리가 사용할 데이터셋을 대략적으로 살펴보겠습니다.
1
2
3
| train = pd.read_csv('../input/dataset/train.csv')
test = pd.read_csv('../input/dataset/test.csv')
train.head()
|
|
PassengerId |
Survived |
Pclass |
Name |
Sex |
Age |
SibSp |
Parch |
Ticket |
Fare |
Cabin |
Embarked |
0 |
1 |
0 |
3 |
Braund, Mr. Owen Harris |
male |
22.0 |
1 |
0 |
A/5 21171 |
7.2500 |
NaN |
S |
1 |
2 |
1 |
1 |
Cumings, Mrs. John Bradley (Florence Briggs Th... |
female |
38.0 |
1 |
0 |
PC 17599 |
71.2833 |
C85 |
C |
2 |
3 |
1 |
3 |
Heikkinen, Miss. Laina |
female |
26.0 |
0 |
0 |
STON/O2. 3101282 |
7.9250 |
NaN |
S |
3 |
4 |
1 |
1 |
Futrelle, Mrs. Jacques Heath (Lily May Peel) |
female |
35.0 |
1 |
0 |
113803 |
53.1000 |
C123 |
S |
4 |
5 |
0 |
3 |
Allen, Mr. William Henry |
male |
35.0 |
0 |
0 |
373450 |
8.0500 |
NaN |
S |
info()
메서드를 사용해 데이터셋의 통계를 볼 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| <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
|
각 열은 다음 정보를 가지고 있습니다.
- PassengerId : 각 승객의 고유 번호
- Survived : 생존 여부(종속 변수)
0 = 사망
1 = 생존
- Pclass : 객실 등급 - 승객의 사회적, 경제적 지위
1st = Upper
2nd = Middle
3rd = Lower
- Name : 이름
- Sex : 성별
- Age : 나이
- SibSp : 동반한 Sibling(형제자매)와 Spouse(배우자)의 수
- Parch : 동반한 Parent(부모) Child(자식)의 수
- Ticket : 티켓의 고유넘버
- Fare : 티켓의 요금
- Cabin : 객실 번호
- Embarked : 승선한 항
C = Cherbourg
Q = Queenstown
S = Southampton
다음 명령을 통해 각 열에서 결측치를 가진 행의 수를 표시할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
| 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
|
데이터 전처리
정확도 향상을 위해 필요없는 데이터를 drop해야 합니다. Name
, Ticket
, Cabin
Object 열이며, 고유한 값을 가집니다. 이는 생존률과의 연관성을 찾기 쉽지 않습니다. 따라서 이 열들을 제거하는 것이 좋습니다.
1
2
3
| # drop
train = train.drop(["Name", "Ticket", "Cabin"], axis=1)
test = test.drop(["Name", "Ticket", "Cabin"], axis=1)
|
그 다음 순서는 결측값 처리입니다. 현재 Age
, Embarked
에 결측값이 존재합니다(Cabin
is dropped).
숫자로 표시되는 Age
의 경우 평균값을, 그렇지 않은 Embarked
의 경우 최빈값을 결측값 대신 사용하겠습니다.
1
2
3
4
5
6
7
8
9
| # NAN
train_mean = train["Age"].mean()
train["Age"].fillna(train_mean, inplace=True)
train["Embarked"].fillna("S", inplace=True)
test_mean = test["Age"].mean()
test["Age"].fillna(test_mean, inplace=True)
test["Embarked"].fillna("S", inplace=True)
test_median = test["Fare"].median()
test["Fare"].fillna(test_median, inplace=True)
|
Sex
의 male / female을 정수형 데이터로 변경하는 과정 또한 필요합니다. 각각 1 / 0으로 매치시키겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
| # astype
for i in train.index:
if train.loc[i, "Sex"] == "male":
train.loc[i, "Sex"] = 1
else:
train.loc[i, "Sex"] = 0
train["Sex"] = train["Sex"].astype(int)
for i in test.index:
if test.loc[i, "Sex"] == "male":
test.loc[i, "Sex"] = 1
else:
test.loc[i, "Sex"] = 0
test["Sex"] = test["Sex"].astype(int)
|
잘못된 관계 형성을 방지하기 위해 범주형 데이터Embarked
는 원핫 인코딩을 거쳐야 합니다.
1
| train["Embarked"].value_counts()
|
1
2
3
4
| S 644
C 168
Q 77
Name: Embarked, dtype: int64
|
1
2
3
4
5
6
7
8
| # OneHot
ohe = OneHotEncoder(sparse=False)
train_cat = ohe.fit_transform(train[["Embarked"]])
train = pd.concat([train.drop(columns=["Embarked"]),
pd.DataFrame(train_cat, columns=["Embarked_" + col for col in ohe.categories_[0]])], axis=1)
test_cat = ohe.fit_transform(test[["Embarked"]])
test = pd.concat([test.drop(columns=["Embarked"]),
pd.DataFrame(test_cat, columns=["Embarked_" + col for col in ohe.categories_[0]])], axis=1)
|
이제 훈련셋 및 테스트셋에 대한 모든 전처리 과정이 끝났습니다.
|
PassengerId |
Survived |
Pclass |
Sex |
Age |
SibSp |
Parch |
Fare |
Embarked_C |
Embarked_Q |
Embarked_S |
0 |
1 |
0 |
3 |
1 |
22.0 |
1 |
0 |
7.2500 |
0.0 |
0.0 |
1.0 |
1 |
2 |
1 |
1 |
0 |
38.0 |
1 |
0 |
71.2833 |
1.0 |
0.0 |
0.0 |
2 |
3 |
1 |
3 |
0 |
26.0 |
0 |
0 |
7.9250 |
0.0 |
0.0 |
1.0 |
3 |
4 |
1 |
1 |
0 |
35.0 |
1 |
0 |
53.1000 |
0.0 |
0.0 |
1.0 |
4 |
5 |
0 |
3 |
1 |
35.0 |
0 |
0 |
8.0500 |
0.0 |
0.0 |
1.0 |
1
2
| print(train.info())
print(test.info())
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| <class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 11 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 Sex 891 non-null int64
4 Age 891 non-null float64
5 SibSp 891 non-null int64
6 Parch 891 non-null int64
7 Fare 891 non-null float64
8 Embarked_C 891 non-null float64
9 Embarked_Q 891 non-null float64
10 Embarked_S 891 non-null float64
dtypes: float64(5), int64(6)
memory usage: 76.7 KB
None
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 10 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 PassengerId 418 non-null int64
1 Pclass 418 non-null int64
2 Sex 418 non-null int64
3 Age 418 non-null float64
4 SibSp 418 non-null int64
5 Parch 418 non-null int64
6 Fare 418 non-null float64
7 Embarked_C 418 non-null float64
8 Embarked_Q 418 non-null float64
9 Embarked_S 418 non-null float64
dtypes: float64(5), int64(5)
memory usage: 32.8 KB
None
|
필요 없는 열과 모든 결측치가 제거되었고, 데이터 타입을 숫자형으로 변환하였으며, 원핫 인코딩으로 정확도를 향상시켰습니다. 이제 학습을 시작할 준비가 끝났습니다.
학습
현재 훈련 데이터셋에는 레이블 열(Survived
)이 포함되어 있습니다. 학습에 사용될 target을 따로 분리시켜야 합니다.
1
2
| target = np.ravel(train.Survived)
train.drop(["Survived"], inplace=True, axis=1)
|
아래 함수는 특정 모델에 대한 학습 및 결과 출력을 일괄적으로 처리합니다.
1
2
3
4
5
6
| def play(model):
model.fit(train, target)
prediction = model.predict(test)
accuracy = round(model.score(train, target) * 100, 2)
print("Accuracy : ", accuracy, "%")
return prediction
|
이제 앞서 언급한 5가지 모델(LogisticRegression, SVC, KNeighborsClassifier, RandomForestClassifier, GaussianNB)에 대한 학습을 실시합니다.
1
2
3
4
5
| log_pred = play(LogisticRegression())
svm_pred = play(SVC())
knn_pred = play(KNeighborsClassifier(n_neighbors = 4))
rf_pred = play(RandomForestClassifier(n_estimators=100))
nb_pred = play(GaussianNB())
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| /opt/conda/lib/python3.7/site-packages/sklearn/linear_model/_logistic.py:818: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
extra_warning_msg=_LOGISTIC_SOLVER_CONVERGENCE_MSG,
Accuracy : 79.35 %
Accuracy : 66.44 %
Accuracy : 74.75 %
Accuracy : 100.0 %
Accuracy : 78.34 %
|
결과
과대적합이 발생한 랜덤 포레스트 분류기를 제외하면, 로지스틱 회귀를 사용했을 때 가장 높은 정확도를 보입니다. 콘테스트에 제출하기 위해, 결과를 csv 파일로 export합니다. 이후 콘테스트 페이지의 Submit Prediction을 클릭해 제출할 수 있습니다.
1
2
3
4
5
6
| submission = pd.DataFrame({
"PassengerId": test["PassengerId"],
"Survived": log_pred
})
submission.to_csv('submission_log.csv', index=False)
|