【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