勾配ブースティングとランダムフォレストの違いを徹底比較

テーブル形式のデータで「とにかく精度の出る予測モデルが欲しい」と思ったとき、多くの人がまず試すのがランダムフォレストと勾配ブースティング(GBDT)です。どちらも決定木をたくさん束ねたアンサンブル学習で、見た目はよく似ています。ところが両者の設計思想は真逆で、ハイパーパラメータの意味も、過学習の仕方も、得意なデータも違います。この違いを知らないまま使うと、「木を増やしたら急に精度が落ちた」「学習率って何を変えているの?」といった場面で立ち往生してしまいます。

この記事を読むと、次のような場面で迷わなくなります。

  • Kaggleなどのコンペや業務の精度勝負 — まずランダムフォレストでベースラインを作り、GBDTで上積みを狙う定石の理由がわかる
  • ノイズや外れ値の多い実データの予測 — どちらのモデルがロバストか、なぜそうなるかを根拠を持って選べる

本記事の内容

  • バギング(並列に分散を減らす)とブースティング(逐次に偏りを減らす)という真逆の思想
  • バイアス・分散分解との対応を、模式図と実測で可視化
  • アルゴリズムの動きを段階的に追う(RFのブートストラップ+特徴量サブサンプル、GBDTの残差への逐次フィット)
  • ハイパーパラメータの意味の違いと過学習耐性・学習速度・並列性・ノイズ耐性の比較実験
  • 使い分けの指針と選択フローチャート

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

一言でいうと何が違うのか

ランダムフォレストとGBDTは、どちらも「弱い予測器である決定木を大量に集めて強い予測器を作る」アンサンブルです。違いは木の集め方にあります。

イメージとしては、こう考えるとわかりやすいです。ランダムフォレストは、独立した大勢の専門家に同じ問題を別々に解いてもらい、その答えを多数決(平均)で集約します。一人ひとりの専門家は気まぐれ(高分散)ですが、たくさん集めて平均すれば、その気まぐれは打ち消し合って安定します。これがバギングの発想です。

一方GBDTは、一人の弟子が問題を解き、その間違い(残差)だけを次の弟子が引き継いで直し、さらにその残った間違いを次の弟子が直す、というリレーです。一人ひとりは浅く単純な木(高バイアス)ですが、前の人の誤りを順に埋めていくことで、全体として複雑な関数を近似できます。これがブースティングの発想です。

下の図は、この「並列で平均するRF」と「逐次で誤りを直すGBDT」の流れを並べたものです。

ランダムフォレストの並列バギングと勾配ブースティングの逐次ブースティングを比較した概念図

左のランダムフォレストは、訓練データから復元抽出した標本ごとに深い木を独立に育て、最後に平均します。木どうしは互いに参照しないので並列に学習できます。右のGBDTは、浅い木を1本ずつ足し、各段で残差を計算してそれを次の木が追うため、必ず逐次に学習します。この一点が、これから見ていくあらゆる違いの根っこになっています。

では、なぜ「平均する」と分散が減り、「誤りを直す」と偏りが減るのでしょうか。これを理解するには、汎化誤差をバイアスと分散に分解する見方が役立ちます。

バイアス・分散分解という共通の物差し

予測モデルの誤差は、大きくバイアス分散に分けて考えられます。バイアスは「モデルが単純すぎて真の関数を表現しきれない」ことによる系統的なズレ、分散は「訓練データがたまたま違っていたら予測がどれだけブレるか」というデータ依存のばらつきです。

ある入力 $x$ に対する真の値を $f(x)$、ノイズ分散を $\sigma^2$、訓練データ $D$ で学習したモデルの予測を $\hat{f}_D(x)$ とすると、期待二乗誤差は次のように分解できます。

$$ \mathbb{E}_D\!\left[(y – \hat{f}_D(x))^2\right] = \underbrace{\left(f(x) – \mathbb{E}_D[\hat{f}_D(x)]\right)^2}_{\text{バイアス}^2} + \underbrace{\mathbb{E}_D\!\left[(\hat{f}_D(x) – \mathbb{E}_D[\hat{f}_D(x)])^2\right]}_{\text{分散}} + \underbrace{\sigma^2}_{\text{除けないノイズ}} $$

