🐈

Kaggleメダル獲得への挑戦 Vol.5 〜ベースライン→特徴量エンジニアリング

に公開

はじめに

この記事は30歳未経験からエンジニアに転身したド文系の僕が、機械学習エンジニアになるためにKaggleに挑戦しながらスキルを身につけていく過程をリアルタイムで発信していく連載記事となります。

毎週末、その週に僕自身が学んだ内容を発信していきます。
学びながら発信していきますので、場合によっては間違った情報や、誤って理解してしまっているものもあるかもしれません。
その際はぜひ温かくご指摘をいただけるとうれしいです!
ド文系でも本気で取り組めば結果を出せるんだという姿を見せられるように、日々頑張っていきます!

Vol.1はこちら

2025年5月26日〜6月1日までの学び

注)Kaggleは英語で構成されているため、その用語に慣れるためにも、主要な用語は英語のまま学ぶこととしています。
なので以下の記事でもところどころ英語のまま記載していますが、それは上記の意図があって敢えてそう表記しているものなので、その点だけご理解ください。

今週は一度基本に立ち返って、機械学習で分析する際の分析手順の流れについて学んできたので、そちらについて解説していきます。

機械学習による分析の進め方の流れ

基本的には以下の通り進めていくのが一般的。

  • ベースライン作成
    最初に「とりあえずのモデル」を作って、どの程度精度が出るか確認する

  • 特徴量エンジニアリング
    モデルが賢くなるように、元データから新しい特徴量を作ったり、不要なものを除外したりする

  • モデルチューニング
    モデルのハイパーパラメーターや手法を改善して、スコアを最大化する

先週まで僕がやっていたのは主に「特徴量エンジニアリング」。
つまりベースライン作成のところをすっ飛ばしていきなり2番目の手順を学習していたことになる。
だから「何のためにこの処理を行なっているのかよく分からない」的な状況に陥ってしまうことが多々あったのかなと感じた。
なので基本に立ち返ってベースライン作成から始めてみる。

ベースライン作成とは?

現場のデータを使って最もシンプルな方法でモデルを作って、評価指標を計算してみること。

これを一番初めに実施する理由は以下の通り
・目標を明確にするため(最初に出たスコアが改善の基準となる)
・データや目的変数の分布を理解するため
・予測がどの程度難しい問題なのか?を把握するため

例えば、今取り組んでいる住宅価格の予測するタスクであれば、とりあえず「全住宅の価格の平均を返すだけのモデル」でもベースラインとして使用可能。
案外こんな簡単なモデルでも一定の精度が出たりするケースもあるので、まずは簡単なベースラインから始めていくといい。

ベースライン作成をしっかり行なっておくと、その後に実施するモデルの改善が「本当に意味のあるものだったかどうか」が評価できるようになる。

例えばモデル改善により「精度90%」となったとしても、ベースラインの精度が元々「88%」だったのであれば、あまり進歩していないと判断できる。

ベースラインを作らずにいきなりXGBoostや深層学習に突っ込むと「改善したのか悪化したのか」が分からなくなるため、最初の一歩としてベースライン作成は非常に重要。

ベースライン作成を踏まえた具体的コード

ということで、今取り組んでいる住宅価格の予測タスクを「ベースライン作成→特徴量エンジニアリング」の流れで処理するコードを以下の通り作成した。

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import KFold, cross_val_score
from sklearn.linear_model import LinearRegression
from xgboost import XGBRegressor


def load_data(path="../data/train.csv"):
    """
    データをCSVファイルから読み込み、PandasのDataFrameとして返す関数。
    引数:
        path: 読み込むCSVファイルのファイルパス
    戻り値:
        読み込まれたデータ(DataFrame)
    """
    df = pd.read_csv(path)
    return df


def baseline_mean_model(df, target):
    """
    機械学習における最もシンプルなベースラインモデルを構築する関数。
    モデルは、全ての予測を目的変数の平均値で置き換える。
    その結果、MSE(平均二乗誤差)を計算して出力する。
    引数:
        df: 入力データ(DataFrame)
        target: 目的変数(予測対象)の列名
    戻り値:
        MSEの値
    """
    mean_price = df[target].mean()
    baseline_preds = pd.Series(mean_price, index=df.index)
    mse = mean_squared_error(df[target], baseline_preds)
    print(f"【平均予測のMSE】: {mse:,.2f}")
    return mse


def simple_feature_model(df, target, feature):
    """
    単一の特徴量(例: GrLivArea)を使って線形回帰モデルを構築し、
    5分割の交差検証によりモデルの平均MSEを評価する。
    引数:
        df: 入力データ(DataFrame)
        target: 目的変数の列名
        feature: 使用する特徴量の列名
    戻り値:
        平均MSEの値
    """
    X = df[[feature]]
    y = df[target]
    model = LinearRegression()
    kf = KFold(n_splits=5, shuffle=True, random_state=42)
    neg_mse_scores = cross_val_score(model, X, y, scoring='neg_mean_squared_error', cv=kf)
    mse_scores = -neg_mse_scores
    print(f"【{feature}単体モデルの平均MSE(交差検証)】: {mse_scores.mean():,.2f}")
    return mse_scores.mean()


def engineer_features(df):
    """
    基本的な特徴量エンジニアリングを行う関数。
    - HouseAge: 現在(2025年)から建築年を引いた築年数
    - TotalArea: 地上面積と地下面積の合計
    引数:
        df: 入力データ
    戻り値:
        特徴量を追加したDataFrame
    """
    df = df.copy()
    df["HouseAge"] = 2025 - df["YearBuilt"]
    df["TotalArea"] = df["GrLivArea"] + df["TotalBsmtSF"]
    return df


