Kaggle のタイタニック問題をやってみる(2)

前回タイタニック問題のデータの中身を調査しましたので、今回はそのデータを元に学習し予測していきたいと思います。

訓練データ準備

今回使いたいデータの項目は、Pclass(チケットクラス)と年齢と性別です。まずは、年齢について何とかします。欠損値がたくさんあったので、これを何とかしないといけません。欠損値を何か適当なそれっぽいデータで埋めようと思うので、Pclass(チケットクラス)毎に年齢の平均を出してみます。

import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.model_selection import train_test_split
from keras.layers import Dense
from keras.models import Sequential
%matplotlib inline
df = pd.read_csv("/root/practice/titanic/csv/train.csv")
df[["Pclass", "Age"]].dropna().groupby(["Pclass"]).mean()
Pclass Age
1 38.233441
2 29.877630
3 25.140620

得られたPclass(チケットクラス)毎の年齢の平均値で、欠損値を埋めます。

# Age 欠損値を埋める
def fill_age(value):
    if not np.isnan(value[1]):
        return value[1]

    pclass_age_map = {
        1: 38,
        2: 30,
        3: 25
    }
    return pclass_age_map.get(value[0], 0)

df["Age"] = df[["Pclass", "Age"]].apply(fill_age, axis=1)

続いて、性別を0,1の値にして、学習に適した形にします。

# 性別を0, 1にする
sex = pd.get_dummies(df["Sex"], drop_first=True, dtype=int)
# 性別を0,1にしたデータを結合しておく --> male という列が最後にできている形になる 
df = pd.concat([df, sex], axis=1)

この時点でこのようなデータになっています。

df.head()
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked male
0 1 0 3 Braund, Mr. Owen Harris male 22.0 1 0 A/5 21171 7.2500 NaN S 1
1 2 1 1 Cumings, Mrs. John Bradley (Florence Briggs Thayer) female 38.0 1 0 PC 17599 71.2833 C85 C 0
2 3 1 3 Heikkinen, Miss. Laina female 26.0 0 0 STON/O2. 3101282 7.9250 NaN S 0
3 4 1 1 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1 0 113803 53.1000 C123 S 0
4 5 0 3 Allen, Mr. William Henry male 35.0 0 0 373450 8.0500 NaN S 1

今回は、学習に利用する項目としてPclass(チケットクラス)と性別と年齢のみを対象にしたいので、必要な列を除いて(性別の元データ列も含めて)削除します。

# 不要カラム削除
df.drop(["Name", "Sex", "SibSp", "Parch", "Ticket", "Fare", "Cabin", "Embarked"], axis=1, inplace=True)

そうすると、これだけスッキリしたデータになりました。

df.head()
PassengerId Survived Pclass Age male
0 1 0 3 22.0 1
1 2 1 1 38.0 0
2 3 1 3 26.0 0
3 4 1 1 35.0 0
4 5 0 3 35.0 1

このデータで訓練データを作成します。作成には sklearn の train_test_splitを利用します。

X_train, X_test, y_train, y_test = train_test_split(
    df.drop(["Survived"], axis=1),
    df["Survived"],
    test_size=0.10,
    random_state=101
)

訓練データの元となるデータは、データから Survived 列を除いたもの(train_test_splitの第一引数)、教師データの元となるデータは Survived 列のデータ(train_test_splitの第二引数)になります。これで訓練データが作成できました。X_train, y_train の中身を確認しておきます。

X_train.head()
PassengerId Pclass Age male
825 826 3 25.0 1
8 9 3 27.0 0
689 690 1 15.0 0
513 514 1 54.0 0
729 730 3 25.0 0
y_train.head()
Survived
825 0
8 1
689 1
513 1
729 0

学習

データの下準備はできたので、学習していきます。まずはニューラルネットワークを構築します。

model = Sequential()

# [中間層] ユニット数: 32, 重み初期化方法: uniform, 活性化関数: reru, 入力: 4項目
model.add(Dense(units=32, kernel_initializer='uniform', activation='relu', input_dim=4))
model.add(Dense(units=32, kernel_initializer='uniform', activation='relu'))

# [出力層] ユニット(出力)数: 1, 重み初期化方法: uniform, 活性化関数: シグモイド
model.add(Dense(units=1, kernel_initializer='uniform', activation='sigmoid'))

# 最適化アルゴリズム: adam, 目的(損失)関数: 平均二乗誤差
model.compile(optimizer='adam', loss='mean_squared_error', metrics=['accuracy'])

