← 機械学習テキスト 一覧

🎓 レベル:発展 | 重要度:A(必須)

📎 前提:ブースティングとAdaBoost | 土台:勾配降下法(→ 最適化と学習理論 目次

要点(BLUF)

1. ブースティングの一般化:何が問題だったか

ブースティングとAdaBoost で見たように、ブースティングの基本思想は「弱学習器を順番に足し込み、前の誤りを次が補う」加法モデルです。最終予測は

FM(x)=m=1Mνmhm(x)F_M(x) = \sum_{m=1}^{M} \nu_m\, h_m(x)

という弱学習器 hmh_m の重み付き和でした。問題は 「各ステップで hmh_m と係数をどう決めるか」。AdaBoost はこれを 指数損失 L(y,F)=eyFL(y, F) = e^{-yF} に対する前向き段階的な最小化として解いていました(後述するように、これは数学的に確かめられた事実です)。

ですが指数損失は外れ値・ラベルノイズに弱いという欠点があります。外れた点の yF-yF が大きくなると損失が指数的に爆発し、その点を補正しようと過剰に引っ張られるからです。

ここで Friedman(2001)が出した答えが「損失を固定せず、微分可能でありさえすれば何でもいい形に書き換えてしまえ」というものです。これが勾配ブースティングです。

graph LR
    A["前向き段階的加法モデル<br/>(弱学習器を順に足す枠組み)"] --> B["指数損失で解く<br/>= AdaBoost"]
    A --> C["任意の微分可能な損失で解く<br/>= 勾配ブースティング"]
    C --> D["二乗誤差<br/>(回帰)"]
    C --> E["対数損失<br/>(分類)"]
    C --> F["Huber 損失<br/>(外れ値に頑健)"]

要するに:AdaBoost は「ブースティングの一例(指数損失版)」にすぎません。勾配ブースティングは損失の選択を自由にして、回帰・分類・頑健推定まで同じ仕組みで扱える「上位概念」です。

2. 前向き段階的加法モデル(FSAM)

まず土台となる 前向き段階的加法モデル を定式化します。加法モデル F(x)=mβmhm(x)F(x) = \sum_m \beta_m h_m(x) 全体を一気に最適化するのは難しいので、1ステップに1つの弱学習器だけを追加し、それを足したときの損失を最小化する、という貪欲な手続きを取ります。

ステップ mm では、すでに確定した Fm1F_{m-1} は固定し、新しく足す hh と係数 β\beta だけを動かして

(βm,hm)=argminβ,hi=1NL(yi,  Fm1(xi)+βh(xi))(\beta_m, h_m) = \arg\min_{\beta,\, h} \sum_{i=1}^{N} L\big(y_i,\; F_{m-1}(x_i) + \beta\, h(x_i)\big)

を解きます。そして Fm=Fm1+βmhmF_m = F_{m-1} + \beta_m h_m と更新します。

要するに:「すでに置いた弱学習器には触らず、次の1個だけを最適に足す」。一度足したものを後から調整しない(=前向き/forward)のがポイントです。決定木の貪欲な再帰分割(決定木)と同じ「1手ずつ最善」の精神です。

ここで LL に指数損失を入れて解くと、ちょうど AdaBoost の重み更新式が出てきます(4-03 で扱った内容)。問題は、一般の LL ではこの argmin\arg\min が解析的に解けないことです。そこを勾配で突破します。

3. 関数空間の勾配降下(核心)

ここが勾配ブースティングの心臓部です。「関数 FF そのものをパラメータとみなして、損失を勾配降下で減らす」と考えます。

なぜ「関数の勾配」なのか

普通の勾配降下は、パラメータ θ\theta に対して損失 J(θ)J(\theta)θθηθJ\theta \leftarrow \theta - \eta\, \nabla_\theta J と更新します(→ 最適化と学習理論 目次)。勾配ブースティングは更新する対象を「パラメータ」ではなく「予測関数 FF」にします。

訓練データ上での総損失を、各点での予測値 F(xi)F(x_i) の関数とみなします:

J(F)=i=1NL(yi,F(xi))J(F) = \sum_{i=1}^{N} L\big(y_i,\, F(x_i)\big)

各点の予測値 F(xi)F(x_i) をひとつの「座標」だと思うと、JJ を最も速く減らす方向(最急降下方向)は、各座標に関する偏微分にマイナスを付けたものです。これを点 ii ごとに計算したのが 擬似残差(pseudo-residual) です:

rim=[L(yi,F(xi))F(xi)]F=Fm1r_{im} = -\left[\frac{\partial L\big(y_i,\, F(x_i)\big)}{\partial F(x_i)}\right]_{F = F_{m-1}}

要するにrimr_{im} は「点 ii の予測値を、損失を減らすにはどっちへどれだけ動かせばいいか」を表す矢印です。NN 個の点それぞれに矢印が立っていて、これが関数空間での「負の勾配ベクトル」になります。

擬似残差を回帰木で近似する

ただし rimr_{im}訓練点の上でしか定義されていません。未知の xx に対しては値がない。そこで、この負の勾配ベクトル (r1m,,rNm)(r_{1m}, \dots, r_{Nm})一番近い回帰木 hmh_m を最小二乗でフィットします:

hm=argminhi=1N(rimh(xi))2h_m = \arg\min_{h} \sum_{i=1}^{N} \big(r_{im} - h(x_i)\big)^2

これが「擬似残差を回帰木で近似する」の正体です。回帰木が負の勾配方向を汎化可能な形に補間してくれるので、未知の点にも降下方向を与えられます。

flowchart TB
    Start["現在のモデル F_{m-1}"] --> Grad["各点で擬似残差を計算<br/>r_im = - dL/dF(負の勾配)"]
    Grad --> Fit["擬似残差を目的変数にして<br/>回帰木 h_m を最小二乗フィット"]
    Fit --> Leaf["各葉ごとに最適値を line search<br/>gamma_jm を決める"]
    Leaf --> Update["学習率を掛けて足し込む<br/>F_m = F_{m-1} + nu * h_m"]
    Update --> Check{"m が M に達した ?"}
    Check -->|"No"| Start
    Check -->|"Yes"| Done["最終モデル F_M を出力"]

要するに:勾配ブースティングの1反復は「負の勾配を求める → それを回帰木でなぞる → 少しだけ足す」。普通の勾配降下が「勾配を求めて少し動く」のと完全に対応していて、ただ動く対象が関数になっているだけです。

回帰木 hmh_m で**降下の「向き」**は決まりましたが、**どれだけ進むか(歩幅)**はまだです。Friedman の TreeBoost では、ここで木の構造を賢く使います。

回帰木は特徴空間を JJ 個の互いに重ならない葉領域 R1m,,RJmR_{1m}, \dots, R_{Jm} に分けます。そこで「木が選んだ分割(葉の領域)はそのまま使い、各葉に入れる定数値 γjm\gamma_{jm} だけを、実際の損失 LL が最小になるように選び直す」ことをします。葉 jj について

γjm=argminγxiRjmL(yi,  Fm1(xi)+γ)\gamma_{jm} = \arg\min_{\gamma} \sum_{x_i \in R_{jm}} L\big(y_i,\; F_{m-1}(x_i) + \gamma\big)

これは葉ごとに独立な1次元の最小化問題(=line search)です。最終的な更新は、各葉領域に最適値 γjm\gamma_{jm} を置いた木を足し込みます:

Fm(x)=Fm1(x)+νj=1Jγjm1[xRjm]F_m(x) = F_{m-1}(x) + \nu \sum_{j=1}^{J} \gamma_{jm}\, \mathbb{1}[x \in R_{jm}]

要するに:木の「形(どこで切るか)」は擬似残差への最小二乗で決め、木の「高さ(葉に入れる値)」は本物の損失 LL で決め直す、という二段構えです。向きを勾配で決め、歩幅を line search で決める—まさに勾配降下の作法です。

なぜ2段階に分けるのか:擬似残差は「損失の1次近似(線形化)」でしかないので、最小二乗フィットだけだと近似誤差が残ります。葉の値を本物の LL で取り直すことで、その葉に関する限り損失を厳密に最小化でき、1反復あたりの改善が大きくなります。

二乗損失の場合:擬似残差=普通の残差

二乗損失 L(y,F)=12(yF)2L(y, F) = \tfrac{1}{2}(y - F)^2 で擬似残差を計算してみます。FF で微分すると

LF=(yF)rim=LFFm1=yiFm1(xi)\frac{\partial L}{\partial F} = -(y - F) \quad\Longrightarrow\quad r_{im} = -\frac{\partial L}{\partial F}\Big|_{F_{m-1}} = y_i - F_{m-1}(x_i)

要するに:二乗損失では擬似残差がそのまま 普通の残差 yFy - F になります。だから「前の木が外した分(残差)を、次の木が学習して埋める」という直観的な説明が成り立つわけです。さらにこのとき葉の line search の答えも、その葉に落ちた残差の単純平均になります(回帰木の葉が平均を返すのと同じ)。一般の損失では擬似残差は「残差そのもの」ではなく「損失を減らす方向の値」になりますが、二乗損失という特別な場合に限って両者が一致する、と理解するのが正確です。

代表的な損失と擬似残差の対応:

損失 L(y,F)L(y,F)タスク擬似残差 rir_iコメント
二乗誤差 12(yF)2\tfrac{1}{2}(y-F)^2回帰yFy - F普通の残差そのもの
絶対誤差 yF\lvert y-F \rvert回帰sign(yF)\mathrm{sign}(y-F)符号だけ。外れ値に頑健
Huber 損失回帰中心は残差・外側は符号二乗と絶対の折衷
対数損失(二項逸脱度)分類ypy - ppp は予測確率)ロジスティック回帰と同じ形
指数損失 eyFe^{-yF}分類yeyFy\, e^{-y F}これを使うと AdaBoost

