大数跨境
0
0

通透!随机森林 vs GBDT !!

通透!随机森林 vs GBDT !! 机器学习和人工智能AI
2025-11-29
2

哈喽,大家好~

今儿咱们来聊聊随机森林 vs GBDT,包括其中的原理、推导、偏差-方差角度、优化方式、计算复杂度、正则化策略等等~

首先,随机森林是并行的Bagging思想

训练若干棵深树,基于自助采样和特征随机子空间来去相关化,最后平均/投票聚合。主要做方差下降,相对偏差较大但鲁棒、稳定。

GBDT是串行的Boosting思想

逐步学习若干棵浅树(弱学习器),每一棵都拟合上一步的残差/负梯度,最后加和得到强学习器。主要做偏差下降(bias reduction),若控制不好容易过拟合。

目标函数与优化方面。

随机森林不显式最小化全局损失,而是在每棵树内部用启发式分裂准则(如Gini/熵/平方误差)局部最优;总模型是简单平均。

GBDT显式在函数空间做梯度下降(或近似Newton),将任意可导损失(平方误差、逻辑损失、指数损失等)作为目标进行迭代优化。

模型结构与正则化方面。

随机森林:大量深树 + 强随机性,天然抗过拟合;调参简单,默认配置常常表现稳健。

GBDT:大量浅树 + 学习率+ 子采样+ 结构约束;调参更敏感但上限更高。

并行化与规模。

随机森林天生并行(树与树之间独立);适合快速训练与大规模特征下的基线模型。

传统GBDT是顺序依赖(树与树之间串行),但单棵树训练可并行;工业版如XGBoost、LightGBM通过工程优化显著提升速度与规模。

可解释与校准。

两者都能提供特征重要性。GBDT在逻辑损失下的概率输出(若有良好正则)通常校准性更好;RF概率往往偏

随机森林

随机森林由Breiman提出,核心是Bagging + 随机子空间 + 决策树弱偏好(高方差)的组合。

给定训练数据集

Bagging(Bootstrap Aggregating)

通过自助采样(有放回)从原始数据生成 个子样本集 ,对每个子样本集训练一棵决策树 ,最后做平均或投票:

回归:

分类(硬投票):

分类(软投票,概率平均):

方差分析:若每棵树的预测方差为 ,两两相关系数为 ,则平均后的方差为

增大 可将第二项压低至近0,但若 较大则方差下界为 。因此需要降低树间相关性——随机子空间就是为此服务。

决策树与分裂准则

分类树常用基尼系数或信息增益:

  • 基尼:
    其中 为结点 内类别 的样本比例。
  • 信息熵:

回归树常用平方误差:

对每个候选切分 (如特征 与阈值 ),选择最大化不纯度下降(或损失下降):

其中 可为

随机子空间

在每个结点分裂时随机抽取 个特征候选( )。这显著降低树与树之间的相关性 ,提升Bagging方差下降的收益。常见默认:

  • 分类:
  • 回归:

Out-of-Bag(OOB)估计

对于每棵树,由于bootstrap采样通常会留下约 的样本未被抽到(Out-of-Bag样本),可以用这些OOB样本对该树(或森林聚合)做验证,从而得到近似的无偏泛化误差估计。

OOB也可用于调参与早期诊断。

偏差-方差与复杂度

单棵深树:低偏差,高方差。

RF通过平均:保留较低偏差的同时显著降方差。

训练复杂度(粗略):每棵树 ,整体 ;可并行化到 台机器/线程。

聚合预测(回归/概率平均),方差分解,分裂准则不纯度下降 ,OOB近似泛化误差。RF没有显式的全局损失最小化目标函数。

GBDT

GBDT将决策树作为基学习器(弱学习器),在函数空间上沿负梯度方向迭代逼近最优。

给定损失函数 ,模型为加性形式:

其中 是第 棵树, 为步长系数, 为学习率。

函数空间梯度下降

在第 步,给定当前模型 ,计算负梯度(伪残差)作为新的拟合目标:

训练一棵回归树 去拟合

随后做线搜索确定 (或在叶子上做二阶近似求解),更新:

回归(平方误差)情形

,则

即传统的拟合残差。线搜索的闭式解为

