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

【Kaggle挑戦記】Spaceship Titanic 攻略 #11:ついに0.8突破!物理情報「Cabin」の導入が運命の分かれ道となった

1. 前回の敗北から原点回帰へ

前回、統計的な分布調整による「閾値最適化」に挑みましたが、結果は 0.79003 へのダウン。 エンジニアとしての仮説「学習データとテストデータの分布は同じはず」は間違っていないと確信しつつも、AIに与える「判断材料(特徴量)」そのものを強化する必要性を痛感しました。 そこで今回、満を持して投入したのが、船内の物理的な位置を示す Cabin(客室番号) です。

2. 実装:ドメイン知識と物理情報の融合

これまでの最高得点(0.79611)を出した「支出データからの睡眠状態逆算」というドメイン知識に基づく論理補完に、Cabinからパースした「Deck(デッキ)」と「Side(右舷・左舷)」を掛け合わせました。 Macのターミナルで実行した、今回の決定版コードがこちらです。

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

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

# 2. 特徴量エンジニアリング(論理補完 & Cabin分解)
spend_cols = ["RoomService", "FoodCourt", "ShoppingMall", "Spa", "VRDeck"]

for df in [train, test]:
    # --- A. 支出実績から CryoSleep を論理的に推論 ---
    df[spend_cols] = df[spend_cols].fillna(0)
    total_spend = df[spend_cols].sum(axis=1)
    
    # 支出があれば起きている(False)、なければ寝ている(True)
    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())

    # --- B. Cabin(客室)を Deck/Num/Side に分解 ---
    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"
]

# 4. データの整形(ダミー変数化)
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)

# 5. モデル学習(LightGBM)
model = lgb.LGBMClassifier(
    n_estimators=100, 
    learning_rate=0.05, 
    random_state=1
)
model.fit(X, y)

# 6. 予測と提出ファイルの出力
predictions = model.predict(X_test)
output = pd.DataFrame({
    'PassengerId': test['PassengerId'], 
    'Transported': predictions.astype(bool)
})
output.to_csv('submission_v11.csv', index=False)

3. リーダーボードの結果:歓喜の瞬間

Kaggleにファイルをアップロードし、リーダーボードが更新された瞬間、思わずガッツポーズが出ました。

 Previous Best : 0.79611
 New Score     : 0.80406 (0.8の壁を突破!)

4. 考察:なぜ「Side」が効いたのか

今回追加した「Cabin_Side(右舷・左舷)」は、事故の被害がどちらから来たかという物理的な衝突面をモデルに示唆しました。 「寝ていたか、起きていたか(状態)」×「船のどちら側にいたか(位置)」。 このミクロな情報の掛け合わせが、これまでのマクロな調整を凌駕し、ついに 0.80 の大台へと連れて行ってくれました。


一つ一つの仮説を積み上げ、データで検証する。エンジニアとしての地道なアプローチが報われた瞬間でした。
しかし、まだ上には上がいます。この勢いを止めることなく、さらなる高みを目指します。



PR