5. アルゴリズム全体

これまでの部品を1つの手続きにまとめます(Friedman のグラディエント・ツリーブースティング)。

flowchart TB
    Init["F_0 = argmin_c Σ L(y_i, c)<br/>定数で初期化"] --> Loop["m = 1 ... M を繰り返す"]
    Loop --> S1["(1)擬似残差<br/>r_im = - dL/dF を全点で計算"]
    S1 --> S2["(2)r_im に回帰木 h_m を最小二乗フィット<br/>葉領域 R_jm が決まる"]
    S2 --> S3["(3)各葉で line search<br/>gamma_jm = argmin_γ Σ L"]
    S3 --> S4["(4)更新<br/>F_m = F_{m-1} + nu * Σ gamma_jm * 1[x ∈ R_jm]"]
    S4 --> Loop
    Loop --> Out["最終モデル F_M"]

ステップを言葉で追うと:

  1. 初期化F0(x)=argminciL(yi,c)F_0(x) = \arg\min_c \sum_i L(y_i, c)。損失を最小にする定数から始めます(二乗損失なら yy の平均、対数損失なら対数オッズ)。
  2. mm について 擬似残差 rim=L/FFm1r_{im} = -\partial L / \partial F |_{F_{m-1}} を全訓練点で計算。
  3. 擬似残差を目的変数として 回帰木 hmh_m を最小二乗フィット(→ 葉領域 RjmR_{jm} が決まる)。
  4. 各葉で line search して最適値 γjm\gamma_{jm} を求める。
  5. 学習率 ν\nu を掛けて足し込むFm=Fm1+νjγjm1[xRjm]F_m = F_{m-1} + \nu \sum_j \gamma_{jm}\,\mathbb{1}[x \in R_{jm}]
  6. m=Mm=M まで繰り返し、FMF_M を最終モデルとする。