(若 完美拟合残差,步长为1;实践中常用

二分类情形

,用对数损失

梯度与Hessian:

负梯度是 ,即观测标签减当前预测概率

叶子为单位的Newton近似(常见于XGBoost/LightGBM类实现),对第 棵树的某个叶子 ,叶子输出值可近似为:

其中 是二阶正则(L2)项。更新:

备注:sklearn的GradientBoostingClassifier使用一阶方法+线搜索思想;XGBoost/LightGBM使用二阶近似,工程更高效。

正则化与防过拟合

学习率 :越小越保守,需更多树数 才能达到同等训练误差,但泛化更好。经典经验:小 +大 优于大 +小

子采样:随机采样训练样本的子集训练每棵树(Stochastic GBDT),可视为近似引入噪声,提升泛化并加速。

结构约束:max_depth、min_samples_leaf、max_leaf_nodes控制树复杂度。

正则项:对叶子权重施加L1/L2(在XGBoost等实现中常见)。

早停:在验证集上监控指标随 变化,选择最优迭代轮次。

随机森林 vs GBDT

偏差-方差

  • RF:偏差较高、方差显著下降。对高噪声数据更加稳健。
  • GBDT:偏差快速下降,若正则不足(大深度、大学习率),则后期可能方差上升(过拟合)。

优化目标

  • RF:没有全局损失最小化,单棵树内做局部最优分裂,最后平均。
  • GBDT:显式最小化损失函数,在函数空间做梯度下降/二阶近似。

并行化

  • RF:天然并行(树与树独立)。
  • GBDT:树与树串行,工程化优化靠近似与系统设计。

超参敏感性

  • RF:更省心,默认配置往往表现不错;OOB能快速诊断。
  • GBDT:对学习率、树深、树数、子采样较敏感,需要验证集与早停。

对噪声与异常值

  • RF:更鲁棒,不容易被异常点拖偏整体模型。
  • GBDT:若损失为对数损失或平方误差,异常点影响较大;可用Huber、Fair之类更鲁棒的损失(在部分库中支持)。

概率校准与可解释

  • RF概率偏粗,需要时可加Platt/Isotonic校准。
  • GBDT逻辑损失下往往更接近概率(但仍需视正则而定)。
  • 两者都有特征重要性,但GBDT常与单调约束、PDP/ICE等解释方法搭配更自然。

何时选谁

  • 快速建立强基线、数据多噪声、多类型特征:RF。
  • 追求SOTA、能认真做验证与调参、数据非线性复杂:GBDT(工业常用XGBoost/LightGBM/CatBoost)。

完整案例

我们构造一个非线性+噪声的二分类数据集:用make_moons产生2个强信息维度,再添加8个高斯噪声维度。

将数据分为训练/验证/测试。

我们在案例中,对比RandomForestClassifier与GradientBoostingClassifier在以下方面的表现:决策边界、特征重要性对比。

RF的OOB误差曲线 vs GBDT的验证集对数损失曲线(同一子图双轴)。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.metrics import accuracy_score, roc_auc_score, log_loss
from matplotlib.colors import ListedColormap
import warnings
warnings.filterwarnings('ignore')

np.random.seed(42)

# 1) 生成非线性+噪声特征的数据
n_samples = 3500
X_2d, y = make_moons(n_samples=n_samples, noise=0.25, random_state=42)  # 两个非线性信息特征
noise_dims = 8
X_noise = np.random.normal(size=(n_samples, noise_dims)) * 1.0
X = np.hstack([X_2d, X_noise])  # 总特征维度=10,前2维是强信息

# 划分训练/验证/测试:60%/20%/20%
X_train_full, X_test, y_train_full, y_test = train_test_split(X, y, test_size=0.2, stratify=y, random_state=42)
X_train, X_valid, y_train, y_valid = train_test_split(X_train_full, y_train_full, test_size=0.25, stratify=y_train_full, random_state=42)
# 现在:训练60%,验证20%,测试20%

# 2) 训练RF和GBDT
# RF:使用OOB估计,并记录随树数量变化的OOB误差曲线
rf = RandomForestClassifier(
    n_estimators=10,  # 我们会用warm_start逐步增加
    max_features='sqrt',
    bootstrap=True,
    oob_score=True,
    n_jobs=-1,
    random_state=42,
    warm_start=True
)

# 为收集OOB曲线,逐步增加树的数量
rf_estimators_grid = list(range(1040110))  # 10 -> 400,每10步
rf_oob_errors = []
for n_trees in rf_estimators_grid:
    rf.set_params(n_estimators=n_trees)
    rf.fit(X_train, y_train)
    # oob_score_为OOB精度
    oob_error = 1.0 - rf.oob_score_
    rf_oob_errors.append(oob_error)

# 最终RF模型(400棵树)
rf_final = rf

# GBDT:使用对数损失,收集staged预测以计算验证集log loss曲线
gbdt = GradientBoostingClassifier(
    loss='log_loss',  # deviance的等价名称
    learning_rate=0.05,
    n_estimators=400,
    max_depth=3,
    subsample=0.8,
    random_state=42
)
gbdt.fit(X_train, y_train)

# 收集GBDT在验证集上的staged log loss
gbdt_valid_logloss = []
gbdt_train_logloss = []
for y_proba_valid, y_proba_train in zip(gbdt.staged_predict_proba(X_valid), gbdt.staged_predict_proba(X_train)):
    gbdt_valid_logloss.append(log_loss(y_valid, y_proba_valid))
    gbdt_train_logloss.append(log_loss(y_train, y_proba_train))

# 3) 测试集评估
def evaluate_model(name, model):
    y_proba_test = model.predict_proba(X_test)
    y_pred_test = np.argmax(y_proba_test, axis=1)
    acc = accuracy_score(y_test, y_pred_test)
    auc = roc_auc_score(y_test, y_proba_test[:, 1])
    ll = log_loss(y_test, y_proba_test)
    print(f"[{name}] Test Accuracy={acc:.4f}, ROC-AUC={auc:.4f}, LogLoss={ll:.4f}")

evaluate_model("RandomForest", rf_final)
evaluate_model("GBDT", gbdt)

# 4) 可视化
plt.figure(figsize=(1410))

cmap_bg = plt.cm.Spectral
color_pos = '#e41a1c'  # 红
color_neg = '#377eb8'  # 蓝
color_rf = '#4daf4a'   # 绿(RF)
color_gbdt = '#984ea3' # 紫(GBDT)

# 决策边界需要2D网格。我们用前两个信息特征绘制,其他特征固定为训练集均值
idx_plot = [01]
X_means = X_train.mean(axis=0)

def make_meshgrid(X2d, h=0.02, pad=0.8):
    x_min, x_max = X2d[:, 0].min() - pad, X2d[:, 0].max() + pad
    y_min, y_max = X2d[:, 1].min() - pad, X2d[:, 1].max() + pad
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))
    return xx, yy

