2.4.8
CatBoost
まとめ
- CatBoostはYandexが開発した勾配ブースティング木で、カテゴリ変数をワンホットやラベルエンコーディングなしにそのまま扱える。
- Ordered Target Statistics(順序付きターゲット統計量)により、ターゲットリークを防ぎながらカテゴリ変数を数値に変換する。
- 対称木(Oblivious Tree)を採用し、全ノードで同じ分割条件を使うことで高速な推論と過学習の抑制を両立する。
直感 #
カテゴリ変数(都市名、商品カテゴリなど)を含むデータでは、前処理としてワンホットやラベルエンコーディングが必要になることが多い。CatBoostはカテゴリ変数をネイティブに処理し、ターゲット統計量を使って数値に変換する。さらに、学習データの順序をシャッフルしてから統計量を計算するため、ターゲットリーク(未来の情報の漏洩)を防ぐ仕組みが組み込まれている。
詳細な解説 #
ライブラリと実験データ #
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
from catboost import CatBoostClassifier
まず数値特徴量のみのデータで基本動作を確認します。
X, y = make_classification(
n_samples=1000, n_features=20, n_informative=10,
n_redundant=5, random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42
)
基本的な学習と評価 #
model = CatBoostClassifier(
iterations=100,
depth=6,
learning_rate=0.1,
random_state=42,
verbose=0,
)
model.fit(X_train, y_train)
y_prob = model.predict_proba(X_test)[:, 1]
print(f"ROC-AUC: {roc_auc_score(y_test, y_prob):.4f}")
カテゴリ変数の処理 #
CatBoostの強みはカテゴリ変数をそのまま渡せることです。cat_featuresパラメーターでカテゴリ列のインデックスを指定します。
# カテゴリ変数を含むデータの例
np.random.seed(42)
n = 500
df = pd.DataFrame({
"num_feat1": np.random.randn(n),
"num_feat2": np.random.randn(n),
"cat_feat1": np.random.choice(["A", "B", "C", "D"], n),
"cat_feat2": np.random.choice(["low", "mid", "high"], n),
})
# カテゴリ変数に依存するターゲット
target = (
(df["cat_feat1"].isin(["A", "B"])).astype(int)
+ (df["num_feat1"] > 0).astype(int)
+ np.random.binomial(1, 0.2, n)
) >= 2
target = target.astype(int)
X_train_c, X_test_c, y_train_c, y_test_c = train_test_split(
df, target, test_size=0.3, random_state=42
)
model_cat = CatBoostClassifier(
iterations=100, depth=6, learning_rate=0.1,
random_state=42, verbose=0,
)
model_cat.fit(
X_train_c, y_train_c,
cat_features=["cat_feat1", "cat_feat2"],
)
y_prob_cat = model_cat.predict_proba(X_test_c)[:, 1]
print(f"ROC-AUC (カテゴリ変数あり): {roc_auc_score(y_test_c, y_prob_cat):.4f}")
depthの影響 #
CatBoostのdepthは対称木の深さを制御します。対称木では全ノードが同じ分割条件を使うため、深さが増えると指数的にノード数が増加します。
depths = [2, 4, 6, 8, 10]
scores = []
for d in depths:
m = CatBoostClassifier(
iterations=100, depth=d,
learning_rate=0.1, random_state=42, verbose=0,
)
m.fit(X_train, y_train)
scores.append(roc_auc_score(y_test, m.predict_proba(X_test)[:, 1]))
plt.figure(figsize=(8, 4))
plt.plot(depths, scores, "o-")
plt.xlabel("depth")
plt.ylabel("ROC-AUC")
plt.title("depthと精度の関係")
plt.grid(True)
plt.show()
l2_leaf_regの影響 #
葉の値に対するL2正則化の強さを制御します。大きくすると過学習を抑えられます。
regs = [0.1, 1.0, 3.0, 5.0, 10.0]
scores = []
for r in regs:
m = CatBoostClassifier(
iterations=100, depth=6,
learning_rate=0.1, l2_leaf_reg=r,
random_state=42, verbose=0,
)
m.fit(X_train, y_train)
scores.append(roc_auc_score(y_test, m.predict_proba(X_test)[:, 1]))
plt.figure(figsize=(8, 4))
plt.plot(regs, scores, "o-")
plt.xlabel("l2_leaf_reg")
plt.ylabel("ROC-AUC")
plt.title("L2正則化と精度の関係")
plt.grid(True)
plt.show()
特徴量の重要度 #
importances = model.feature_importances_
indices = np.argsort(importances)[::-1]
plt.figure(figsize=(10, 5))
plt.bar(range(len(importances)), importances[indices])
plt.xlabel("特徴量インデックス")
plt.ylabel("重要度")
plt.title("CatBoost 特徴量重要度")
plt.show()
対称木(Oblivious Tree)とは #
CatBoostはデフォルトで対称木を使います。通常の決定木は各ノードで異なる特徴量・閾値で分割しますが、対称木は同じ深さのノードで同じ分割条件を使います。
- 利点: 推論が高速(全データに同じ条件を一括適用できる)、過学習しにくい
- 制約: 表現力は通常の木より限定的だが、アンサンブルで補う
- Gradient Boosting(回帰) — 勾配ブースティングの基礎
- XGBoost — 正則化重視の勾配ブースティング
- LightGBM — ヒストグラム近似に特化した高速実装