要するに:「定数から始めて、残差(負の勾配)を回帰木で順に削る」を MM 回。分類でも回帰でも使うのは回帰木である点に注意してください(分類でも擬似残差は連続値なので、フィットするのは常に回帰木です)。

6. 正則化:暴走させない3つのつまみ

勾配ブースティングは「訓練損失を順々に削る」仕組みなので、放っておくと訓練データに過剰適合します。これを抑える主要なつまみが3つあります。

(1) 学習率(shrinkage)ν\nu と木の本数 MM

更新を Fm=Fm1+νhmF_m = F_{m-1} + \nu\, h_m と、0<ν10 < \nu \le 1わざと縮めて足します。ν\nu を小さくすると1本あたりの寄与が小さくなり、過学習しにくくなりますが、同じ訓練損失に到達するのに必要な木の本数 MM は増えます。

xychart-beta
    title "学習率と必要な木の本数のトレードオフ(概念図)"
    x-axis "木の本数 M" [100, 500, 1000, 2000, 4000]
    y-axis "検証損失" 0 --> 10
    line "学習率 大(nu=0.3)" [6, 4, 4.5, 6, 8]
    line "学習率 小(nu=0.03)" [9, 5, 3, 2.5, 2.8]

要するにν\nuMM は逆向きの関係です。実務では「小さめの ν\nu(0.01〜0.1 程度)にして、MM を大きめに取り、early stopping で止める」のが定石です。経験的には、小さい学習率でゆっくり進むほど汎化が良くなる傾向があります。ν\nu は「各ステップで勾配方向にどれだけ進むか」の歩幅そのものなので、これを縮めるのは勾配降下の学習率を下げるのと同じ意味です。