def model_proba_on_grid(model, xx, yy, X_means, idx_plot):
    # 构造全特征矩阵:在网格点处给前两维赋值,其他维用均值
    grid_points = np.c_[xx.ravel(), yy.ravel()]
    X_grid = np.tile(X_means, (grid_points.shape[0], 1))
    X_grid[:, idx_plot[0]] = grid_points[:, 0]
    X_grid[:, idx_plot[1]] = grid_points[:, 1]
    proba = model.predict_proba(X_grid)[:, 1]
    return proba.reshape(xx.shape)

xx, yy = make_meshgrid(X_train[:, idx_plot])

# 图1:RF的决策边界
plt.subplot(221)
Z_rf = model_proba_on_grid(rf_final, xx, yy, X_means, idx_plot)
plt.contourf(xx, yy, Z_rf, alpha=0.85, cmap=cmap_bg)
plt.scatter(X_train[y_train==0][:, 0], X_train[y_train==0][:, 1], s=20, c=color_neg, edgecolor='k', alpha=0.9, label='Train 0')
plt.scatter(X_train[y_train==1][:, 0], X_train[y_train==1][:, 1], s=20, c=color_pos, edgecolor='k', alpha=0.9, label='Train 1')
plt.title("随机森林:决策边界(前两特征)", fontsize=12, fontweight='bold')
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.legend(loc='upper right', frameon=True)

# 图2:GBDT的决策边界
plt.subplot(222)
Z_gbdt = model_proba_on_grid(gbdt, xx, yy, X_means, idx_plot)
plt.contourf(xx, yy, Z_gbdt, alpha=0.85, cmap=cmap_bg)
plt.scatter(X_train[y_train==0][:, 0], X_train[y_train==0][:, 1], s=20, c=color_neg, edgecolor='k', alpha=0.9, label='Train 0')
plt.scatter(X_train[y_train==1][:, 0], X_train[y_train==1][:, 1], s=20, c=color_pos, edgecolor='k', alpha=0.9, label='Train 1')
plt.title("GBDT:决策边界(前两特征)", fontsize=12, fontweight='bold')
plt.xlabel("Feature 1")
plt.ylabel("Feature 2")
plt.legend(loc='upper right', frameon=True)

# 图3:特征重要性对比(前10维全部展示)
plt.subplot(223)
rf_imp = rf_final.feature_importances_
gbdt_imp = gbdt.feature_importances_
indices = np.arange(len(rf_imp))
width = 0.38
plt.bar(indices - width/2, rf_imp, width=width, color=color_rf, alpha=0.85, label='RF')
plt.bar(indices + width/2, gbdt_imp, width=width, color=color_gbdt, alpha=0.85, label='GBDT')
plt.xticks(indices, [f'F{i}' for i in range(X.shape[1])], rotation=0)
plt.ylabel("Feature Importance")
plt.title("特征重要性对比(RF vs GBDT)", fontsize=12, fontweight='bold')
plt.legend(loc='upper right', frameon=True)

