カテゴリカル特徴を持つ勾配ブースティング

Beginner

This tutorial is from open-source community. Access the source code

はじめに

この実験では、グラディエントブースティング推定器におけるカテゴリカル特徴の処理方法を比較するために、エイムズ住宅データセットを使用します。このデータセットには数値型とカテゴリカル型の両方の特徴が含まれており、目的は住宅の販売価格です。4 つの異なるパイプラインの性能を比較します。

  • カテゴリカル特徴を削除する
  • カテゴリカル特徴をワンホットエンコードする
  • カテゴリカル特徴を順序値として扱う
  • グラディエントブースティング推定器におけるネイティブなカテゴリカルサポートを使用する

クロスバリデーションを使用して、パイプラインの適合時間と予測性能の観点から評価します。

VM のヒント

VM の起動が完了したら、左上隅をクリックしてノートブックタブに切り替え、Jupyter Notebook を使って練習しましょう。

時々、Jupyter Notebook が読み込み終了するまで数秒待つ必要があります。Jupyter Notebook の制限により、操作の検証を自動化することはできません。

学習中に問題がある場合は、Labby にお問い合わせください。セッション後にフィードバックを提供してください。すぐに問題を解決いたします。

データセットの読み込み

Scikit-Learn のfetch_openml関数を使ってエイムズ住宅データセットを読み込み、サンプルを高速化するために特徴量のサブセットを選択します。また、カテゴリカル特徴量を'category'型に変換します。

from sklearn.datasets import fetch_openml

X, y = fetch_openml(data_id=42165, as_frame=True, return_X_y=True, parser="pandas")

## Select only a subset of features of X to make the example faster to run
categorical_columns_subset = [
    "BldgType",
    "GarageFinish",
    "LotConfig",
    "Functional",
    "MasVnrType",
    "HouseStyle",
    "FireplaceQu",
    "ExterCond",
    "ExterQual",
    "PoolQC",
]

numerical_columns_subset = [
    "3SsnPorch",
    "Fireplaces",
    "BsmtHalfBath",
    "HalfBath",
    "GarageCars",
    "TotRmsAbvGrd",
    "BsmtFinSF1",
    "BsmtFinSF2",
    "GrLivArea",
    "ScreenPorch",
]

X = X[categorical_columns_subset + numerical_columns_subset]
X[categorical_columns_subset] = X[categorical_columns_subset].astype("category")

ベースラインパイプライン - カテゴリカル特徴の削除

カテゴリカル特徴を削除し、HistGradientBoostingRegressor 推定器を学習するパイプラインを作成します。

from sklearn.ensemble import HistGradientBoostingRegressor
from sklearn.pipeline import make_pipeline
from sklearn.compose import make_column_transformer
from sklearn.compose import make_column_selector

dropper = make_column_transformer(
    ("drop", make_column_selector(dtype_include="category")), remainder="passthrough"
)
hist_dropped = make_pipeline(dropper, HistGradientBoostingRegressor(random_state=42))

ワンホットエンコードパイプライン

カテゴリカル特徴をワンホットエンコードし、HistGradientBoostingRegressor 推定器を学習するパイプラインを作成します。

from sklearn.preprocessing import OneHotEncoder

one_hot_encoder = make_column_transformer(
    (
        OneHotEncoder(sparse_output=False, handle_unknown="ignore"),
        make_column_selector(dtype_include="category"),
    ),
    remainder="passthrough",
)

hist_one_hot = make_pipeline(
    one_hot_encoder, HistGradientBoostingRegressor(random_state=42)
)

順序エンコードパイプライン

カテゴリカル特徴を順序値として扱い、HistGradientBoostingRegressor 推定器を学習するパイプラインを作成します。カテゴリカル特徴をエンコードするために OrdinalEncoder を使用します。

from sklearn.preprocessing import OrdinalEncoder
import numpy as np

ordinal_encoder = make_column_transformer(
    (
        OrdinalEncoder(handle_unknown="use_encoded_value", unknown_value=np.nan),
        make_column_selector(dtype_include="category"),
    ),
    remainder="passthrough",
    verbose_feature_names_out=False,
)