(2) Early stopping(早期終了)

MM を固定せず、検証データの損失が改善しなくなった時点で打ち切るのが early stopping です。木を足すほど訓練損失は下がり続けますが、ある点を超えると検証損失は上昇に転じます(過学習の始まり)。その直前で止めます。

要するに:「最適な木の本数 MM」をハイパーパラメータとして探索する代わりに、検証損失をモニタしながら自動で決める方法です。ν\nu を小さく取って MM を大きめに用意し、early stopping に止めどころを任せる、という組み合わせがよく使われます。

(3) 確率的勾配ブースティング(subsample)と木の複雑さ

Friedman は、各反復で訓練データの一部(例:50%)を非復元抽出してサブサンプルし、その上で擬似残差のフィットと line search を行う改良を提案しました。これが 確率的勾配ブースティング(stochastic gradient boosting) です。バギング(バギングとランダムフォレスト)の「行サンプリング」をブースティングに持ち込んだものと言えます。

サブサンプリングには2つの効果があります:

加えて、木の深さ(葉の数 JJ も重要な正則化のつまみです。勾配ブースティングでは**浅い木(深さ1〜6程度、しばしば「切り株」に近いもの)**を使うのが基本です。

graph TB
    Reg["勾配ブースティングの正則化"]
    Reg --> R1["学習率 nu を小さく<br/>(+ 本数 M を増やす)"]
    Reg --> R2["early stopping<br/>(検証損失で打ち切り)"]
    Reg --> R3["subsample < 1.0<br/>(確率的勾配ブースティング)"]
    Reg --> R4["木の深さ・葉数を制限<br/>(弱学習器を浅く保つ)"]

要するに:勾配ブースティングは「弱い木をたくさん」が鉄則です。1本1本を弱く(浅く)保ち、学習率で寄与を縮め、サブサンプルでばらつきを入れ、early stopping で止める—これらを組み合わせて過学習を抑えます。

7. AdaBoost との統一的理解(必ず押さえる)

ここまで来ると、ブースティングとAdaBoost と勾配ブースティングの関係がはっきりします。

AdaBoost は「指数損失 L(y,F)=eyFL(y,F) = e^{-yF} を選んだときの勾配ブースティングの特殊例」です。

これは歴史的には後から発見された事実です。AdaBoost(1995)はもともと「誤分類した点の重みを増やす」という重み更新の手続きとして提案されましたが、後年「指数損失に対する前向き段階的加法モデル」として再解釈できることが示されました(Friedman, Hastie, Tibshirani 2000)。そして Friedman(2001)が「ならば損失を指数損失に限る必要はない」と一般化したのが勾配ブースティングです。

指数損失で擬似残差を計算すると

rim=eyiF(xi)F(xi)Fm1=yieyiFm1(xi)r_{im} = -\frac{\partial e^{-y_i F(x_i)}}{\partial F(x_i)}\Big|_{F_{m-1}} = y_i\, e^{-y_i F_{m-1}(x_i)}

となり、この eyiFm1(xi)e^{-y_i F_{m-1}(x_i)} がまさに AdaBoost の標本重み に対応します。誤って分類されている(yiFm1<0y_i F_{m-1} < 0 の)点ほど重みが大きくなり、次の弱学習器がそこを重点的に学習する—これが AdaBoost の「誤った点の重みを増やす」操作の正体だったわけです。

graph TB
    GB["勾配ブースティング<br/>(任意の微分可能な損失)"]
    GB -->|"指数損失 e^(-yF)"| Ada["AdaBoost"]
    GB -->|"対数損失"| LogB["ロジスティック・ブースティング<br/>(LogitBoost 系)"]
    GB -->|"二乗損失"| L2["L2 ブースティング<br/>(残差を順に学習)"]
    Ada -.->|"擬似残差 = 標本重み<br/>y * e^(-yF)"| Note["重み更新は<br/>勾配の特殊な姿"]

要するに:AdaBoost の「重み付け」と勾配ブースティングの「擬似残差」は別物ではなく同じものです。指数損失という特定の損失を選ぶと、勾配(擬似残差)がちょうど標本重みの形になる—それが AdaBoost だった、という統一的な見方ができます。この視点に立つと、損失を対数損失や二乗損失に差し替えるだけで分類・回帰・頑健推定へ自在に展開できることが腑に落ちます。

8. バギング(ランダムフォレスト)との対比

同じ「木のアンサンブル」でも、勾配ブースティングとランダムフォレスト(バギングとランダムフォレスト)は狙いが正反対です。

観点ランダムフォレスト(バギング)勾配ブースティング
木の作り方並列・独立に多数育てる逐次・前の誤りを次が補う
木の深さ深い木(低バイアス・高バリアンス)浅い木(高バイアス・低バリアンス)
主に下げるものバリアンス(平均化で安定化)バイアス(残差を順に削る)
過学習木を増やしても基本的に悪化しにくい木を増やしすぎると過学習する(要 early stopping)
並列化しやすい木は逐次依存(ただし1本内の分割探索は並列化可)

要するに:ランダムフォレストは「深い木を平均してバリアンスを潰す」、勾配ブースティングは「浅い木を積み上げてバイアスを潰す」。前者は木を増やしても安全ですが、後者は増やしすぎると過学習するので止めどころ(early stopping)が要ります。一般に、丁寧にチューニングした勾配ブースティングは表形式データでランダムフォレストを上回る精度を出すことが多い反面、ハイパーパラメータに敏感です。

⚠️ よくある誤解

対応するシミュレーション

simulations/gradient_boosting_residuals.py:勾配ブースティング(二乗誤差)を自前実装し、いまの予測の残差に小さな木を当てはめて少しずつ足す様子を可視化します。木1本ではざっくりした階段でも、本数を重ねると残差が小さくなり予測が真の関数へ近づくこと(訓練MSE 0.20→0.005)を示します。二乗誤差では残差が損失の負の勾配=関数空間の勾配降下です。バギング(バギングとランダムフォレスト)が並列に分散を下げるのと対照的に、ブースティングは逐次にバイアスを下げます。

残差を段階的に学習する勾配ブースティング

関連ノート