この式の意味を押さえておきましょう。第1項のバイアスは「平均的な予測 $\mathbb{E}_D[\hat{f}_D(x)]$ が真の値からどれだけ離れているか」、第2項の分散は「個々の予測が平均的な予測からどれだけ散らばっているか」です。第3項のノイズはどんなモデルでも消せません。良いモデルとは、このバイアスと分散の和を小さくするモデルです。

ここで一つ大事な視点を補っておきます。バイアスと分散は、しばしばトレードオフの関係にあります。モデルを複雑にする(木を深くする)とバイアスは下がりますが分散は上がり、逆にモデルを単純にする(木を浅くする)とバイアスは上がりますが分散は下がります。単独の決定木では、この綱引きのちょうど良い点を「木の深さ」ひとつで探すしかありません。ところがアンサンブルの巧妙さは、この綱引きを片側だけ動かせる点にあります。RFは「深い木(低バイアス・高分散)を出発点にして、平均で分散だけを削る」、GBDTは「浅い木(高バイアス・低分散)を出発点にして、逐次の足し算でバイアスだけを削る」。どちらも、もう一方の項をあまり悪化させずに目的の項を下げられるからこそ、単独の木より強くなれるのです。

ここで重要なのは、バギングとブースティングがこの分解の別々の項を攻めている点です。下の図の左は、モデルの複雑さ(木の深さ)に対してバイアスと分散がどう動くかの模式図、右は1次元回帰で実際に30回学習を繰り返して測ったバイアス$^2$と分散です。

バイアス分散トレードオフの模式図と4つのモデルで実測したバイアスと分散の棒グラフ

右の実測棒グラフを見ると、設計思想の違いがはっきり出ています。深い木1本は分散が大きく(0.055)、これを30本平均したランダムフォレストは分散がほぼ半減します(0.029)。バイアスはほとんど変わりません。つまりRFは分散を削る装置です。一方、浅い木1本はバイアス$^2$が突出して大きく(0.048)、これを100本逐次に足したGBDTではバイアス$^2$が一気に小さくなります(0.001)。つまりGBDTはバイアスを削る装置です。同じ「木のアンサンブル」でも、削っている誤差の種類が違うわけです。

この「分散を削るRF」「バイアスを削るGBDT」という対比を、次はアルゴリズムの動きそのもので確かめていきましょう。

ランダムフォレストの動き:ブートストラップと特徴量サブサンプル

ランダムフォレストが分散を削れる仕組みは、2段階のランダム化にあります。

  1. ブートストラップ標本 — 訓練データ $n$ 件から復元抽出で $n$ 件を選び直し、木ごとに少しずつ違うデータで学習する。これで木どうしの相関が下がる。
  2. 特徴量サブサンプル — 各分割の候補となる特徴量を、全 $d$ 個のうちランダムに $m$ 個(回帰なら $m \approx d/3$ が目安)に絞る。これで木どうしがさらに違う形になる。

なぜ「木どうしを違う形にする」ことが効くのでしょうか。$M$ 本の木の予測 $\hat{f}_1, \dots, \hat{f}_M$ がそれぞれ分散 $v$ を持ち、ペアの相関が $\rho$ だとすると、平均 $\bar{f} = \frac{1}{M}\sum_m \hat{f}_m$ の分散は次のようになります。

$$ \mathrm{Var}(\bar{f}) = \rho\, v + \frac{1 – \rho}{M}\, v $$

この式が語ることは強烈です。$M$ を無限に増やしても第2項しか消えず、分散は $\rho v$ までしか下がりません。だからこそ、木どうしの相関 $\rho$ を下げる工夫(ブートストラップと特徴量サブサンプル)が本質的なのです。逆に言えば、木を増やしても分散は下限に収束するだけで増えはしない — これがRFの過学習耐性の正体です。