def add_advanced_features(df):
    """
    より複雑でドメイン知識に基づいた特徴量を追加する関数。
    - PricePerSqft: 1平方フィートあたりの価格(価格 / 面積)
    - RoomDensity: 1部屋あたりの平均面積(面積 / 部屋数)
    - QualityScore: 品質評価×面積÷築年数(高級感や住みやすさを反映)
    引数:
        df: 特徴量エンジニアリング済みのDataFrame
    戻り値:
        複雑な特徴量を加えたDataFrame
    """
    df = df.copy()
    df["PricePerSqft"] = df["SalePrice"] / (df["GrLivArea"] + 1)
    df["RoomDensity"] = df["GrLivArea"] / (df["TotRmsAbvGrd"] + 1)
    df["QualityScore"] = (df["OverallQual"] ** 2) * df["GrLivArea"] / (df["HouseAge"] + 1)
    return df


def engineered_feature_model(df, target, features):
    """
    複数の特徴量を組み合わせて線形回帰モデルを構築し、
    5分割交差検証によってその平均MSEを評価する関数。
    引数:
        df: 入力データ(特徴量エンジニアリング済)
        target: 予測対象の列名
        features: 使用する特徴量のリスト
    戻り値:
        平均MSEの値
    """
    X = df[features]
    y = df[target]
    model = LinearRegression()
    kf = KFold(n_splits=5, shuffle=True, random_state=42)
    neg_mse_scores = cross_val_score(model, X, y, scoring='neg_mean_squared_error', cv=kf)
    mse_scores = -neg_mse_scores
    print(f"【特徴量エンジニアリング後モデルの平均MSE(交差検証)】: {mse_scores.mean():,.2f}")
    return mse_scores.mean()


def evaluate_xgboost_model(df, target, features):
    """
    XGBoostモデルを用いて複数の特徴量による価格予測を行い、
    交差検証でMSEを評価し、さらに特徴量重要度を可視化する関数。
    引数:
        df: 入力データ
        target: 予測対象の列名
        features: 使用する特徴量のリスト
    戻り値:
        平均MSEの値
    """
    X = df[features]
    y = df[target]
    model = XGBRegressor(n_estimators=100, random_state=42, verbosity=0)
    kf = KFold(n_splits=5, shuffle=True, random_state=42)
    scores = cross_val_score(model, X, y, scoring='neg_mean_squared_error', cv=kf)
    mse = -scores.mean()
    print(f"【XGBoostモデルの平均MSE】: {mse:,.2f}")

    model.fit(X, y)
    importance = model.feature_importances_
    importance_df = pd.DataFrame({
        'Feature': features,
        'Importance': importance
    }).sort_values(by='Importance', ascending=False)

    plt.figure(figsize=(8, 5))
    sns.barplot(data=importance_df, x='Importance', y='Feature')
    plt.title("XGBoost Feature Importance")
    plt.tight_layout()
    plt.show()

    return mse


def main():
    df = load_data()
    target = 'SalePrice'

    # Step 1: 平均によるベースライン
    baseline_mean_model(df, target)

    # Step 2: GrLivArea のみ使用したモデル(交差検証付き)
    simple_feature_model(df, target, feature='GrLivArea')

    # Step 3: 基本特徴量エンジニアリング
    df_eng = engineer_features(df)
    engineered_feature_model(df_eng, target, features=['GrLivArea', 'HouseAge', 'TotalArea'])

    # Step 4: さらに高度な特徴量追加
    df_adv = add_advanced_features(df_eng)
    advanced_features = ['GrLivArea', 'HouseAge', 'TotalArea', 'PricePerSqft', 'RoomDensity', 'QualityScore']

    # Step 5: 線形回帰モデル評価(高度な特徴量込み)
    engineered_feature_model(df_adv, target, features=advanced_features)

    # Step 6: XGBoostモデル評価と重要度可視化
    evaluate_xgboost_model(df_adv, target, features=advanced_features)


if __name__ == "__main__":
    main()

結果は以下の通り

【平均予測のMSE】: 6,306,788,585.35
【GrLivArea単体モデルの平均MSE(交差検証)】: 3,204,919,519.84
【特徴量エンジニアリング後モデルの平均MSE(交差検証)】: 2,058,361,441.05
【特徴量エンジニアリング後モデルの平均MSE(交差検証)】: 612,988,051.86
【XGBoostモデルの平均MSE】: 357,363,848.00

結果からわかる通り、一番初めに作成したベースラインである「平均予測のMSE」モデルから、一番最後に行なっている「XGBoostモデルの平均MSE」に進むにつれて徐々にモデルの精度が向上しているのがわかる
※MSEは値が小さいほど精度がいいと判断できる指標

あとはより精度を高めるために、さらに複雑な特徴量エンジニアリングを加えたり、モデルチューニングを実施したりしていけば良い。

そのためにはこのデータの背景にある意味や特徴、その分野に関する詳しい情報(例えば、どのような間取りが人気があるのか/ないのか、どのエリアがここ数年で人気になってきているのか、今後の都市計画予定は?など)を踏まえながらデータ分析を実施していく必要がある。

この部分にどこまで本気で取り組めるかが精度向上の鍵。

今週の学びは以上です。
次週も頑張って学びを積み上げていきます!

未経験からエンジニア転職を目指すあなたへ

僕は今「未経験から本気でエンジニア転職を目指す人」のための新しいサポートサービスを準備中です。

もしあなたが…

  • 業務改善を通じて価値を出せるエンジニアを目指したい
  • 数字や仕組みで現場を変える力を、キャリアに活かしたい
  • これから学ぶべきステップを明確にしたい

そんな想いを少しでも持っているなら、ぜひ僕の公式LINEに登録しておいてください。
サービスに関する先行案内や最新情報を優先的にお届けします。
👉公式LINEはこちら

Discussion