6.2 Bagging、随机森林与 OOB:误差相关性怎样限制平均的收益
一棵深树可能几乎记住训练数据,换一批样本后却连根节点都改了。对重采样训练的多棵树取平均,可以降低这种不稳定性;前提是它们没有整齐划一地犯错。
这就是 Bagging 的核心:对不稳定学习器重复抽样、拟合与平均。随机森林又在每个节点限制候选特征,主动降低树与树之间的相关性。
本课目标
- 理解 bootstrap aggregation 怎样降低方差;
- 解释随机森林的样本随机与特征随机;
- 正确使用 OOB 估计并识别其失效场景;
- 区分 impurity importance 与 permutation importance;
- 从准确率之外评估概率、资源成本与部署行为。
1. Bagging 的训练与预测
有
对
分类集成可以采用多数投票,也可以平均类别概率。下方 scikit-learn 森林使用概率平均,保留分数以便后续选择阈值和做校准。
2. 为什么“多”还不够
若每棵树在某个输入处的误差方差都是
当
例如
3. 随机森林怎样降低相关性
随机森林通常结合两个随机源:
- 每棵树使用不同 bootstrap sample;
- 每个节点只在随机抽取的特征子集中寻找最佳分裂。
max_features 越小,树之间通常越不相似,但单棵树也可能更弱。最优平衡由特征冗余、样本量和目标损失决定。
几个相近模型不要混为一谈:
- Bagging tree:bootstrap 行,节点可查看全部特征;
- Random forest:bootstrap 行,并在每个节点随机选择候选特征;
- Extra Trees:还会更随机地选择候选阈值,具体抽样行为看实现参数。
4. 一个可复现的训练骨架
以下是 Python 3 片段,已用 scikit-learn 1.9.0 核验。需提供独立的训练、验证数组,特征为可直接使用的数值,标签为 0/1,两个集合都含两类。这里假设行近似 IID;为保持下文的 OOB 含义,不先在全部训练行上拟合预处理器。
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import log_loss, roc_auc_score
forest = RandomForestClassifier(
n_estimators=500,
max_features="sqrt",
min_samples_leaf=5,
bootstrap=True,
oob_score=True,
n_jobs=1,
random_state=42,
)
forest.fit(X_train, y_train)
valid_probability = forest.predict_proba(X_valid)[:, 1]
print("validation log loss:", log_loss(y_valid, valid_probability))
print("validation ROC AUC:", roc_auc_score(y_valid, valid_probability))
print("OOB score:", forest.oob_score_)这里的 oob_score=True 返回 OOB accuracy。计算概率指标应使用 oob_decision_function_,并检查每行是否得到足够 OOB 投票。这是在相应抽样假设下评估森林;若预处理器已看过全部训练行,OOB 不能代替整个 pipeline 的独立验证。
类别列、缺失值和文本特征的预处理仍应封装进训练折内的 pipeline。某个库版本能接受 NaN,不代表所有森林实现都采用相同缺失路由。
5. OOB 从哪里来
一行在单棵树的
所以可用没有见过该行的树为它生成 out-of-bag 预测。聚合这些预测,能在不额外切出一块数据的情况下估计某些泛化指标。
但 OOB 不是万能验证集:
- 同一用户/设备的其他行可能已进入那棵树,造成 group leakage;
- 时间序列 bootstrap 会让未来记录帮助预测过去;
- 在全数据上先做填补、编码、selection,OOB 也救不了 preprocessing leakage;
- 调参反复查看 OOB 后,它已参与模型选择,仍需独立 test 做最终估计。
存在群组或时间结构时,优先采用 group/time-aware validation。
6. 主要容量参数怎样相互作用
树的容量
max_depth、min_samples_leaf、max_leaf_nodes 决定单树多细。完全长开的树不是随机森林的定义要求;噪声高、概率估计重要时,较大叶子常更稳定。
森林大小
增加 n_estimators 通常使有限森林的平均更稳定,本身不等同于 boosting 式的逐轮追噪声。但收益会趋于平台,训练、内存和推理成本持续增加。
样本与特征采样
max_samples 控制每棵树看到多少行,max_features 控制节点候选列。两者同时影响单树强度、相关性和成本。
类别/样本权重
class_weight 或 sample_weight 改变分裂目标和叶概率。它们可反映成本/抽样设计,但可能让原始目标分布上的概率失准,需要另行校准。
7. MDI importance 的偏差
许多实现的 feature_importances_ 汇总各特征带来的 mean decrease in impurity(MDI)。它便宜,却有明确限制:
- 在训练数据上统计,可能奖励过拟合分裂;
- 候选切分点多的连续/高基数特征容易获得更高重要性;
- 相关特征会分摊或替代彼此的重要性;
- 它描述模型怎样使用列,不是列对结果的因果作用。
不要据此直接删除其余特征后再在同一验证集宣称性能提高。
8. Permutation importance 也需要正确问题
在 held-out 数据上随机打乱某列,观察模型分数下降:
from sklearn.inspection import permutation_importance
result = permutation_importance(
forest,
X_valid,
y_valid,
scoring="neg_log_loss",
n_repeats=20,
random_state=42,
n_jobs=1,
)这估计的是“对这个已经训练好的模型,在当前验证分布中破坏该列会损失多少”。它依然不是因果效应。强相关列中,打乱一列时另一列仍携带同类信号,单列重要性可能都很低。
打乱相关列也可能产生真实数据中不存在的组合,夸大模型对该列的依赖;不能只考虑重要性被低估这一种方向。
可进一步:
- 对一组相关特征做 grouped permutation;
- 在多个时间/群组切片重复估计;
- 用 partial dependence、ICE 或 ALE 研究响应形状,同时说明其分布假设;
- 结合领域约束与稳定性分析,而非只排一个名次。
9. 概率、校准与阈值
森林概率是许多叶概率的平均。它可能比单树平滑,但不会自动校准。叶子太小、class weighting、分布变化都会影响概率。
评估应分开:
- discrimination:ROC AUC/AUPRC、排序;
- probability quality:log loss、Brier、reliability curve;
- decision:固定容量下的 recall、成本或净收益;
- stability:不同 fold、时间窗和随机种子的变化。
校准器必须用森林训练时没见过的数据,或使用交叉拟合。
10. 工程成本
树可并行训练和预测,但 n_jobs=-1 不代表资源免费:
- 训练进程可能复制数据并增加峰值内存;
- 大量深树会放大模型体积、冷启动和尾延迟;
- 线上并发与单请求内部并行可能争抢 CPU;
- 可复现需要固定随机种子、依赖版本和线程设置。
对表格数据,随机森林是值得比较的强 baseline 之一,不是任何任务都必须先选的模型。稀疏高维文本、平滑外推、严格延迟或可加性解释要求可能让线性/GAM/其他方法更合适。
常见误区
- 加树就能消除所有误差:相关误差和系统偏差仍会保留。
- OOB 等同独立 test:调参、群组、时间与前处理泄漏都会破坏它。
feature_importances_是客观特征价值:它是有偏的模型使用统计。- 随机森林必须把单树长到最大:树容量需要验证。
- 不标准化就等于不需要前处理:类别、缺失、时间与数据泄漏仍需处理。
练习
- 用不同
和 max_features记录 validation 误差与树间预测相关性。 - 比较 OOB 与独立 validation;再制造同用户多行数据观察泄漏。
- 加入一个高基数噪声列,比较 MDI 与 held-out permutation importance。
- 测量森林大小、峰值内存、单请求延迟和批量吞吐。
小结
Bagging 通过平均降低不稳定学习器的方差,随机森林用特征随机进一步降低树间相关性。OOB、重要性和概率输出都很有用,但各自回答的问题有限,必须放在正确的数据切分与评估协议中。
梯度提升改用顺序加树,让每一步沿当前损失的下降方向修正模型。