新米データサイエンティストの日常（架空）｜Kaggleチュートリアル編

# はじめに
今回は、「新米データサイエンティストの日常」シリーズの第2弾になります。
新米データサイエンティストが躓きがちなテーマについて、先輩データサイエンティストと会話しているシーンをChatGPTに生成しもらいました。

第1弾はこちら↓

https://qiita.com/UKI_datascience/items/09986bff545acc83a2da

---

# 登場人物（架空）
- **新米データサイエンティスト**：新卒1年目のKaggle初心者。OJTでKaggleのコンペに挑戦中。
- **先輩データサイエンティスト**：入社5年目のKaggle GrandMaster。OJT担当。

---
#### **シーン1：コンペの選定**
**後輩**：「先輩、OJTの一環でKaggleのコンペに参加することになったんですが、どのコンペに挑戦すればいいですか？」

**先輩**：「Kaggleのコンペには大きく分けて **初心者向け（PlaygroundやGetting Started）** と、 **本格的な競争型コンペ（FeaturedやResearch）** があるんだ。最初はデータ処理やモデリングの基礎を学べる **House Prices - Advanced Regression Techniques** や **Titanic - Machine Learning from Disaster** がおすすめだね。」

**後輩**：「Titanicは有名ですね！でも、ちょっと簡単すぎる気がします。」

**先輩**：「じゃあ、 **House Prices** に挑戦しようか。これは回帰問題で、特徴量エンジニアリングが重要になる。データの前処理や扱い方を学ぶのにちょうどいい。」

---
#### **シーン2：データの確認**
**後輩**：「データはもうダウンロードしました。いきなりモデルを作ればいいですか？」

**先輩**：「ダメダメ（笑）。まずはデータを **"理解"** しないと。`train.csv` を `df = pd.read_csv("train.csv")` で読み込んで、 `df.info()` や `df.describe()` でデータの基本情報を確認してみよう。」

**後輩**：「なるほど…あ、`LotFrontage` や `Alley` ってカラムに `NaN` が結構あります！」

**先輩**：「いい気づきだね！データの **欠損値処理** は重要な前処理のひとつ。適切な処理方法を選ぶのがポイントだよ。」

---
#### **シーン3：前処理のコツ**
**後輩**：「欠損値の処理って、`fillna()` で適当に埋めればいいんですか？」

**先輩**：「場合によるね。例えば、`LotFrontage` は数値データだけど、ランダムに埋めると意味がなくなる。だから **中央値で補完** するのが一般的だね。」

```python
df["LotFrontage"] = df["LotFrontage"].fillna(df["LotFrontage"].median())
```

**後輩**：「なるほど！じゃあ、`Alley` みたいなカテゴリ変数は？」

**先輩**：「カテゴリ変数なら **"None" や "Unknown" で埋める** のがいいね。あるいは、欠損を 'has_alley' みたいな **新しい特徴量として追加** する手もある。」

```python
df["Alley"] = df["Alley"].fillna("None")
df["HasAlley"] = df["Alley"].apply(lambda x: 0 if x == "None" else 1)
```

**後輩**：「なるほど、欠損情報自体を特徴量にするのもアリなんですね！」

---
#### **シーン4：特徴量エンジニアリング**
**後輩**：「他に前処理で気をつけることってありますか？」

**先輩**：「例えば、 **カテゴリ変数のエンコーディング** も重要。`pd.get_dummies()` でOne-Hotエンコーディングするのが基本だけど、 **カテゴリの種類が多いとカラムが増えすぎる** から、ラベルエンコーディングやターゲットエンコーディングを使うこともあるよ。」

```python
from sklearn.preprocessing import LabelEncoder

encoder = LabelEncoder()
df["Neighborhood"] = encoder.fit_transform(df["Neighborhood"])
```

**後輩**：「へぇ～、ターゲットエンコーディングってなんですか？」

