アンサンブル学習:決定木からランダムフォレスト・勾配ブースティングまで

決定木の基礎からバギング(ランダムフォレスト)、ブースティング(GBDT・XGBoost・LightGBM)まで、理論とscikit-learn実装を解説します。

はじめに

アンサンブル学習は、複数の弱学習器を組み合わせることで、単体モデルより高い汎化性能を実現する手法です。Kaggle等のコンペティションでもトップソリューションの多くがアンサンブル手法を採用しています。

本記事では、基本構成要素である決定木から、バギング(ランダムフォレスト)、**ブースティング(GBDT)**までを体系的に解説します。

決定木

CART アルゴリズム

分類木ではジニ不純度を最小化する分割を再帰的に探索します。

\[\text{Gini}(t) = 1 - \sum_{k=1}^{K} p_k^2 \tag{1}\]

ここで \(p_k\) はノード \(t\) におけるクラス \(k\) の割合です。

回帰木では二乗誤差を使います。

\[\text{MSE}(t) = \frac{1}{|t|} \sum_{i \in t} (y_i - \bar{y}_t)^2 \tag{2}\]

決定木の長所と短所

長所短所
解釈しやすい過学習しやすい
前処理が不要決定境界が軸に平行
非線形に対応分散が大きい(不安定)

バギング

原理

**Bootstrap Aggregating(バギング)**は、ブートストラップサンプルで複数の決定木を独立に学習し、予測を平均化(回帰)または多数決(分類)します。

\[\hat{f}_{\text{bag}}(\mathbf{x}) = \frac{1}{B} \sum_{b=1}^{B} \hat{f}_b(\mathbf{x}) \tag{3}\]

分散を削減する効果があります。

\[\text{Var}\left(\frac{1}{B}\sum_b f_b\right) = \frac{\sigma^2}{B} \quad (\text{独立な場合}) \tag{4}\]

ランダムフォレスト

バギングに加えて、各分割で特徴量のランダムサブセット(\(m \approx \sqrt{p}\))のみを候補にします。これにより木の間の相関を下げ、さらに分散を削減します。

ブースティング

勾配ブースティング(GBDT)

弱学習器を逐次的に追加し、前の学習器の残差(負の勾配)をフィッティングします。

ステップ \(m\) での更新:

\[F_m(\mathbf{x}) = F_{m-1}(\mathbf{x}) + \eta \cdot h_m(\mathbf{x}) \tag{5}\]

ここで \(h_m\) は擬似残差 \(r_{im} = -\frac{\partial L(y_i, F_{m-1}(\mathbf{x}_i))}{\partial F_{m-1}(\mathbf{x}_i)}\) にフィットした決定木、\(\eta\) は学習率です。

バギング vs ブースティング

特徴バギングブースティング
学習方式並列(独立)逐次(依存)
主な効果分散の削減バイアスの削減
過学習耐性高い低い(要チューニング)
代表手法ランダムフォレストGBDT, XGBoost, LightGBM

Python実装

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import (RandomForestClassifier,
                               GradientBoostingClassifier)
from sklearn.metrics import accuracy_score

# --- データ生成 ---
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)

# --- モデル比較 ---
models = {
    'Decision Tree': DecisionTreeClassifier(max_depth=5, random_state=42),
    'Random Forest': RandomForestClassifier(n_estimators=100, max_depth=5,
                                             random_state=42),
    'Gradient Boosting': GradientBoostingClassifier(n_estimators=100,
                                                     max_depth=3,
                                                     learning_rate=0.1,
                                                     random_state=42),
}

results = {}
for name, model in models.items():
    cv_scores = cross_val_score(model, X_train, y_train, cv=5,
                                 scoring='accuracy')
    model.fit(X_train, y_train)
    test_acc = accuracy_score(y_test, model.predict(X_test))
    results[name] = {'cv_mean': cv_scores.mean(), 'cv_std': cv_scores.std(),
                     'test_acc': test_acc}
    print(f"{name}: CV={cv_scores.mean():.3f}±{cv_scores.std():.3f}, "
          f"Test={test_acc:.3f}")

# --- 特徴量重要度(ランダムフォレスト) ---
rf = models['Random Forest']
importances = rf.feature_importances_
indices = np.argsort(importances)[::-1][:10]

plt.figure(figsize=(10, 5))
plt.bar(range(10), importances[indices])
plt.xticks(range(10), [f'Feature {i}' for i in indices], rotation=45)
plt.ylabel('Importance')
plt.title('Random Forest Feature Importance (Top 10)')
plt.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.show()

主要ハイパーパラメータ

ランダムフォレスト

パラメータ説明目安
n_estimators木の数100-500(多いほど安定)
max_depth木の深さ5-20
max_features分割候補の特徴量数\(\sqrt{p}\)(分類)、\(p/3\)(回帰)

勾配ブースティング

パラメータ説明目安
n_estimatorsブースティングの反復数100-1000
learning_rate学習率 \(\eta\)0.01-0.1
max_depth各木の深さ3-8(浅め)

学習率と反復数にはトレードオフがあり、小さな学習率 + 多くの反復が一般的です。

関連記事

参考文献

  • Breiman, L. (2001). “Random Forests”. Machine Learning, 45(1), 5-32.
  • Friedman, J. H. (2001). “Greedy Function Approximation: A Gradient Boosting Machine”. Annals of Statistics, 29(5), 1189-1232.
  • scikit-learn Ensemble Methods