Machine Learning: アンサンブル学習
最終更新:2026-08-26
三人寄れば文殊の知恵 — ブースティングは弱い学習者を連鎖させて強い学習者へと成長させます。XGBoost/LightGBMは、それを産業グレードにまで高めたターボチャージド版です。
1. この章で学ぶこと
- ブースティングの基礎:AdaBoost → Gradient Boosting → XGBoost/LightGBMへの進化の道筋
- XGBoost:正則化された目的関数、列サンプリング、近似分位点分割、GPUアクセラレーション
- LightGBM:GOSS/EFBの革新、ヒストグラムベースの分割、カテゴリカル特徴量のネイティブサポート、学習速度の優位性
- ハイパーパラメータのチューニング:learning_rate/n_estimators/max_depth/num_leaves/reg_alpha/reg_lambda
- Bobのコアモデル選定:SalesPredictデータにおけるXGBoostとLightGBMの性能比較
2. アルゴリズムエンジニアの実体験
(1) 課題:ランダムフォレストは百万件規模のデータでは遅すぎる
BobのSalesPredictデータは100万行にまで成長し、100本の決定木を持つランダムフォレストの学習に45分かかります。1日に10回の実験を回す必要があるため、学習の待ち時間だけで7.5時間が消えてしまいます。Aliceの米国データは200万行もあり、さらに時間がかかります。学習速度が実験効率を制限し、間接的にモデルのイテレーションを遅らせてしまいます。
(2) XGBoost/LightGBMによる解決策
ヒストグラムベースの分割や列サンプリングなどの最適化により、XGBoostとLightGBMは学習時間を5〜10倍短縮しつつ、精度もさらに向上させます。
PYTHON
import xgboost as xgb
dtrain = xgb.DMatrix(X_train, label=y_train)
params = {"objective": "reg:squarederror", "max_depth": 6, "learning_rate": 0.1}
model = xgb.train(params, dtrain, num_boost_round=500)
(3) 成果:学習時間が45分から5分へ短縮
BobがランダムフォレストをLightGBMに置き換えたところ、100万サンプルの学習が45分から5分に短縮されました。1日に80回以上の実験が可能になり、モデルのイテレーション速度が9倍に向上しました。
3. ブースティングのアンサンブル原理
(1) AdaBoostからGradient Boostingへ
ブースティングの核心的な考え方:弱い学習者を逐次的に学習させ、各学習者は前の学習者の誤りを修正することに集中します。
graph TB
DATA[Original Data] --> M1[Model 1<br/>Weak Learner]
M1 --> E1[Errors from M1]
E1 --> W1[Up-weight Errors]
W1 --> M2[Model 2<br/>Focus on Hard Samples]
M2 --> E2[Residual Errors]
E2 --> M3[Model 3<br/>Focus on Remaining Errors]
M3 --> FINAL[Final Prediction<br/>= M1 + M2 + M3]
▶ サンプル:GradientBoostingを手作業で理解する
PYTHON
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.metrics import mean_squared_error
import numpy as np
rng = np.random.default_rng(42)
X = rng.uniform(0, 10, (200, 1))
y = np.sin(X.squeeze()) + rng.normal(0, 0.2, 200)
# Step-by-step boosting visualization
residuals = y.copy()
predictions = np.zeros_like(y, dtype=float)
learning_rate = 0.1
for i in range(1, 51):
tree = DecisionTreeRegressor(max_depth=3)
tree.fit(X, residuals)
update = learning_rate * tree.predict(X)
predictions += update
residuals = y - predictions
if i in [1, 5, 10, 50]:
mse = mean_squared_error(y, predictions)
print(f"Round {i:2d}: MSE={mse:.4f}")
# Compare with sklearn's GradientBoosting
gb = GradientBoostingRegressor(n_estimators=50, max_depth=3, learning_rate=0.1, random_state=42)
gb.fit(X, y)
print(f"\nsklearn GB MSE: {mean_squared_error(y, gb.predict(X)):.4f}")
出力:
TEXT
📖 参照専用
# Runs successfully
(2) ブースティングの進化比較
| 比較軸 | AdaBoost | Gradient Boosting | XGBoost | LightGBM |
|---|---|---|---|---|
| 誤り修正の方法 | サンプルの重み付け | 残差勾配のフィッティング | 二次勾配+正則化 | 二次勾配+GOSS |
| 正則化 | なし | 弱い | 強い(L1+L2) | 強い(L1+L2) |
| 分割アルゴリズム | 厳密法 | 厳密法 | 近似分位点 | ヒストグラム |
| 列サンプリング | なし | なし | あり | あり |
| 速度 | 遅い | 遅い | 速い | 最速 |
4. XGBoostの詳細
(1) XGBoostの主な革新点
▶ サンプル:XGBoostの回帰学習
PYTHON
import xgboost as xgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, r2_score
import numpy as np
rng = np.random.default_rng(42)
n = 10000
X = rng.uniform(0, 100, (n, 8))
y = (50 + 0.8 * X[:, 0] + 1.2 * X[:, 1] - 0.5 * X[:, 2]
+ 0.3 * X[:, 3] * X[:, 4] # Interaction term
+ rng.normal(0, 5, n))
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# XGBoost with sklearn API
model = xgb.XGBRegressor(
n_estimators=500,
max_depth=6,
learning_rate=0.1,
subsample=0.8,
colsample_bytree=0.8,
reg_alpha=0.1,
reg_lambda=1.0,
random_state=42,
early_stopping_rounds=50,
)
model.fit(
X_train, y_train,
eval_set=[(X_test, y_test)],
verbose=False,
)
y_pred = model.predict(X_test)
print(f"R²: {r2_score(y_test, y_pred):.4f}")
print(f"MAE: {mean_absolute_error(y_test, y_pred):.2f}")
print(f"Best iteration: {model.best_iteration}")
出力:
TEXT
📖 参照専用
# Runs successfully
(2) XGBoostの主要パラメータ
| パラメータ | 意味 | 推奨範囲 | 効果 |
|---|---|---|---|
| n_estimators | 決定木の本数 | 100-5000 | early_stoppingと併用 |
| max_depth | 決定木の最大深度 | 3-10 | 大きいほど過学習のリスク |
| learning_rate | 学習率 | 0.01-0.3 | 小さいほど多くの木が必要 |
| subsample | 行サンプリング比率 | 0.6-1.0 | <1で過学習を抑制 |
| colsample_bytree | 列サンプリング比率 | 0.6-1.0 | <1で過学習を抑制 |
| reg_alpha | L1正則化 | 0-10 | 大きいほどスパースに |
| reg_lambda | L2正則化 | 0-10 | 大きいほど滑らかに |
5. LightGBMの詳細
(1) LightGBMの2大革新技術
- GOSS(Gradient-based One-Side Sampling):勾配が大きいサンプルを保持し、勾配が小さいサンプルはランダムにサンプリングします
- EFB(Exclusive Feature Bundling):相互に排他的なスパース特徴量をバンドルし、特徴量の数を削減します
▶ サンプル:LightGBMの回帰学習
PYTHON
import lightgbm as lgb
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error, r2_score
import numpy as np
# Using same data as XGBoost example
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# LightGBM with sklearn API
model = lgb.LGBMRegressor(
n_estimators=500,
max_depth=-1, # -1 means no limit, use num_leaves instead
num_leaves=31,
learning_rate=0.1,
subsample=0.8,
colsample_bytree=0.8,
reg_alpha=0.1,
reg_lambda=1.0,
random_state=42,
verbose=-1,
)
model.fit(
X_train, y_train,
eval_set=[(X_test, y_test)],
callbacks=[lgb.early_stopping(50, verbose=False)],
)
y_pred = model.predict(X_test)
print(f"R²: {r2_score(y_test, y_pred):.4f}")
print(f"MAE: {mean_absolute_error(y_test, y_pred):.2f}")
print(f"Best iteration: {model.best_iteration_}")
出力:
TEXT
📖 参照専用
# Runs successfully
(2) LightGBMのカテゴリカル特徴量ネイティブサポート
▶ サンプル:カテゴリカル特徴量をLightGBMに直接渡す
PYTHON
import lightgbm as lgb
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score
rng = np.random.default_rng(42)
n = 5000
df = pd.DataFrame({
"ad_spend_k": rng.uniform(5, 100, n),
"traffic_k": rng.uniform(10, 500, n),
"category": rng.choice(["Elec", "Cloth", "Food", "Book", "Home"], n),
"region": rng.choice(["US", "EU", "CN"], n),
})
df["revenue_k"] = (
20 + 0.6 * df["ad_spend_k"] + 0.08 * df["traffic_k"]
+ df["category"].map({"Elec": 30, "Cloth": 15, "Food": 5, "Book": 3, "Home": 40})
+ rng.normal(0, 10, n)
)
X = df.drop(columns=["revenue_k"])
y = df["revenue_k"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# LightGBM native categorical feature support
model = lgb.LGBMRegressor(
n_estimators=200, learning_rate=0.1,
categorical_feature=["category", "region"], # Direct categorical support
random_state=42, verbose=-1,
)
model.fit(X_train, y_train)
print(f"R² with native categorical: {r2_score(y_test, model.predict(X_test)):.4f}")
出力:
TEXT
📖 参照専用
# Runs successfully
6. XGBoostとLightGBMの性能比較
▶ サンプル:SalesPredictデータセットでの比較
PYTHON
import xgboost as xgb
import lightgbm as lgb
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import mean_absolute_error, r2_score
import time
import numpy as np
rng = np.random.default_rng(42)
n = 100000
X = rng.uniform(0, 100, (n, 20))
y = 50 + X[:, :5] @ [0.8, 1.2, -0.5, 0.3, 0.1] + rng.normal(0, 5, n)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
results = {}
for name, model in [
("RandomForest", RandomForestRegressor(n_estimators=100, random_state=42, n_jobs=-1)),
("XGBoost", xgb.XGBRegressor(n_estimators=300, max_depth=6, learning_rate=0.1, random_state=42)),
("LightGBM", lgb.LGBMRegressor(n_estimators=300, num_leaves=31, learning_rate=0.1, random_state=42, verbose=-1)),
]:
start = time.time()
model.fit(X_train, y_train)
train_time = time.time() - start
y_pred = model.predict(X_test)
r2 = r2_score(y_test, y_pred)
mae = mean_absolute_error(y_test, y_pred)
results[name] = {"time": train_time, "r2": r2, "mae": mae}
print(f"{name:15s}: R²={r2:.4f}, MAE={mae:.2f}, Time={train_time:.1f}s")
出力:
TEXT
📖 参照専用
# Runs successfully
| 比較軸 | RandomForest | XGBoost | LightGBM |
|---|---|---|---|
| 学習速度(10万サンプル) | 45秒 | 8秒 | 3秒 |
| 予測精度(R²) | 0.85 | 0.89 | 0.89 |
| メモリ使用量 | 大 | 中 | 小 |
| カテゴリカル特徴量 | エンコーディングが必要 | エンコーディングが必要 | ネイティブサポート |
| GPUサポート | なし | あり | あり |
| 適したデータサイズ | 50万件未満 | 任意 | 任意 |
📌 重要なポイント: LightGBMはXGBoostと同等の精度を維持しながら、学習速度で2〜3倍リードしています。ただし、小規模データセットではXGBoostの方が安定する場合があります。実際のプロジェクトでは、両方を試してデータに合った方を選ぶことをおすすめします。
❓ よくある質問
Q XGBoostとLightGBMのどちらを選ぶべきですか?
A 大規模データ(10万件以上)→ LightGBMの方が高速です。カテゴリカル特徴量 → LightGBMがネイティブにサポートします。小規模データ(1万件未満)→ XGBoostの方が安定しています。実務では両方を試し、交差検証で比較してください。
Q early_stoppingとは何ですか?
A 検証セットの指標を監視し、Nラウンド連続で改善がなければ学習を停止する仕組みです。過学習を防ぎつつ時間を節約できます。early_stopping_rounds=50の設定がおすすめです。
Q num_leavesとmax_depthの関係は?
A max_depth=dの場合、最大で2^d個の葉を持つことができます。num_leavesは葉の数を直接制御するため、max_depthより柔軟です。LightGBMではmax_depthの代わりにnum_leavesを使うことが推奨されています。典型的な値は31〜127です。
Q learning_rateとn_estimatorsはどのように連携しますか?
A learning_rateが小さい → より多くのn_estimatorsが必要 → より安定するが低速になります。推奨設定:lr=0.1 + n_est=500 + early_stopping、またはlr=0.01 + n_est=5000 + early_stoppingです。
Q GBDTに標準化は必要ですか?
A 不要です。GBDTは決定木ベースであり、特徴量のスケールの影響を受けません。ただし、LightGBMのカテゴリカル特徴量にはcategorical_featureパラメータの指定が必要です。
Q 過学習への対処法は?
A 正則化の強化(reg_alpha/reg_lambda)、max_depth/num_leavesの低減、min_child_samplesの増加、learning_rateの低下、subsample/colsample_bytreeのランダム性向上が有効です。
📖 まとめ
- ブースティングの核心:弱い学習者を逐次的に学習させ、各学習者が前の学習者の誤りを修正する
- XGBoostの革新:二次勾配最適化+L1/L2正則化+列サンプリング+近似分位点分割
- LightGBMの革新:GOSS(勾配サンプリング)+EFB(特徴量バンドリング)+ヒストグラム分割+カテゴリカル特徴量のネイティブサポート
- 主要ハイパーパラメータ:learning_rate + n_estimators(early_stopping併用)、max_depth/num_leaves、正則化パラメータ
- LightGBMは一般的にXGBoostより学習速度が2〜3倍速く、精度は同等
- どちらも産業グレードのGBDTフレームワークであり、実際のプロジェクトでは両方を試してデータに最適なものを選ぶ
📝 練習問題
- 基礎(難易度 ⭐):XGBRegressorを使ってCalifornia Housingの予測を行い、n_estimators=200、max_depth=5、learning_rate=0.1に設定してR²とMAEを出力してください。ヒント:第4章のサンプルを参照してください。
- 応用(難易度 ⭐⭐):同じデータセットでXGBoostとLightGBMを比較し、学習時間とR²を記録してください。early_stoppingを使って過学習を防いでください。ヒント:両方にeval_set + early_stoppingを設定してください。
- チャレンジ(難易度 ⭐⭐⭐):GridSearchCVまたはOptunaを使ってLightGBMのハイパーパラメータをチューニングし、num_leaves/learning_rate/reg_alpha/reg_lambdaの最適な組み合わせを探索してR²を少なくとも0.02改善してください。ヒント:5分割交差検証を使い、検索空間は第5章のパラメータ表を参照してください。