バギング (Bagging)

入門

2.5.4

バギング (Bagging)

最終更新 2020-03-11 読了時間 2 分
まとめ
  • バギングは、ブートストラップ標本ごとに同種の学習器を独立に学習し、予測を平均化・多数決する手法である。
  • 主に分散を下げる効果が強く、不安定な学習器(特に決定木)で性能改善が出やすい。
  • ベース学習器の複雑さとアンサンブル数の組み合わせが、精度と計算コストの実用的な最適点を決める。

直感 #

1つのモデルに全データを任せる代わりに、データを何度も引き直して複数モデルを作り、結果を合算するのがバギングです。個々のモデルが外れた予測をしても、集約すると外れが平均化されて安定します。

数学的定式化 #

分散低減の原理 #

$B$ 個の学習器がそれぞれ分散 $\sigma^2$ を持ち、学習器間の相関が $\rho$ のとき、バギング予測の分散は:

$$\text{Var}\!\left(\frac{1}{B}\sum_{b=1}^{B} h_b(x)\right) = \rho \sigma^2 + \frac{1 - \rho}{B}\sigma^2$$
  • 第1項 $\rho\sigma^2$: 学習器間の相関由来。$B$ を増やしても消えない
  • 第2項: $B \to \infty$ で消える。これがバギングの分散低減効果

ランダムフォレストは特徴量サブサンプリングで $\rho$ を下げ、第1項も縮小させます。

ブートストラップサンプリング #

$N$ 個のデータから重複ありで $N$ 個を抽出する操作です。特定のサンプルが1回も選ばれない確率は:

$$P(\text{非選出}) = \left(1 - \frac{1}{N}\right)^N \xrightarrow{N \to \infty} \frac{1}{e} \approx 0.368$$

つまり約 63.2% のデータが各ブートストラップ標本に含まれ、残り約 36.8% が Out-of-Bag (OOB) データとなります。

OOB 誤差推定 #

OOB データは各木が学習時に使わなかったサンプルなので、追加の検証セットなしで汎化性能を推定できます:

$$\hat{E}_{\text{OOB}} = \frac{1}{N}\sum_{i=1}^{N} L\!\left(y_i,\; \frac{1}{|S_i|}\sum_{b \in S_i} h_b(x_i)\right)$$

ここで $S_i = {b : x_i \notin D_b}$ は、サンプル $i$ を学習に使わなかった木の集合です。

バギングの予測 #

  • 回帰: $\hat{f}(x) = \frac{1}{B}\sum_{b=1}^{B} h_b(x)$
  • 分類: $\hat{y}(x) = \text{mode}{h_1(x), \dots, h_B(x)}$(多数決)

詳細な解説 #

バギングは Bootstrap Aggregating の略で、データをブートストラップサンプリングして複数の弱学習器を学習し、その平均や多数決を取るアンサンブル手法です。決定木と組み合わせるとランダムフォレストに発展します。


1. 手順 #

  1. 学習データから重複ありサンプリング(ブートストラップ)で複数セットを生成
  2. 各セットで同じモデルを学習
  3. 予測は平均(回帰)または多数決(分類)

バギングは主にモデルの分散を減らし、安定性を高めます。


2. Python 実装 #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import BaggingRegressor

X, y = fetch_california_housing(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)

base = DecisionTreeRegressor(max_depth=None, random_state=0)
bagging = BaggingRegressor(
    estimator=base,
    n_estimators=100,
    max_samples=0.8,
    max_features=0.8,
    bootstrap=True,
    random_state=0,
)
bagging.fit(X_train, y_train)

pred = bagging.predict(X_test)
print("RMSE:", mean_squared_error(y_test, pred, squared=False))
print("OOB スコア:", bagging.oob_score_)

3. ハイパーパラメータ #

  • n_estimators: 学習器の数。多いほど安定するが計算コスト増
  • max_samples, max_features: 1 個の学習器に渡すデータ/特徴量の割合
  • bootstrap: True でサンプルを重複抽出、bootstrap_features で特徴量も重複抽出
  • oob_score: アンサンブルに含まれなかったサンプル(Out-of-Bag)で汎化性能を推定できる

4. 長所と短所 #

長所短所
実装が簡単で並列化しやすい大量の学習器を保持する必要がある
分散を大きく削減できるバイアスは減らせないので弱学習器自体がある程度強くないと効果薄
OOB 推定で追加の検証セット不要解釈性は単体モデルより低い

5. まとめ #

  • バギングは「データを何度も引き直して平均を取る」シンプルなアイデアで高い安定性を得る
  • 決定木 + バギング = ランダムフォレストという位置付けを把握しておくと理解が深まる
  • サンプル数や特徴量が多い場合でも、並列化すれば実用的な速度で動作します

推定器数とバギング #

推定器数を増やすとバギングの予測がどう安定するか確認できます。