実際の動きを1次元回帰で見てみましょう。

ブートストラップ標本ごとの深い木はギザギザで高分散だが、平均する木を増やすほど滑らかになる様子

左は、3つの異なるブートストラップ標本で育てた深い木です。どれも訓練点に過剰に反応してギザギザで、単体では真の関数(破線)から大きく外れます(高分散)。右は、この種の木を1本・10本・100本と平均したものです。本数を増やすほど予測が滑らかになり、真の関数に近づきます。個々が暴れていても、平均すれば暴れが打ち消し合う — バギングの効き目がそのまま見えています。

ブートストラップにはもう一つ嬉しい副産物があります。復元抽出では、各木の学習に使われなかったサンプルが平均して約 $1/e \approx 37\%$ 残ります(これをOut-Of-Bag、OOBサンプルと呼びます)。あるサンプルについて、それを使わなかった木だけで予測を集計すれば、検証データを別に取り分けなくても汎化性能を見積もれます。これがOOB誤差で、RFが「交差検証なしでも自分の性能をある程度自己診断できる」便利さの源です。GBDTは全データを毎回使う逐次学習なので、この仕組みは自然には持たず、過学習の監視には別途の検証セットが要ります。

平均で分散を削るRFの動きがわかりました。では、誤りを逐次に直すGBDTはどう動くのでしょうか。今度はまったく違う絵になります。

勾配ブースティングの動き:残差への逐次フィット

GBDTは、初期予測 $F_0(x)$(二乗誤差なら全体の平均値)から始め、各段で現在の予測の残差を新しい木にフィットして足し込みます。二乗誤差 $L = \frac{1}{2}(y – F)^2$ の場合、その負の勾配 $-\partial L / \partial F = y – F$ はちょうど残差そのものです。だからGBDTは「残差を予測する木を順に足す」アルゴリズムになります。

$$ F_m(x) = F_{m-1}(x) + \eta\, h_m(x), \qquad h_m \approx \underbrace{y – F_{m-1}(x)}_{\text{残差}} $$

ここで $\eta$ は学習率(learning rate)で、1本の木の寄与をどれだけ控えめにするかを決めます。$\eta$ を小さくすると一歩が慎重になり、その分多くの木が必要になりますが、行き過ぎを防げます。

「勾配ブースティング」という名前は、この残差が損失関数の勾配と結びつくことに由来します。一般の微分可能な損失 $L(y, F)$ に対し、各段で現在の予測 $F_{m-1}$ における負の勾配 $-\partial L/\partial F$ を計算し、それを新しい木にフィットして足し込みます。つまりGBDTは、関数の空間で勾配降下法を行っていると見なせます。二乗誤差 $L=\frac{1}{2}(y-F)^2$ の場合は負の勾配がちょうど残差 $y-F$ になるので「残差を追う」という直感的な形になりますが、ロジスティック損失(分類)や絶対誤差・Huber損失(外れ値に強い回帰)でも同じ枠組みがそのまま使えます。損失を差し替えるだけで分類にも回帰にも対応できる柔軟さが、GBDT系が幅広い問題で使われる理由のひとつです。

この逐次の様子を、段階を追って可視化したのが次の図です。上段が予測、下段がその時点での残差です。

勾配ブースティングが初期値から木を1本5本100本と足すごとに予測が真の関数に近づき残差が小さくなる様子

初期値(左端)では予測は平均値の水平線で、残差は真の関数の形そのままに大きく波打っています(RMSE=0.709)。木を1本足すと、その残差の大きな山を浅い木がざっくり埋め、残差が小さくなります(0.571)。5本で残差はかなり平らになり(0.325)、100本では予測が真の関数にほぼ重なって残差がほとんど0付近に集まります(0.194)。前の段の「埋め残し」を次の木が狙い撃ちする — これがブースティングがバイアスを削る仕組みです。

RFとGBDTの動きがわかったところで、両者を最も混乱させやすいハイパーパラメータ、とくに「木の本数」の意味の違いを整理しましょう。

