はじめに
Kaggleの復習もやることにした。まずは定番のタイタニックから。
できたこと
- 機械学習モデル: LightGBM
- 交差検証: Stratified K-Fold
- CVスコア(logloss): 0.418
- パブリックスコア: 0.765
ライブラリをインポート
# 関連ライブラリをインポート
import lightgbm as lgb
import numpy as np
#import os
import pandas as pd
import random
#import torch
# scikit-learn関連をインポート
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import accuracy_score
# warningを非表示
import warnings
warnings.filterwarnings('ignore')
乱数を固定する。
# 参考: 乱数固定
def seed_everything(seed=1234):
random.seed(seed)
np.random.seed(seed)
#os.environ['PYTHONHASHSEED'] = str(seed)
# torch.manual_seed(seed)
# torch.cuda.manual_seed(seed)
# torch.backends.cudnn.daterministic = True
seed_everything(1234)
データ読み込み
コンペサイトからあらかじめデータをダウンロードし、読み込ませる。
# データ読み込み
train = pd.read_csv('../input/titanic/train.csv')
test = pd.read_csv('../input/titanic/test.csv')
gender_submission = pd.read_csv('../input/titanic/gender_submission.csv')
前処理・特徴量エンジニアリング
前処理と特徴量エンジニアリングを施す。
# データを連結(前処理効率化のため)
data = pd.concat([train, test], sort=False)
# 前処理
data['Sex'].replace(['male', 'female'], [0, 1], inplace=True)
data['Embarked'].fillna(('S'), inplace=True)
data['Embarked'] = data['Embarked'].map({'S': 0, 'C': 1, 'Q': 2}).astype(int)
data['Fare'].fillna(np.mean(data['Fare']), inplace=True)
data['Age'].fillna(data['Age'].median(), inplace=True)
data['FamilySize'] = data['Parch'] + data['SibSp'] + 1
data['IsAlone'] = 0
data.loc[data['FamilySize'] == 1, 'IsAlone'] = 1
# 使用しない列を削除
delete_columns = ['Name', 'PassengerId', 'Ticket', 'Cabin']
data.drop(delete_columns, axis=1, inplace=True)
# 訓練データとテストデータに戻す
train = data[:len(train)]
test = data[len(train):]
# 目的変数と説明変数に分割
y_train = train['Survived']
X_train = train.drop('Survived', axis=1)
X_test = test.drop('Survived', axis=1)
# 表示
print(f"X_train: {X_train.shape}")
display(X_train.head())
print(f"y_train: {y_train.shape}")
display(y_train.head())
print(f"X_test: {X_test.shape}")
display(X_test.head())
出力は以下のような感じになるはず(データフレームの表示は割愛)。
X_train: (891, 9)
y_train: (891,)
X_test: (418, 9)
交差検証の準備
交差検証を行うための準備を行う。Stratified K-Foldを行うためにインスタンスを生成。目的変数が均等に分割できているか確認。
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=0)
# 目的変数が均等に分割できているか確認
for fold_id, (train_index, valid_index) in enumerate(cv.split(X_train, y_train)):
X_tr = X_train.loc[train_index, :]
X_val = X_train.loc[valid_index, :]
y_tr = y_train[train_index]
y_val = y_train[valid_index]
print(f'fold_id: {fold_id}')
print(f'y_tr y==1 rate: {sum(y_tr)/len(y_tr)}')
print(f'y_val y==1 rate: {sum(y_val)/len(y_val)}')
print("--------------------------------")
出力は以下の通り。ちゃんと均等になっている。
fold_id: 0
y_tr y==1 rate: 0.38342696629213485
y_val y==1 rate: 0.3854748603351955
--------------------------------
fold_id: 1
y_tr y==1 rate: 0.38429172510518933
y_val y==1 rate: 0.38202247191011235
--------------------------------
fold_id: 2
y_tr y==1 rate: 0.38429172510518933
y_val y==1 rate: 0.38202247191011235
--------------------------------
fold_id: 3
y_tr y==1 rate: 0.38429172510518933
y_val y==1 rate: 0.38202247191011235
--------------------------------
fold_id: 4
y_tr y==1 rate: 0.38288920056100983
y_val y==1 rate: 0.38764044943820225
--------------------------------
メインループ
訓練を行う。
y_preds = [] # 予測値を格納するリスト
models = [] # モデルを格納するリスト
oof_train = np.zeros((len(X_train),)) # 各分割でのoof(訓練に使用されなかったfold)に対する予測値
categorical_features = ['Embarked', 'Pclass', 'Sex'] # カテゴリ変数
# ハイパーパラメータ
params = {
'objective': 'binary',
'max_bin': 300,
'learning_rate': 0.05,
'num_leaves': 40
}
少し用語をメモ。Out-of-fold(oof)は、各foldで訓練に使われなかったfoldのこと(多分)。oof_trainは、各foldでoof(訓練に使用されなかったfold)に対する「予測値」のこと(多分)。
# メインループ
for fold_id, (train_index, valid_index) in enumerate(cv.split(X_train, y_train)):
# 表示
print('-------------------')
print(f'Fold: {fold_id}')
# 訓練データと検証データに分割
X_tr = X_train.loc[train_index, :]
X_val = X_train.loc[valid_index, :]
y_tr = y_train[train_index]
y_val = y_train[valid_index]
# データセットを生成
lgb_train = lgb.Dataset(X_tr, y_tr, categorical_feature=categorical_features)
lgb_eval = lgb.Dataset(X_val, y_val, reference=lgb_train, categorical_feature=categorical_features)
# 訓練
model = lgb.train(params, lgb_train,
valid_sets=[lgb_train, lgb_eval],
num_boost_round=1000,
callbacks=[lgb.early_stopping(stopping_rounds=10,
verbose=True),
lgb.log_evaluation(10)])
# 検証データでの予測
oof_train[valid_index] = model.predict(X_val, num_iteration=model.best_iteration)
y_pred = model.predict(X_test, num_iteration=model.best_iteration)
# 予測値と学習済みモデルを格納
y_preds.append(y_pred)
models.append(model)
評価
oof_train(訓練に使用されなかったfoldに対する予測値)をcsvで保存し、表示させる。
# oof_train(訓練に使用されなかったfoldに対する予測値)を保存・表示
pd.DataFrame(oof_train).to_csv('oof_train_skfold.csv', index=False)
print(oof_train[:10])
[0.10743915 0.9336148 0.24343427 0.97174844 0.23722008 0.05967737
0.10523416 0.27028808 0.37330822 0.9413475 ]
次に各foldの検証データに対するスコアをscores変数に代入し、printする。
# 各foldの検証データに対するスコア(logloss)を格納・表示
scores = [m.best_score['valid_1']['binary_logloss'] for m in models]
print(scores)
[0.38009408187107163, 0.37924619589793596, 0.40508420679400237, 0.4450699329180633, 0.4825556025529494]
スコア(logloss)は0.38~0.48となった。ばらつきがある。スコアを平均して、最終的なスコア(Cross Validationスコア)を計算する。
# 各foldの検証データに対する予測値を平均して、最終的なスコアを生成
score = sum(scores) / len(scores)
print('===CV scores===')
print(score)
===CV scores===
0.41841000400680456
次に、oof_train(訓練に使用されなかったfoldに対する予測値)を閾値0.5で二値化する。その値と、訓練データの目的変数を使って、正解率を計算する。
# oof_train(訓練に使用されなかったfoldに対する予測値)を0.5で二値化
y_pred_oof = (oof_train > 0.5).astype(int)
print(accuracy_score(y_train, y_pred_oof)) # 精度を計算
0.8294051627384961
正解率は82%となった。
Submit
提出するためにデータを作成する。今回は、各foldでの「テストデータに対する予測値」の平均をとる。
# 各foldでの「テストデータに対する予測値」の平均をとる
y_sub = sum(y_preds) / len(y_preds)
y_sub = (y_sub > 0.5).astype(int) # 閾値0.5で二値化
y_sub[:10]
array([0, 0, 0, 0, 0, 0, 1, 0, 1, 0])
提出用ファイルを作成する。サンプル用のデータ(gender_submission.csv)を読み込み、計算した予測値を代入する。
# 提出用ファイルを作成
sub = pd.read_csv('../input/titanic/gender_submission.csv')
sub['Survived'] = y_sub
sub.to_csv('submission_lightgbm_skfold.csv', index=False)
sub.head()
結果確認
出力したcsvファイル(submission_lightgbm_skfold.csv)を提出する。パブリックスコアは76%だった。
おわりに
今回は、タイタニックコンペにデータを提出してみた。
- 機械学習モデル: LightGBM
- 前処理,特徴量エンジニアリング: 適当に…
- 交差検証: Stratiried K-Fold (5 Fold)
- CVスコア: 82%
- パブリックスコア: 76%
出典