データサイエンティストになりたいのでタイタニックコンペに挑戦してみた
データサイエンティストになりたいので初心者向けコンペの タイタニック(Titanic - Machine Learning from Disaster)に挑戦してみました。 今回はKaggle Notebook上でPythonを使ってモデルの学習と予測をしました。 LightGBMでGBDT(勾配ブースティング木)、Kerasでニューラルネットワークのモデルを学習し、 それぞれの予測値の平均をとってアンサンブルしました。
分析の準備
必要なモジュールをインポートします。
from pathlib import Path import numpy as np import pandas as pd from sklearn.model_selection import train_test_split from sklearn.pipeline import make_pipeline from sklearn.impute import SimpleImputer from sklearn.preprocessing import StandardScaler from sklearn.metrics import accuracy_score import lightgbm as lgb from tensorflow import keras from tensorflow.keras import layers
データを読み込みます。 このコンペではtrain.csv、test.csvとgender_submission.csvが与えられます。
data_dir = Path("/kaggle/input/titanic") train = pd.read_csv(data_dir / "train.csv") test = pd.read_csv(data_dir / "test.csv") sample_submission = pd.read_csv(data_dir / "gender_submission.csv")
各カラムの説明は以下のとおりです。
Survival
- 生存したか(0=死亡、1=生存)Pclass
- チケットクラスName
- 名前Sex
- 性別Age
- 年齢SibSp
- タイタニックに乗船した兄弟/配偶者の人数Parch
- タイタニックに乗船した親/子の人数Ticket
- チケット番号Fare
- 運賃Cabin
- 客室番号Embarked
- 乗船港
特徴量の作成
preprocess
関数で前処理をしています。
与えられたデータからTitleとFamilyという特徴量を作りました。
前処理の内容は以下のとおりです。
Title
- Nameに含まれる敬称(Mr、Miss、Mrs、Master、それ以外)を整数値に変換Cabin
- 先頭のアルファベットを抽出し整数値に変換Sex
- 女性=0、男性=1に変換Family
- SibSpとParchの合計Fare
- 分布に偏りがあるのでlog(x+1)
で変換Embarked
- 整数値に変換
LightGBMは入力に欠損値があっても学習できるのでpreprocess
では欠損値補完していません。
ニューラルネットワーク用にカテゴリ変数(Title、Cabin、Embarked)をone-hot encodingしました。
PassengerId、Name、Ticketは不要なので削除しました。
def preprocess(df): df = df.copy() df["Title"] = 0 title_dict = { " Mr\. ": 1, " Miss\. ": 2, " Mrs\. ": 3, " Master\. ": 4, } for pattern, label in title_dict.items(): df.loc[df["Name"].str.contains(pattern), "Title"] = label df["Title"] = pd.Categorical(df["Title"], categories=range(5)) cabin_dict = {"A": 1, "B": 2, "C": 3, "D": 4, "E": 5, "F": 6, "G": 7, "T": 8} df["Cabin"] = df["Cabin"].str[0].replace(cabin_dict).fillna(0) df["Cabin"] = pd.Categorical(df["Cabin"], categories=range(9)) df["Sex"] = df["Sex"].replace({"female": 0, "male": 1}) df["Family"] = df["SibSp"] + df["Parch"] df["Fare"] = np.log1p(df["Fare"]) df["Embarked"] = df["Embarked"].fillna(0).replace({"C": 1, "Q": 2, "S": 3}) df["Embarked"] = pd.Categorical(df["Embarked"], categories=range(4)) dummies = pd.get_dummies(df[["Title", "Cabin", "Embarked"]], drop_first=True) df = pd.concat([df, dummies], axis=1) df = df.drop(columns=["PassengerId", "Name", "Ticket"]) return df _train = preprocess(train) train_X = _train.drop(columns="Survived") train_y = _train["Survived"] test_X = preprocess(test)
モデルの学習・予測
今回はGBDTとニューラルネットワークの2つのモデルを作成し、 予測値の平均をとってアンサンブルします。
GBDT
GBDTの学習・予測は以下のとおりです。 検証データに対する正解率は0.843でした。
cols = ["Pclass", "Sex", "Age", "Fare", "Cabin", "Embarked", "Title", "Family"] train_X_lgb, val_X_lgb, train_y_lgb, val_y_lgb = train_test_split( train_X[cols], train_y, test_size=0.3, shuffle=True, stratify=train_y, random_state=42, ) test_X_lgb = test_X[cols] # データセット作成 train_dataset = lgb.Dataset(train_X_lgb, train_y_lgb) val_dataset = lgb.Dataset(val_X_lgb, val_y_lgb) # ハイパーパラメータの設定 params = { "objective": "binary", "num_leaves": 7, "learning_rate": 0.1, "bagging_fraction": 0.9, "bagging_freq": 1, "lambda_l1": 0.5, "lambda_l2": 1, "seed": 42, } categorical_features = ["Cabin", "Embarked", "Title"] # 学習 model = lgb.train( params, train_dataset, num_boost_round=100, valid_sets=[train_dataset, val_dataset], early_stopping_rounds=10, categorical_feature=categorical_features, verbose_eval=10, ) # 予測 val_pred_lgb = model.predict(val_X_lgb) test_pred_lgb = model.predict(test_X_lgb) # 評価 acc = accuracy_score(val_y_lgb, np.where(val_pred_lgb > 0.5, 1, 0)) print("Accuracy:", acc)
ニューラルネットワーク
build_model
でニューラルネットワークのモデルを構築しています。
one-hot encodingしたカテゴリ変数を選択しています。
ニューラルネットワークは欠損値を扱えない、スケーリングが必要なのでSimpleImputer
で欠損値補完、StandardScaler
で標準化しています。
検証データに対する正解率は0.832でした。
def build_model(num_inputs): model = keras.Sequential([ layers.Dense(64, activation="relu"), layers.Dropout(0.1), layers.Dense(32, activation="relu"), layers.Dropout(0.1), layers.Dense(1, activation="sigmoid"), ]) model.compile( optimizer="adam", loss="binary_crossentropy", metrics=["accuracy"], ) return model cols = [ "Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Title_1", "Title_2", "Title_3", "Title_4", "Cabin_1", "Cabin_2", "Cabin_3", "Cabin_4", "Cabin_5", "Cabin_6", "Cabin_7", "Cabin_8", "Embarked_1", "Embarked_2", "Embarked_3", ] scaler = make_pipeline( StandardScaler(), SimpleImputer(), ) train_X_nn = scaler.fit_transform(train_X[cols]) train_X_nn, val_X_nn, train_y_nn, val_y_nn = train_test_split( train_X_nn, train_y, test_size=0.3, shuffle=True, stratify=train_y, random_state=43, ) test_X_nn = scaler.transform(test_X[cols]) # 学習 model = build_model(len(cols)) early_stopping = keras.callbacks.EarlyStopping(patience=10, restore_best_weights=True) model.fit( train_X_nn, train_y_nn, batch_size=32, epochs=100, callbacks=[early_stopping], validation_data=(val_X_nn, val_y_nn), shuffle=True, ) # 予測 val_pred_nn = model.predict(val_X_nn).flatten() test_pred_nn = model.predict(test_X_nn).flatten() # 評価 acc = accuracy_score(val_y_nn, np.where(val_pred_nn > 0.5, 1, 0)) print("Accuracy:", acc)
アンサンブル
GBDTとニューラルネットワークの予測値を平均します。
test_pred = test_pred_lgb * 0.5 + test_pred_nn * 0.5
提出ファイルの作成
submission = sample_submission.assign(Survived=np.where(test_pred > 0.5, 1, 0)) submission.to_csv("./submission.csv", index=False)
結果
スコアは0.77751でした。 今後もコンペに参加してデータサイエンティストを目指していきたいです!!