LoginSignup
5
2

More than 1 year has passed since last update.

Kaggle奮闘録(Titanic編4) -アンサンブル学習-

Posted at

1.はじめに

引き続きkaggleの"Titanic - Machine Learning from Disaster"に挑戦していきます.ここ最近になって,このタイタニックの2値分類において精度8割越えがかなり難易度高いことに気づいて少し絶望していますが,もっと勉強してなんとか達成してみたいと思います.
今回は前回の最後で述べたように,ニューラルネットワークモデルとLightGBMのアンサンブル学習を実践してみましたところ,前回よりもさらに精度が向上したのでそれを記載しておきます.

2.方法

以下,提出したコードです.

Titanic Tutorialに載っていたデータインストールのために必要なコード(コピペ)

Titanic Tutorial

import numpy as np
import pandas as pd
import os
for dirname, _, filenames in os.walk('/kaggle/input'):
    for filename in filenames:
        print(os.path.join(dirname, filename))

パッケージインストール

import matplotlib.pyplot as plt
from sklearn.preprocessing import LabelEncoder, StandardScaler
from imblearn.over_sampling import SMOTE
from sklearn.model_selection import train_test_split
import optuna.integration.lightgbm as lgb
import keras

ニューラルネットワークモデルの定義

def build_model(unit1=64, unit2=128, unit3=256, dropout=0):
  model = keras.models.Sequential()
  model.add(keras.layers.Dense(unit1, activation='relu', kernel_initializer=keras.initializers.he_normal(seed=0)))   # He Weight Initialization
  model.add(keras.layers.Dense(unit2, activation='relu', kernel_initializer=keras.initializers.he_normal(seed=0)))   # He Weight Initialization
  model.add(keras.layers.Dense(unit3, activation='relu', kernel_initializer=keras.initializers.he_normal(seed=0)))   # He Weight Initialization
  if dropout > 0:
    model.add(keras.layers.Dropout(dropout))
  model.add(keras.layers.Dense(1, activation='sigmoid', kernel_initializer=keras.initializers.glorot_uniform(seed=0)))   # Xavier Weight Initialization
  model.compile(optimizer='adam', loss='binary_crossentropy', metrics=["accuracy"])
  return model

今回はTitanic編2の時のニューラルネットワークに少し変更を加え,中間層を1層増やした3層のニューラルネットワークを使用しました.2層の中間層のニューラルネットワークの学習曲線ではtrain_accuracy < validation_accuracyとなっており,過学習が考えられたためです.
またハイパーパラメータの初期値はTitanic編2と同様の方法で行い,その結果unit1=64, unit2=128, unit3=256, dropout=0という値が得られたのでそちらを使用していますが,今回はハイパーパラメータチューニングの部分のコードは割愛しています.

データインストール&欠損値処理

train = pd.read_csv("/kaggle/input/titanic/train.csv")
test = pd.read_csv("/kaggle/input/titanic/test.csv")

train['Age']= train.groupby(['Sex','Pclass'])['Age'].apply(lambda row : row.fillna(row.median()))
test['Age']= test.groupby(['Sex','Pclass'])['Age'].apply(lambda row : row.fillna(row.median()))
test['Fare']= test.groupby(['Sex','Pclass'])['Fare'].apply(lambda row : row.fillna(row.median()))

この辺も前回と全く同じ,中央値による補間です.

特徴量選択とカテゴリー変数の変換

names = ['Pclass','Sex','Age','SibSp','Parch','Fare']
X_train = train[names]
X_test = test[names]
y_train = train['Survived']

sex_le = LabelEncoder()
X_train['Sex'] = sex_le.fit_transform(X_train['Sex'])
X_test['Sex'] = sex_le.fit_transform(X_test['Sex'])
X_train.head()

標準化&検証データの用意

standard = StandardScaler()
standard.fit(X_train)
X_train = standard.transform(X_train)
X_test = standard.transform(X_test)

X_train, X_val, y_train, y_val = train_test_split(X_train, y_train, test_size = 0.2, stratify = y_train, random_state = 0)

前回触れていませんでしたが,random_stateを指定することによって,学習データの分割方法が何回やっても同じになるように固定することができます(乱数シードの固定).
全体の実装が終わってから思いましたが,ここをホールドアウト法ではなく,交差検証法に変えてアンサンブル学習を行えばもっと上手くいくのでは?と思ったので次回以降で試してみようと思います(予定).

LightGBMモデルの構築

# making dataset for lightGBM
lgb_train = lgb.Dataset(X_train, y_train)
lgb_val = lgb.Dataset(X_val, y_val)

# fix parameters
params = {
    'boosting': 'gbdt',   # Gradient Boosting Decision Tree
    'objective':'binary',   # Method of task     
    'metric':'binary_error',   # Evaluation criteria
    "verbosity": -1,   # Not visualize progress
}

# hyperparameter tuning & build lightGBM model with optimal parameters
model1 = lgb.train(params = params, 
                train_set = lgb_train,
                valid_sets = lgb_val,
                verbose_eval = 100,
                early_stopping_rounds = 100)

前回と同じ.

ニューラルネットワークモデルの構築

# build model
model2 = build_model()

# set callbacks
early_stopping = keras.callbacks.EarlyStopping(
    monitor='val_loss',
    patience=10,
)

# train model
history = model2.fit(
X_train,
y_train,
validation_data=(X_val, y_val),
epochs=50,
batch_size=32,
callbacks=early_stopping
)

前々回と同じ.

アンサンブル学習

y_pred1 = model1.predict(X_test, num_iteration=model1.best_iteration)
y_pred2 = np.squeeze(model2.predict(X_test))

y_pred = (y_pred1 + y_pred2) / 2

y_pred[(y_pred >= 0.5)] = 1
y_pred[(y_pred < 0.5)] = 0
y_pred = np.array(y_pred, dtype='int')

LightGBMにより予測された(ラベル1である)確率をy_pred1,Neural Networkにより予測された確率をy_pred2に格納し,それぞれの平均値をとったものを最終予測確率としました.これをソフトなアンサンブル学習というようです.ちなみに分類器が奇数個存在する場合には,多数決により予測ラベルを決定するアンサンブル学習が可能であり,これをハードなアンサンブル学習というようです.詳しくはkoshian2さんのこちらの記事をご参照ください.

予測結果をCSVファイルに変換

output = pd.DataFrame({'PassengerId': test.PassengerId, 'Survived': y_pred})
output.to_csv('submission.csv', index=False)
print("Your submission was successfully saved!")

3.結果

以上のモデルによる予測結果を提出したところ,予測精度は78.708%と,過去最高の分類精度となりました.分類精度をあと1.3%あげることができれば目標達成です.
この予測結果をsubmitし,Leaderboardで自分の順位を確認したところ,16408チーム中1568位だったので,上位10%以内の比較的精度の高い分類器であることがわかります.

4.おわりに

今回は前回に引き続きLightGBMとNeural Networkのアンサンブル学習により,タイタニックデータの生存者・死亡者の2値分類に挑みました.その結果大きく精度が向上したのでモチベーションの向上につながりました.

次回は交差検証法により今回の分類器を複数生成し,アンサンブル学習を行う予定です.
あとはもう少し解説要素を増やせるよう,統計学等を勉強します.

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2