**先輩**：「カテゴリごとの **目的変数（SalePrice）の平均をエンコード** する方法だね。ただし、リークを防ぐために **K-foldで分割して計算** するのがコツ。」

---
#### **シーン5：ベースラインモデルの作成**
**後輩**：「前処理が終わったら、すぐに高精度なモデルを作るんですか？」

**先輩**：「いや、その前にまずは **ベースラインモデル** を作ろう。LightGBM や XGBoost をそのまま使って、スコアの目安を作るのが大事。」

```python
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_squared_error

X = df.drop("SalePrice", axis=1)
y = df["SalePrice"]

X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=0.2, random_state=42)

model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train, y_train)

preds = model.predict(X_valid)
print("RMSE:", mean_squared_error(y_valid, preds, squared=False))
```

**後輩**：「おお！これで最初のモデルができました！」

**先輩**：「これをベースラインにして、特徴量を増やしたり、ハイパーパラメータを調整したりして改善していくんだよ。」

---
#### **シーン6：Kaggleでの提出**
**後輩**：「できたモデルを提出するにはどうすればいいんですか？」

**先輩**：「予測結果を `.csv` に保存して提出するんだ。」

```python
submission = pd.DataFrame({"Id": test_df["Id"], "SalePrice": model.predict(test_df)})
submission.to_csv("submission.csv", index=False)
```

**後輩**：「簡単ですね！でも、スコアがあまり良くないかも…。」

**先輩**：「それがKaggleの面白いところ！ここから **特徴量を工夫したり、アンサンブルしたりして改善** していくんだよ。」

---
#### **シーン7：ベースラインモデルのスコア確認**
**後輩**：「先輩、ベースラインモデルのRMSEスコアが **0.23** でした。でも、KaggleのLB（リーダーボード）を見たら、上位の人たちは **0.12** とかになってます…。こんなに差があると、どう改善すればいいのか分からなくて。」

**先輩**：「いい質問だね！スコアを改善する方法は大きく分けて **3つ** ある。  
1. **特徴量エンジニアリングを改善する**  
2. **モデルを工夫する（アルゴリズム選定、ハイパーパラメータ調整、アンサンブル）**  
3. **データの扱い方を最適化する（外れ値処理、データ拡張など）**  

まずは **特徴量エンジニアリング** からやってみよう。」

---
#### **シーン8：特徴量エンジニアリング**
**後輩**：「前処理のときに `LotFrontage` を中央値で補完したんですが、それ以外に改善点ってありますか？」

**先輩**：「例えば **新しい特徴量を作る** のがいいね。`LotFrontage` を **'LotArea'（土地の広さ）で割ると、土地の形状に関する情報が得られる** かもしれない。」

```python
df["Frontage_Ratio"] = df["LotFrontage"] / df["LotArea"]
df["Frontage_Ratio"].fillna(0, inplace=True)
```

**後輩**：「なるほど！単純な数値をそのまま使うんじゃなくて、新しい指標を作るんですね。」

**先輩**：「そうそう。他にも `YearBuilt` や `YearRemodAdd` から **"築年数"** の特徴量を作るのもよく使う手法だよ。」

```python
df["HouseAge"] = 2024 - df["YearBuilt"]
df["RemodAge"] = 2024 - df["YearRemodAdd"]
```

**後輩**：「あ！それなら `GarageYrBlt` も `GarageAge` に変えた方がいいですね。」

```python
df["GarageAge"] = 2024 - df["GarageYrBlt"]
df["GarageAge"].fillna(0, inplace=True)
```

**先輩**：「いいね！そうやって **意味のある特徴量を増やす** と、モデルの予測精度が上がることが多いよ。」

---
#### **シーン9：モデルの選定**
**後輩**：「次にモデルを強化したいんですが、ランダムフォレスト以外に何がいいですか？」

**先輩**：「Kaggleでは **勾配ブースティング系のモデル（LightGBM, XGBoost, CatBoost）** がよく使われるね。まずは `LightGBM` を試してみよう。」

