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

【Kaggle挑戦記】Spaceship Titanic 攻略 #12:最強タッグ「LGBM × XGBoost」結成。しかし、正解は一つとは限らない

1. 次なる一手:二大巨頭のアンサンブル

前回、物理情報「Cabin」の導入により、ついに 0.80406 という大台を突破しました。 さらなる高みを目指し、今回は Kaggle の定石である「アンサンブル(学習器の平均化)」に挑戦します。 単独で高スコアを出した鋭い LightGBM に、手堅い XGBoost を組み合わせることで、予測の「揺らぎ」を抑え、さらなる精度向上を狙いました。

2. 【実装】アンサンブル・ハイブリッドモデル全文

性格の違う2つのAIに「確率」を出させ、その平均をとる合議制ロジックです。Macのターミナルで実行した、今回のフルコードを公開します。

import pandas as pd
import numpy as np
import lightgbm as lgb
import xgboost as xgb

# 1. データの読み込み
train = pd.read_csv('train.csv')
test = pd.read_csv('test.csv')

# 2. 特徴量エンジニアリング(最高スコア時のロジックを継承)
spend_cols = ["RoomService", "FoodCourt", "ShoppingMall", "Spa", "VRDeck"]

for df in [train, test]:
    # 支出の論理補完
    df[spend_cols] = df[spend_cols].fillna(0)
    total_spend = df[spend_cols].sum(axis=1)
    df.loc[(df['CryoSleep'].isnull()) & (total_spend > 0), 'CryoSleep'] = False
    df.loc[(df['CryoSleep'].isnull()) & (total_spend == 0), 'CryoSleep'] = True
    df['Age'] = df['Age'].fillna(df['Age'].median())

    # Cabinの分解(物理情報の追加)
    df['Cabin'] = df['Cabin'].fillna('U/U/U')
    df['Cabin_Deck'] = df['Cabin'].apply(lambda x: x.split('/')[0])
    df['Cabin_Side'] = df['Cabin'].apply(lambda x: x.split('/')[-1])

# 3. 特徴量の選定と整形
features = ["CryoSleep", "Age", "RoomService", "FoodCourt", "ShoppingMall", "Spa", "VRDeck", "Cabin_Deck", "Cabin_Side"]
X = pd.get_dummies(train[features], drop_first=True)
y = train["Transported"].astype(int)
X_test = pd.get_dummies(test[features], drop_first=True)
X, X_test = X.align(X_test, join='left', axis=1, fill_value=0)

# 4. モデル1:LightGBM の学習と確率予測
model_lgb = lgb.LGBMClassifier(n_estimators=100, learning_rate=0.05, random_state=1)
model_lgb.fit(X, y)
prob_lgb = model_lgb.predict_proba(X_test)[:, 1]

# 5. モデル2:XGBoost の学習と確率予測
model_xgb = xgb.XGBClassifier(n_estimators=100, learning_rate=0.05, max_depth=6, random_state=1)
model_xgb.fit(X, y)
prob_xgb = model_xgb.predict_proba(X_test)[:, 1]

# 6. アンサンブル(二人の予測確率を平均する)
final_prob = (prob_lgb + prob_xgb) / 2

# 7. 最終判定(0.5を閾値とする)
final_predictions = (final_prob >= 0.5)

# 8. 保存
output = pd.DataFrame({'PassengerId': test['PassengerId'], 'Transported': final_predictions.astype(bool)})
output.to_csv('sub_v12_ensemble.csv', index=False)

3. コンソールが示した「完璧な調和」

実行後、コンソールに出力された数字は驚くほど均衡が取れていました。

 Ensemble Complete!
LGBM Mean Prob: 0.5029
XGB Mean Prob: 0.5039

学習データの正解割合 0.5036 に対して、両モデルとも極めて近い数値を算出。二つのAIが、どちらもデータの全体像(分布)を正確に捉えていたことがわかります。

4. 結果と考察:安定を選んだ代償

リーダーボードの結果は 0.80360。前回(0.80406)からわずか 0.00046 の微減となりました。 なぜ「最強の二人」を混ぜたのに下がったのか?ここにはエンジニアリングの面白い側面があります。

  • 「尖った正解」がマイルドになった: LightGBMがギリギリの判断で正解していた難問を、XGBoostの慎重な判断が打ち消してしまった可能性があります。
  • 汎化性能の向上: スコアは僅かに下がりましたが、平均確率が安定したことで、未知のデータに対して「大外し」しにくい、より頑健なモデルになったと言えます。

5. まとめ:次なるフロンティアへ

「混ぜれば上がる」という神話を、自分のコードで検証した今回の実験。 0.8台を安定して出せるようになったことは大きな前進です。現在の特徴量においてアンサンブルが「安定」に寄ったということは、さらなるスコアアップには「新たな特徴量」が必要であるというサインでもあります。


次は、もう一つの物理情報、「グループ(PassengerId)」の解析に切り込み、さらなる高みを目指します。

PR