三層のニューラルネットワークを構築しました。各層の詳細な設定はソース内のコメントを参照してください。

それでは学習していきます。

# バッチサイズ: 32, 反復数: 300
history = model.fit(X_train, y_train, batch_size=32, epochs=300, verbose=1)

学習結果を折れ線グラフで表示させてみます。

sns.lineplot(data=history.history["loss"])

sns.lineplot(data=history.history["accuracy"])

学習を重ねる毎に誤差が減少し、精度が増加していくことがわかります。

テストデータ準備

学習ができたので、テストデータの予測していきます。 前回のブログでも書いた通り、訓練データから「生存フラグ」項目が無いものがテストデータになります。 データの準備は訓練データと同じなので、同じ手順でデータを加工していきます。

df_test = pd.read_csv("/root/practice/titanic/csv/test.csv")
df_test.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):
 #   Column       Non-Null Count  Dtype  
---  ------       --------------  -----  
 0   PassengerId  418 non-null    int64  
 1   Pclass       418 non-null    int64  
 2   Name         418 non-null    object 
 3   Sex          418 non-null    object 
 4   Age          332 non-null    float64
 5   SibSp        418 non-null    int64  
 6   Parch        418 non-null    int64  
 7   Ticket       418 non-null    object 
 8   Fare         417 non-null    float64
 9   Cabin        91 non-null     object 
 10  Embarked     418 non-null    object 
dtypes: float64(2), int64(4), object(5)
memory usage: 36.0+ KB
df_test.isnull().sum().sort_values(ascending=False)
data
Cabin 327
Age 86
Fare 1
PassengerId 0
Pclass 0
Name 0
Sex 0
SibSp 0
Parch 0
Ticket 0
Embarked 0

データ数は418行、テストデータの年齢の項目にも欠損値が多数あるようです。

# Age の欠損値を埋める
df_test["Age"] = df_test[["Pclass", "Age"]].apply(fill_age, axis=1)
# 性別を 0, 1 にする
sex = pd.get_dummies(df_test["Sex"], drop_first=True, dtype=int)
df_test = pd.concat([df_test, sex], axis=1)
df_test.head()
PassengerId Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked male
0 892 3 "Kelly, Mr. James" male 34.5 0 0 330911 7.8292 NaN Q 1
1 893 3 "Wilkes, Mrs. James (Ellen Needs)" female 47.0 1 0 363272 7.0000 NaN S 0
2 894 2 "Myles, Mr. Thomas Francis" male 62.0 0 0 240276 9.6875 NaN Q 1
3 895 3 "Wirz, Mr. Albert" male 27.0 0 0 315154 8.6625 NaN S 1
4 896 3 "Hirvonen, Mrs. Alexander (Helga E Lindqvist)" female 22.0 1 1 3101298 12.2875 NaN S 0
# 不要カラム削除
df_test.drop(["Name", "Sex", "SibSp", "Parch", "Ticket", "Fare", "Cabin", "Embarked"], axis=1, inplace=True)
df_test.head()
PassengerId Pclass Age male
0 892 3 34.5 1
1 893 3 47.0 0
2 894 2 62.0 1
3 895 3 27.0 1
4 896 3 22.0 0

予測

テストデータの準備ができたので、いよいよ予測していきたいと思います。

# 学習済みのニューラルネットワークで予測
test_predicts = model.predict(df_test)
test_predicts = [ 1 if y >= 0.5 else 0 for y in test_predicts]

出力された値(予測)を0,1にするところまでできたので、予め決められた提出するファイルのフォーマットにする準備をします。

# 提出用のCSVを出力する準備
df_test_result = pd.DataFrame({
    'PassengerId': df_test['PassengerId'],
    'Survived': test_predicts
})
df_test_result.head()
PassengerId Survived
0 892 0
1 893 1
2 894 0
3 895 0
4 896 1

後はCSVに出力するだけです。

df_test_result.to_csv("/root/practice/titanic/csv/predictions.csv", index=False)

提出

予測結果をCSVに出力することができたので、提出してみます。 Titanic - Machine Learning from Disasterのページに行き、「Submit Predictions」ボタンを押下して、CSVファイルをアップロードします。 エラーが無ければ、アップロードに成功した旨の通知が来るので、Leaderboardで自分の順位を確認します。

おおおおおおお!圧倒的に下から数えた方が早い順位ですが、Leaderboardに自分の名前が刻まれました!