hist_ordinal = make_pipeline(
    ordinal_encoder, HistGradientBoostingRegressor(random_state=42)
)

ネイティブカテゴリカルサポートパイプライン

HistGradientBoostingRegressor 推定器のネイティブカテゴリカルサポートを使ってカテゴリカル特徴を処理するパイプラインを作成します。データの前処理には依然として OrdinalEncoder を使用します。

hist_native = make_pipeline(
    ordinal_encoder,
    HistGradientBoostingRegressor(
        random_state=42,
        categorical_features=categorical_columns,
    ),
).set_output(transform="pandas")

モデル比較

クロスバリデーションを使用して 4 つのパイプラインの性能を比較し、適合時間と平均絶対パーセンテージエラースコアをプロットします。

from sklearn.model_selection import cross_validate
import matplotlib.pyplot as plt

scoring = "neg_mean_absolute_percentage_error"
n_cv_folds = 3

dropped_result = cross_validate(hist_dropped, X, y, cv=n_cv_folds, scoring=scoring)
one_hot_result = cross_validate(hist_one_hot, X, y, cv=n_cv_folds, scoring=scoring)
ordinal_result = cross_validate(hist_ordinal, X, y, cv=n_cv_folds, scoring=scoring)
native_result = cross_validate(hist_native, X, y, cv=n_cv_folds, scoring=scoring)

def plot_results(figure_title):
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 8))

    plot_info = [
        ("fit_time", "Fit times (s)", ax1, None),
        ("test_score", "Mean Absolute Percentage Error", ax2, None),
    ]

    x, width = np.arange(4), 0.9
    for key, title, ax, y_limit in plot_info:
        items = [
            dropped_result[key],
            one_hot_result[key],
            ordinal_result[key],
            native_result[key],
        ]

        mape_cv_mean = [np.mean(np.abs(item)) for item in items]
        mape_cv_std = [np.std(item) for item in items]

        ax.bar(
            x=x,
            height=mape_cv_mean,
            width=width,
            yerr=mape_cv_std,
            color=["C0", "C1", "C2", "C3"],
        )
        ax.set(
            xlabel="Model",
            title=title,
            xticks=x,
            xticklabels=["Dropped", "One Hot", "Ordinal", "Native"],
            ylim=y_limit,
        )
    fig.suptitle(figure_title)

plot_results("Gradient Boosting on Ames Housing")

分割数の制限

木の数と各木の深さを制限することで、人工的に分割の総数を制限したアンダーフィットモデルを使って、同じ分析を再度実行します。

for pipe in (hist_dropped, hist_one_hot, hist_ordinal, hist_native):
    pipe.set_params(
        histgradientboostingregressor__max_depth=3,
        histgradientboostingregressor__max_iter=15,
    )

dropped_result = cross_validate(hist_dropped, X, y, cv=n_cv_folds, scoring=scoring)
one_hot_result = cross_validate(hist_one_hot, X, y, cv=n_cv_folds, scoring=scoring)
ordinal_result = cross_validate(hist_ordinal, X, y, cv=n_cv_folds, scoring=scoring)
native_result = cross_validate(hist_native, X, y, cv=n_cv_folds, scoring=scoring)

plot_results("Gradient Boosting on Ames Housing (few and small trees)")

まとめ

この実験では、エームズ住宅データセットを使って、勾配ブースティング推定器におけるカテゴリカル特徴の処理に対して、4 つの異なるパイプラインを比較しました。カテゴリカル特徴を削除すると予測性能が低下することがわかり、カテゴリカル特徴を使用した 3 つのモデルは誤差率が比較可能であることがわかりました。カテゴリカル特徴をワンホットエンコードするのは、最も遅い方法でしたが、カテゴリカル特徴を順序値として扱い、HistGradientBoostingRegressor 推定器のネイティブカテゴリカルサポートを使用すると、適合時間が似ていました。分割の総数を制限した場合、ネイティブカテゴリカルサポート戦略が最も良い性能を発揮しました。