はじめに
今回は、「新米データサイエンティストの日常」シリーズの第2弾になります。
新米データサイエンティストが躓きがちなテーマについて、先輩データサイエンティストと会話しているシーンをChatGPTに生成しもらいました。
第1弾はこちら↓
登場人物(架空)
- 新米データサイエンティスト:新卒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
は数値データだけど、ランダムに埋めると意味がなくなる。だから 中央値で補完 するのが一般的だね。」
df["LotFrontage"] = df["LotFrontage"].fillna(df["LotFrontage"].median())
後輩:「なるほど!じゃあ、Alley
みたいなカテゴリ変数は?」
先輩:「カテゴリ変数なら "None" や "Unknown" で埋める のがいいね。あるいは、欠損を 'has_alley' みたいな 新しい特徴量として追加 する手もある。」
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エンコーディングするのが基本だけど、 カテゴリの種類が多いとカラムが増えすぎる から、ラベルエンコーディングやターゲットエンコーディングを使うこともあるよ。」
from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder()
df["Neighborhood"] = encoder.fit_transform(df["Neighborhood"])
後輩:「へぇ~、ターゲットエンコーディングってなんですか?」
先輩:「カテゴリごとの 目的変数(SalePrice)の平均をエンコード する方法だね。ただし、リークを防ぐために K-foldで分割して計算 するのがコツ。」
シーン5:ベースラインモデルの作成
後輩:「前処理が終わったら、すぐに高精度なモデルを作るんですか?」
先輩:「いや、その前にまずは ベースラインモデル を作ろう。LightGBM や XGBoost をそのまま使って、スコアの目安を作るのが大事。」
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
に保存して提出するんだ。」
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つ ある。
- 特徴量エンジニアリングを改善する
- モデルを工夫する(アルゴリズム選定、ハイパーパラメータ調整、アンサンブル)
- データの扱い方を最適化する(外れ値処理、データ拡張など)
まずは 特徴量エンジニアリング からやってみよう。」
シーン8:特徴量エンジニアリング
後輩:「前処理のときに LotFrontage
を中央値で補完したんですが、それ以外に改善点ってありますか?」
先輩:「例えば 新しい特徴量を作る のがいいね。LotFrontage
を 'LotArea'(土地の広さ)で割ると、土地の形状に関する情報が得られる かもしれない。」
df["Frontage_Ratio"] = df["LotFrontage"] / df["LotArea"]
df["Frontage_Ratio"].fillna(0, inplace=True)
後輩:「なるほど!単純な数値をそのまま使うんじゃなくて、新しい指標を作るんですね。」
先輩:「そうそう。他にも YearBuilt
や YearRemodAdd
から "築年数" の特徴量を作るのもよく使う手法だよ。」
df["HouseAge"] = 2024 - df["YearBuilt"]
df["RemodAge"] = 2024 - df["YearRemodAdd"]
後輩:「あ!それなら GarageYrBlt
も GarageAge
に変えた方がいいですね。」
df["GarageAge"] = 2024 - df["GarageYrBlt"]
df["GarageAge"].fillna(0, inplace=True)
先輩:「いいね!そうやって 意味のある特徴量を増やす と、モデルの予測精度が上がることが多いよ。」
シーン9:モデルの選定
後輩:「次にモデルを強化したいんですが、ランダムフォレスト以外に何がいいですか?」
先輩:「Kaggleでは 勾配ブースティング系のモデル(LightGBM, XGBoost, CatBoost) がよく使われるね。まずは LightGBM
を試してみよう。」
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
ってライブラリを使うといいよ。」
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になったけど、まだ上位の人たちには追いつけないですね…。」
先輩:「そういうときは アンサンブル を試してみよう。複数のモデルを組み合わせることで、より強いモデルを作れる。」
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の醍醐味だからね!」
後輩:「ありがとうございます!引き続き、改善を続けてみます!」