# 图4:学习动态对比
# RF:OOB error vs #trees
# GBDT:Valid LogLoss vs #trees
plt.subplot(224)
x_rf = rf_estimators_grid
y_rf = rf_oob_errors
line1, = plt.plot(x_rf, y_rf, color=color_rf, lw=2.5, label='RF OOB Error')
plt.xlabel("#Trees / #Estimators")
plt.ylabel("RF OOB Error", color=color_rf)
plt.tick_params(axis='y', labelcolor=color_rf)
plt.title("学习动态:OOB误差 vs 验证LogLoss", fontsize=12, fontweight='bold')

# 叠加GBDT验证曲线(右轴)
ax2 = plt.gca().twinx()
x_gbdt = np.arange(1, len(gbdt_valid_logloss)+1)
y_gbdt = gbdt_valid_logloss
line2, = ax2.plot(x_gbdt, y_gbdt, color=color_gbdt, lw=2.5, label='GBDT Valid LogLoss')
ax2.set_ylabel("GBDT Valid LogLoss", color=color_gbdt)
ax2.tick_params(axis='y', labelcolor=color_gbdt)

# 合并图例
lines = [line1, line2]
labels = [l.get_label() for l in lines]
plt.legend(lines, labels, loc='upper right', frameon=True)

plt.tight_layout(rect=[00.0310.95])
plt.show()

随机森林的决策边界

背景色为模型对正类的预测概率等高线(颜色越亮/偏暖代表概率越大),训练样本散点覆盖其上,不同颜色表示不同类别。

随机森林由多棵深树投票/平均,决策边界通常呈现块状/台阶状的非线性形态,边界变化可能相对粗糙,但对噪声较鲁棒。

即使加入了8个噪声维度,RF通过随机子空间与多数投票,仍能学到稳定边界;其平均化的机制让单个树的过拟合不会直接导致整体过拟合。

GBDT的决策边界

同样是正类概率的等高线,但由GBDT的加法模型给出。

GBDT通过逐步拟合负梯度/残差,可以在复杂区域提升边界的细腻程度。一般情况下,你会看到GBDT边界在曲线难度较高的区域更贴合真实分布(例如月亮数据的弯曲部分)。

不过若学习率过大或树太深,边界可能呈现过拟合的局部硬折线,需要适当正则与早停。

特征重要性对比

展示全部10个特征的importance,左侧为RF,右侧为GBDT,颜色鲜明对比。

由于前2个特征为informative(make_moons),通常RF与GBDT都会为这两维给出最高重要性。

噪声特征的权重在两者中都应较低,但分布可能略有差异:RF因为随机子空间,在部分种子下可能对某些噪声特征给出相对不为零的重要性;GBDT则可能更集中于少数关键特征,尤其是在浅树+小学习率配置下。

学习动态对比

RF:随树的数量(#trees)增加的OOB误差曲线(左轴,绿色)。

GBDT:随迭代轮次的验证集LogLoss曲线(右轴,紫色)。

RF 随着树数增加,OOB误差逐步下降并趋于平稳,体现平均化收敛的特征;一旦达到足够多的树,继续增加对性能提升有限(但也不太会过拟合)。

GBDT,验证LogLoss在初期迅速下降(偏差快速下降),随后可能在某个拐点后趋于震荡或回升(过拟合开始);最优点常在某个中等迭代数,适合做早停。

总结

随机森林与GBDT是两类极为成功的树模型集成方法,其本质差异在于并行平均 vs 串行提升方差下降 vs 偏差下降启发式分裂 vs 函数空间优化

随机森林 往往是一个高质量、快稳的基线:极具鲁棒性、无需过多调参、OOB可做快速泛化估计。

GBDT 在合适的正则与早停设置下,往往能在复杂非线性问题上拿到更高的SOTA指标;但需在学习率、树深、树数、子采样等超参上更为谨慎。

最后

最近准备了16大块的内容,124个算法问题的总结,完整的机器学习小册,免费领取~
领取:备注「算法小册」即可~
扫码如有问题,记得添加微信号:xiaobai_ml012

【声明】内容源于网络
0
0
机器学习和人工智能AI
让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
内容 333
粉丝 0
机器学习和人工智能AI 让我们一起期待 AI 带给我们的每一场变革!推送最新行业内最新最前沿人工智能技术!
总阅读220
粉丝0
内容333