ハイパーパラメータ比較:同じ名前でも意味が違う

RFとGBDTには n_estimators(木の本数)や木の深さといった同名のパラメータがありますが、その役割は対照的です。主なものを表にまとめます。

パラメータ ランダムフォレスト 勾配ブースティング(GBDT)
n_estimators(木の本数 $M$) 多いほど分散が下限に収束。増やしても基本的に過学習しない 多いほど残差を追い続ける。増やしすぎると過学習する(早期終了で制御)
木の深さ(max_depth 深い木でよい(低バイアス・高分散を平均で抑える)。目安は深め〜無制限 浅い木でよい(弱学習器)。目安は3〜6程度
learning_rate($\eta$) なし(平均なので各木は等しく $1/M$) あり。小さいほど慎重・要本数増。0.01〜0.1が定番
特徴量サブサンプル 分散低減の主役(回帰 $d/3$、分類 $\sqrt{d}$ が目安) 補助的(行・列のサブサンプルで正則化に寄与)
並列性 木が独立なので完全並列 木が逐次依存なので木の中の計算しか並列化できない

とくに混乱しやすいのが n_estimators です。「木を増やせば良くなる」という直感はRFには当てはまりますが、GBDTには当てはまりません。これを実験で確かめましょう。Friedman#1という8次元の回帰問題(後ろ3次元は無関係な特徴量)で、木の本数に対するテストRMSEを描きます。

木の本数に対するテストRMSEの学習曲線でRFは収束しGBDTは最小点の後に悪化する様子

この学習曲線から、n_estimators の意味の違いがはっきり読み取れます。ランダムフォレスト(青)は本数を増やすとRMSEが下がって400本で2.35に収束し、その後悪化しません。一方GBDT(lr=0.3, 赤)は14本付近で最小(RMSE=2.33)を取った後、じわじわ悪化していきます。これがGBDTの過学習です。さらにGBDT(lr=0.1, 橙)は赤より遅れて最小点に達し、より低いRMSE(2.21)まで到達できています。

なぜこうなるのかは、これまでの議論からそのまま説明できます。RFにとって木を1本足すことは「平均する対象を1つ増やす」ことであり、$\mathrm{Var}(\bar{f}) = \rho v + (1-\rho)v/M$ の第2項を少し減らすだけです。減らしすぎても下限 $\rho v$ に収束するだけで、悪化する方向には決して動きません。だからRFの n_estimators は「計算資源が許す範囲で多めに」が基本で、過学習の心配はほぼ要りません。一方GBDTにとって木を1本足すことは「残差をさらに追う」ことであり、追いすぎればノイズまで覚え込みます。最小点を過ぎてからの本数は、バイアスをわずかに減らす代わりに分散を増やすので、テスト誤差が反転して上がっていくのです。RFの木は足しても安全、GBDTの木は足しすぎると毒 — この非対称性が、両者のチューニングの作法をまるごと分けています。

ここから「学習率を下げ、その分木を増やす」のが効くことが見えます。学習率を変えた比較も見ておきましょう。

学習率を1.0から0.03まで変えたGBDTの学習曲線。学習率が低いほど良い点に到達する

学習率が大きい($\eta=1.0$)と数本で底を打ってすぐ過学習に転じます。学習率を下げるほど、ゆっくりですがより低いRMSEまで到達できます。「小さな学習率 × 多くの木 × 早期終了」がGBDTのチューニングの王道なのは、このトレードオフが理由です。RFにはこの調整軸そのものが存在しません。

パラメータの意味の違いを押さえたところで、実データで悩みがちな「ノイズ・外れ値への強さ」と「決定境界の質感」を見ておきましょう。

ノイズ耐性と決定境界の比較

GBDTは残差を追い続けるため、外れ値に引っ張られやすい弱点があります。二乗誤差を使う標準的なGBDTでは、外れ値の残差が大きいので、それを埋めようと後続の木が過剰に反応します。RFは平均でならすので、外れ値の影響が薄まります。実際に外れ値を3点混入させて比べてみましょう。

外れ値を含むデータでランダムフォレストは滑らかなままだがGBDTは外れ値に引っ張られる比較

左のランダムフォレストは外れ値の近くでも予測がほぼ真の関数に沿っています。右のGBDTは外れ値(×印)の方向に予測が跳ね、その周辺で真の関数から外れます。ノイズや外れ値が多いデータでは、無調整でもRFが安定しやすいことがわかります(GBDTでも huber 損失や subsample で緩和できますが、デフォルトの二乗誤差では弱点が出ます)。

分類問題での決定境界の質感も比べておきます。2つの三日月形データに対する予測スコアを見てみましょう。

ランダムフォレストとGBDTの決定境界。RFはなめらかな投票平均でGBDTはシャープな加法モデル

RF(左)は200本の投票率なので境界がなめらかにぼけ、スコアが連続的に変化します。GBDT(右)は加法モデルなので境界がよりシャープで、自信を持って0/1に振り切る領域が広いのが特徴です。どちらが良いというより、RFは確率的な滑らかさ、GBDTは決め打ちのシャープさという個性の違いとして捉えると、後処理(しきい値設定や確率校正)の方針が立てやすくなります。

ここまで図で違いを見てきました。両者の「集め方」の違いが、実はとても短いコードの差にすぎないことを、最後に手を動かして確かめておきましょう。

コードで見る「集め方」の違い

RFとGBDTの本質的な違いは、決定木を束ねるループの中だけにあります。ここでは、回帰木を作る関数 build_tree と予測する関数 predict_tree が用意されているとして(どちらも二乗誤差を最小化する標準的な実装です)、その上にRFとGBDTを薄く載せてみます。

まずランダムフォレストです。やることは「ブートストラップ標本で深い木を独立に育て、最後に平均する」だけです。

import numpy as np

class RandomForest:
    """バギング:ブートストラップ標本ごとに深い木を独立に育て、平均する"""
    def __init__(self, n_trees=100, max_depth=12, seed=0):
        self.n_trees, self.max_depth, self.seed = n_trees, max_depth, seed

    def fit(self, X, y):
        rng = np.random.default_rng(self.seed)
        n = len(y)
        self.trees = []
        for _ in range(self.n_trees):
            idx = rng.integers(0, n, n)          # ブートストラップ標本(復元抽出)
            self.trees.append(build_tree(X[idx], y[idx], self.max_depth))
        return self

    def predict(self, X):
        # 全ての木の予測を「平均」する(=分散を減らす)
        return np.mean([predict_tree(t, X) for t in self.trees], axis=0)

ポイントは fit の中で木どうしが一切互いを参照していないことです。各木は別々のブートストラップ標本だけを見て育つので、原理的に並列化できます。予測は単純な平均で、これが分散を削る操作そのものです。

次に勾配ブースティングです。こちらは「初期値(平均)から始め、毎回の残差に浅い木をフィットして、学習率を掛けて足す」というループです。

class GBDT:
    """ブースティング:残差に浅い木を逐次フィットして足し込む"""
    def __init__(self, n_trees=100, lr=0.1, max_depth=3):
        self.n_trees, self.lr, self.max_depth = n_trees, lr, max_depth

    def fit(self, X, y):
        self.f0 = float(y.mean())                # 初期予測は全体の平均
        F = np.full(len(y), self.f0)
        self.trees = []
        for _ in range(self.n_trees):
            r = y - F                            # 残差(二乗誤差の負の勾配)
            tr = build_tree(X, r, self.max_depth)  # 残差に浅い木をフィット
            F += self.lr * predict_tree(tr, X)   # 学習率を掛けて足し込む
            self.trees.append(tr)
        return self

    def predict(self, X):
        F = np.full(len(X), self.f0)
        for tr in self.trees:
            F += self.lr * predict_tree(tr, X)   # 全ての木の予測を「足し算」する
        return F

2つのクラスを見比べると、違いが鮮明です。RFの fit では木が独立(X[idx] だけを見る)で predict平均、GBDTの fit では各木が直前までの予測の残差 y - F を見て(逐次依存)predict足し算です。「平均で分散を削る」か「足し算でバイアスを削る」か、という設計思想の違いが、この十数行にそのまま表れています。同じ build_tree を使っていても、束ね方ひとつでまったく性質の異なるモデルになる — これがアンサンブルの面白さです。

実務でこれらをスクラッチ実装することは稀で、scikit-learnの RandomForestRegressor / GradientBoostingRegressor、あるいは高速なXGBoost・LightGBMを使います。とはいえ、束ねるループの中身を一度自分で書いておくと、ハイパーパラメータの意味(なぜRFは木を増やしても過学習しにくく、GBDTは学習率が要るのか)が腑に落ちます。

ここまでの違いを踏まえて、最後に実務での使い分けを整理しましょう。

使い分けの指針

ここまでの比較をまとめると、選び方の指針が見えてきます。

  • とりあえず動かしたい・調整時間がない → ランダムフォレスト。ほぼ無調整で安定し、並列で速く、過学習しにくい。ベースライン作りや特徴量の重要度確認に最適です。
  • ノイズ・外れ値が多い → ランダムフォレスト。平均でならすのでロバストです。
  • 精度を限界まで詰めたい → GBDT系。とくにXGBoostLightGBMは、ヒストグラム分割・正則化・欠損値の自動処理・GPU対応などで高速かつ高精度です。テーブルデータのコンペで上位を占めるのはほぼGBDT系です。
  • 大規模データで速度も精度も欲しい → LightGBM。葉単位の成長(leaf-wise)とヒストグラム化で大規模データに強いです。

少しだけ補足しておくと、近年のGBDT系ライブラリは、ここで述べた素朴なGBDTの弱点をかなり克服しています。XGBoostは目的関数に正則化項(葉の数・葉の値の大きさへのペナルティ)を加え、二次のテイラー展開を使って各分割の利得を厳密に評価します。LightGBMは連続値をヒストグラムに離散化して分割探索を高速化し、葉単位で成長させることで深い相互作用を効率よく捉えます。どちらも行・列のサブサンプリングや早期終了を備え、外れ値に対してもHuber損失などで緩和できます。つまり「GBDTは過学習しやすく外れ値に弱い」という弱点は、適切なライブラリと設定でかなり抑えられる、というのが実情です。

実務の定石は単純です。まずRFで基準値を作り、次にGBDTでそれを超えられるか試す。GBDTでは必ず検証セットを用意して早期終了で過学習を監視します。下のフローチャートにまとめました。

ランダムフォレストとGBDTの使い分けフローチャート

このフローの背骨は「RFで基準値、GBDTで上積み」という一本道です。RFが過学習しにくいので外れ値の確認やベースラインに使い、精度勝負の局面でGBDT系(XGBoost/LightGBM)に切り替えて、検証セットと早期終了で慎重にチューニングする — この流れを覚えておけば、テーブルデータの予測でまず迷いません。

まとめ

本記事では、ランダムフォレストと勾配ブースティングの違いを徹底比較しました。

  • 両者とも決定木のアンサンブルだが、RFはバギング(並列に分散を減らす)GBDTはブースティング(逐次にバイアスを減らす)という真逆の思想に立つ
  • バイアス・分散分解で見ると、RFは分散を、GBDTはバイアスを削る装置であることを実測で確認した
  • n_estimators の意味が逆 — RFは増やしても過学習しにくく、GBDTは増やしすぎると過学習する
  • RFは無調整で安定・ロバスト・並列、GBDTは学習率と早期終了で高精度を狙う
  • 実務の定石は「RFで基準値 → GBDTで上積み」、精度勝負ではXGBoost/LightGBM

決定木アンサンブルの全体像をさらに俯瞰したい場合や、GBDTの数理に踏み込みたい場合は、以下の記事も参考にしてください。

次のステップとして、以下の記事も参考にしてください。