```python
from lightgbm import LGBMRegressor

model = LGBMRegressor(n_estimators=1000, learning_rate=0.05, num_leaves=31, random_state=42)
model.fit(X_train, y_train, eval_set=[(X_valid, y_valid)], early_stopping_rounds=100, verbose=50)

preds = model.predict(X_valid)
print("RMSE:", mean_squared_error(y_valid, preds, squared=False))
```

**後輩**：「おお、ランダムフォレストよりスコアが **0.18** まで改善しました！」

**先輩**：「LightGBMは計算も速くて便利だよ。他にも、 **XGBoost** や **CatBoost** も試すといいね。」

---
#### **シーン10：ハイパーパラメータチューニング**
**後輩**：「でも、`num_leaves` や `learning_rate` って適当に決めました…。これってどうやって調整するんですか？」

**先輩**：「ハイパーパラメータの最適化には `Optuna` ってライブラリを使うといいよ。」

```python
import optuna

def objective(trial):
    param = {
        "n_estimators": trial.suggest_int("n_estimators", 100, 2000),
        "learning_rate": trial.suggest_loguniform("learning_rate", 0.01, 0.3),
        "num_leaves": trial.suggest_int("num_leaves", 20, 100),
        "max_depth": trial.suggest_int("max_depth", 3, 12),
    }
    
    model = LGBMRegressor(**param, random_state=42)
    model.fit(X_train, y_train, eval_set=[(X_valid, y_valid)], early_stopping_rounds=100, verbose=0)
    
    preds = model.predict(X_valid)
    return mean_squared_error(y_valid, preds, squared=False)

study = optuna.create_study(direction="minimize")
study.optimize(objective, n_trials=50)

print("Best Params:", study.best_params)
```

**後輩**：「なるほど…これを使えば、自動でいいパラメータを見つけられるんですね！」

**先輩**：「そうそう。手動で調整するより効率がいいし、精度も上がりやすいよ。」

---
#### **シーン11：アンサンブル**
**後輩**：「LightGBMのスコアは0.16になったけど、まだ上位の人たちには追いつけないですね…。」

**先輩**：「そういうときは **アンサンブル** を試してみよう。複数のモデルを組み合わせることで、より強いモデルを作れる。」

```python
from sklearn.ensemble import StackingRegressor
from xgboost import XGBRegressor
from catboost import CatBoostRegressor

lgb = LGBMRegressor(n_estimators=1000, learning_rate=0.05, num_leaves=31, random_state=42)
xgb = XGBRegressor(n_estimators=1000, learning_rate=0.05, random_state=42)
cat = CatBoostRegressor(n_estimators=1000, learning_rate=0.05, verbose=0, random_state=42)

stacking_model = StackingRegressor(
    estimators=[("lgb", lgb), ("xgb", xgb), ("cat", cat)],
    final_estimator=RandomForestRegressor(n_estimators=500, random_state=42)
)

stacking_model.fit(X_train, y_train)
preds = stacking_model.predict(X_valid)
print("RMSE:", mean_squared_error(y_valid, preds, squared=False))
```

**後輩**：「おお！スコアが **0.14** まで改善しました！でも、まだトップとの差がありますね。」

**先輩**：「そうだね。あとは **外れ値の処理やデータの拡張** も試すといいよ。それに、上位陣のNotebookを参考にするのも大事。」

---
#### **シーン12：最終的なまとめ**
**後輩**：「今日は **特徴量エンジニアリング、モデル選定、ハイパーパラメータ調整、アンサンブル** を学びました！Kaggleって、ほんとに奥が深いですね。」

**先輩**：「そうだね。最初は大変だけど、慣れれば楽しくなるよ。とにかく試行錯誤を繰り返して、少しずつスコアを改善していくのがKaggleの醍醐味だからね！」

**後輩**：「ありがとうございます！引き続き、改善を続けてみます！」