忍者ブログ
統計、機械学習、AIを学んでいきたいと思います。 お役に立てば幸いです。

【Kaggle挑戦記】Titanic 攻略 #17:0.77511への浮上。LightGBMを「制御」する手応え

前回、最強モデル LightGBM を投入するも、過学習により 0.76315 という惨敗を喫しました。今回はその反省を活かし、モデルにあえて「制約」を課すリベンジマッチ。結果は 0.77511。自己ベストには届きませんでしたが、確かな改善の兆しが見えてきました。

1. 戦略の検証:過学習の抑制は成功したか?

今回の修正の肝は、モデルに「深追いさせない」ことでした。その結果、スコアには以下のような変化が現れました。

  • 本番スコアの向上: 0.76315 → 0.77511(+0.012の改善)
  • CVスコアの適正化: 0.8418 → 0.8373(手元の数字が下がり、本番が上がった)

この「手元の数字を下げて、本番を上げる」という現象こそ、過学習が解消に向かっている決定的な証拠です。F1マシンを路地裏に合わせて減速させたことで、壁にぶつからずにコーナーを曲がれるようになったのです。

2. 【実装】「制約」を刻んだリベンジ・コード

過学習した前回(負け)の設定をコメントで残し、今回(改善)の設定を対比させました。

import pandas as pd
import numpy as np
import lightgbm as lgb
from sklearn.model_selection import GridSearchCV

# 1. 前処理(自己ベスト 0.78468 時の最強布陣を維持)
train_data = pd.read_csv('train.csv')
test_data = pd.read_csv('test.csv')

for df in [train_data, test_data]:
    df['Embarked'] = df['Embarked'].fillna('S')
    df['FamilySize'] = df['SibSp'] + df['Parch'] + 1

group_cols = ['Pclass', 'Sex']
train_data['Age'] = train_data['Age'].fillna(train_data.groupby(group_cols)['Age'].transform('median'))
test_data['Age'] = test_data['Age'].fillna(test_data.groupby(group_cols)['Age'].transform('median'))
test_data['Fare'] = test_data['Fare'].fillna(test_data['Fare'].median())

# 2. 特徴量のダミー変数化
features = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "FamilySize", "Embarked"]
X = pd.get_dummies(train_data[features], drop_first=True)
y = train_data["Survived"]
X_test = pd.get_dummies(test_data[features], drop_first=True)
X, X_test = X.align(X_test, join='left', axis=1, fill_value=0)

# 3. ライトGBM:過学習を防ぐための「抑制」チューニング
param_grid = {
    # 'num_leaves': [31],        # 前回:多すぎて細かく分けすぎていた
    'num_leaves': [7, 10, 15],   # 修正:モデルをシンプルに保つ

    'learning_rate': [0.01, 0.05],
    'n_estimators': [100, 200],

    # 'max_depth': [-1],         # 前回:無制限が過学習の主因
    'max_depth': [3, 4],         # 修正:あえて「浅い木」に限定する

    'min_child_samples': [20, 40], # 修正:1つの枝に40人以上のデータを要求(ノイズ対策)

    'random_state': [1]
}

# 4. グリッドサーチ実行
gbm = lgb.LGBMClassifier(verbosity=-1)
grid_search = GridSearchCV(gbm, param_grid, cv=5, n_jobs=-1, verbose=0)
grid_search.fit(X, y)

# 5. 結果の記録
print(f"Best Params: {grid_search.best_params_}")
print(f"Best Score (CV): {grid_search.best_score_:.4f}") # 0.8373 を記録

# 6. 予測
best_model = grid_search.best_estimator_
predictions = best_model.predict(X_test)
pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': predictions}).to_csv('submission_lgbm_v2.csv', index=False)

3. 考察:0.8の壁を越えるために必要なこと

エンジニア的な視点:
今回の実験で、「強力なアルゴリズムを使うなら、強力なブレーキが必要である」という教訓が実証されました。0.77511 という数字は、まだ自己ベストには届きませんが、「モデルの使いこなし」という点では過去最高のレベルに達しています。

ここからさらに 0.03 スコアを伸ばし、0.8の大台に乗るには、モデルの微調整(チューニング)だけでは限界があるかもしれません。次は、データの背後に隠れた「家族の運命」や「チケット番号の繋がり」など、人間らしい洞察(特徴量エンジニアリング)を LightGBM に教え込むフェーズに来ていると感じます。


数字は嘘をつきません。改善した 0.012 は、私たちが正しい方向に進んでいる証拠。この調子で、次なる一手「特徴量の深化」へ進みます!



PR