【Kaggle挑戦記】Titanic 攻略 #15:0.78468 不変。ハイパーパラメータ最適化が証明した「モデルの限界」
前回(攻略 #14)、デッキ情報の追加でスコアを落とした反省を活かし、今回はベストスコア 0.78468 を出した最強の布陣に対し、科学的なメスを入れました。GridSearchCV(交差検証)による「木の深さ」の最適化。しかし、返ってきたスコアは驚くほど正確に前回と同じ 0.78468 でした。
1. なぜ「最適化」したのにスコアが変わらなかったのか?
勘に頼っていた max_depth=5 という設定を、交差検証によって [3, 4, 5, 6, 7, 8] の中から最も優れたものへ自動選択させました。それにも関わらずスコアが不変だった理由。そこにはエンジニアとして納得のいく理由が隠れています。
- 「深さ5」がすでに黄金比だった: 交差検証の結果、実はこれまでの「深さ5」が、学習データとテストデータのバランスを保つ上で既に最適な値であった可能性が高いです。
- 特徴量の表現力の限界: パラメータという「火加減」を調整してもスコアが動かないのは、材料である「特徴量」が持つ情報の限界に達していることを意味します。
- 高い汎化性能の証明: スコアが落ちなかったということは、モデルが変に過学習せず、安定した予測能力を維持できている証拠でもあります。
2. 【実装】GridSearchCV による最適化の全記録
結果は維持でしたが、今後の試行錯誤において「確信」を持ってパラメータを設定するための必須ステップです。
import pandas as pd
import numpy as np
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV
# 1. データの読み込み
train_data = pd.read_csv('train.csv')
test_data = pd.read_csv('test.csv')
# 2. 前処理(ベストスコア時の構成を維持)
train_data['Embarked'] = train_data['Embarked'].fillna('S')
test_data['Embarked'] = test_data['Embarked'].fillna('S')
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'))
for df in [train_data, test_data]:
df['FamilySize'] = df['SibSp'] + df['Parch'] + 1
test_data['Fare'] = test_data['Fare'].fillna(test_data['Fare'].median())
# 3. 特徴量のダミー変数化
features = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "FamilySize", "Embarked"]
X = pd.get_dummies(train_data[features])
y = train_data["Survived"]
X_test = pd.get_dummies(test_data[features])
X, X_test = X.align(X_test, join='left', axis=1, fill_value=0)
# 4. パラメータの探索
param_grid = {
'n_estimators': [500],
'max_depth': [3, 4, 5, 6, 7, 8],
'min_samples_leaf': [1, 3, 5],
'random_state': [1]
}
# 5. グリッドサーチによる交差検証
grid_search = GridSearchCV(
estimator=RandomForestClassifier(),
param_grid=param_grid,
cv=5,
n_jobs=-1,
verbose=1
)
grid_search.fit(X, y)
# 最適なモデルを抽出
best_model = grid_search.best_estimator_
predictions = best_model.predict(X_test)
# 6. 出力
output = pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': predictions})
output.to_csv('submission_optimized.csv', index=False)
print(f"Best Params found: {grid_search.best_params_}")
3. 考察:次のフェーズは「材料そのもの」の変革
エンジニア的な視点:
今回の実験で、「今の特徴量の組み合わせ(Embarked, FamilySizeなど)を、今のランダムフォレストで回す限り、これ以上の伸び代はない」ということがはっきりしました。0.78468 は一つの完成形です。ここから 0.79、0.80 を目指すには、微調整(チューニング)ではなく、革新(イノベーション)が必要です。
スコアが変わらなかったことは、決して無駄ではありません。「迷い」が「確信」に変わった瞬間です。次回からは、これまでの安定した布陣をベースにしつつ、全く新しいアルゴリズム(XGBoostやLightGBM)を試すか、あるいは「チケット番号の重複」など、より高度な特徴量生成(Feature Engineering)の深淵へと足を踏み入れます!
PR