随机森林不是一棵树,而是一组树共同投票。这种"多个模型组合决策"的做法就是集成学习(Ensemble Learning):不依赖单一模型,而是把多个模型的判断组合起来,让整体更稳或更强。
为什么要组合多个模型
单个模型各有短板。决策树容易过拟合,KNN 对特征尺度敏感,线性模型表达能力有限。一个模型在某些样本上判断很好,换一批样本可能就不稳定。
集成学习的思路是:
text
多个模型各自学习
-> 各自给出预测
-> 用投票、平均或加权方式合并结果
就像做判断时不止听一个人的意见,而是让多个视角互相补充。
集成学习主要解决两类问题:偏差与方差
机器学习里常提两个词:偏差和方差。偏差高,说明模型本身太简单,学不到真实规律;方差高,说明模型太敏感,容易被训练数据里的偶然细节带偏。
集成学习据此分成两个方向:
| 方法 | 主要目标 | 直觉 |
|---|---|---|
| Bagging | 降低方差 | 多个模型并行学习,最后投票或平均 |
| Boosting | 降低偏差 | 模型一个接一个学习,后面的重点修正前面的错误 |
随机森林属于 Bagging。梯度提升树(GBDT)、AdaBoost、XGBoost、LightGBM 走的是 Boosting 路线。
Bagging:有放回抽样再汇总
Bagging 是 Bootstrap Aggregating 的缩写,分两步:
- Bootstrap:从训练集中有放回抽样,生成多份不同的数据子集;
- Aggregating:分别训练多个模型,再把结果汇总。
有放回抽样意味着同一个样本可能被抽到多次,也可能一次都没被抽到。每棵树大约只能见到 63.2% 的不同样本,剩下没被抽中的称为袋外样本(out-of-bag)。每个模型看到的数据都不完全一样,学出的规律也有差异,最后平均或投票就能压低单个模型的偶然波动。
text
数据 ─┬─ 子集 1 ─→ 树 1 ─┐
├─ 子集 2 ─→ 树 2 ─┤
├─ ... → ... ┼─→ 投票 / 平均
└─ 子集 N ─→ 树 N ─┘
每棵树独立训练,互不相干
随机森林:Bagging 的典型代表
随机森林在 Bagging 之上又加了一层随机:
- 每棵树用不同的样本子集训练;
- 每次分裂节点时,只随机看一部分特征。
两层随机保证每棵树都不完全相同。分类时多数投票,回归时取平均。n_estimators 控制树的数量,树越多结果通常越稳,但训练和预测也会更慢。
Boosting:接力修正错误
Bagging 是多个模型并行训练、彼此独立;Boosting 更像接力。第一个模型先学一遍,后面的模型重点关注前面没学好的地方。
text
数据 → 树 1 → 放大错题 → 树 2 → 放大错题 → 树 3 → ... → 加权合并
后一棵树专攻前一棵树分错的样本
最终结果是把多个模型按权重组合,准确率高的模型说话更有分量。Boosting 通常能得到更强的效果,但也更容易受噪声影响:它会不断盯着错误样本,如果错误来自脏数据或错标签,模型可能越学越偏。
AdaBoost:放大错分样本的权重
AdaBoost 的做法是让后面的模型更关注前面分错的样本。某个样本前一轮分错了,下一轮的权重就变大,新模型就会更努力地学这些困难样本。这像老师批改作业后发现某些题错得多,下一轮就重点练这些题。
Gradient Boosting:拟合残差
Gradient Boosting 不直接调样本权重,而是让新模型去拟合前面模型的残差。回归任务里可以粗略理解为:
text
第一棵树预测一个结果
第二棵树学习第一棵树还差多少
第三棵树继续学习剩余误差
所以 Boosting 是逐步修正,而不是一次训练很多个独立模型。
代码:随机森林 vs AdaBoost 跑一遍
用 scikit-learn 造一份表格型数据,分别用 Bagging 和 Boosting 的代表模型训练对比:
python
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.metrics import accuracy_score
# 造 1000 样本、20 特征的分类数据,flip_y=0.1 人为加 10% 标签噪声
X, y = make_classification(
n_samples=1000, n_features=20, n_informative=8,
flip_y=0.1, random_state=42,
)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42,
)
# Bagging 代表:200 棵树并行,每棵只看部分样本和部分特征
rf = RandomForestClassifier(
n_estimators=200, max_depth=8, random_state=42, n_jobs=-1,
)
rf.fit(X_train, y_train)
print(f"随机森林准确率: {accuracy_score(y_test, rf.predict(X_test)):.3f}")
# Boosting 代表:用浅树当基学习器
# Boosting 靠"多棵弱树接力"取胜,单棵树太深反而过拟合,违背初衷
base_tree = DecisionTreeClassifier(max_depth=1) # 只有一层 = 决策树桩
ada = AdaBoostClassifier(estimator=base_tree, n_estimators=100, random_state=42)
ada.fit(X_train, y_train)
print(f"AdaBoost 准确率: {accuracy_score(y_test, ada.predict(X_test)):.3f}")
max_depth=1 的树叫决策树桩(decision stump),是 Boosting 常用的基学习器。基学习器故意弱,接力修正才有发挥空间。
Bagging 和 Boosting 怎么选
把两者的差异铺开对比:
| 维度 | Bagging | Boosting |
|---|---|---|
| 训练方式 | 多个模型并行 | 模型按顺序接力 |
| 每棵树的权重 | 等权,所有树同等发言 | 不等权,准确率高的树权重更大 |
| 样本权重 | 所有样本平权 | 被分错的样本权重更大 |
| 典型代表 | 随机森林 | AdaBoost、GBDT、XGBoost、LightGBM |
| 主要作用 | 降低方差,让结果更稳 | 降低偏差,让模型更强 |
| 对噪声 | 相对更稳 | 更敏感,可能把噪声当难题死磕 |
| 训练速度 | 快,可并行 | 慢,必须串行 |
| 调参难度 | 通常较低 | 通常更高 |
按问题类型决策:
text
过拟合严重(方差大)?
└─ 是 → Bagging / 随机森林
欠拟合严重(偏差大)?
└─ 是 → Boosting / XGBoost / LightGBM
数据里标签噪声多?
└─ Boosting 会把噪声当难题死磕,这时 Bagging 更安全
想快速建立一个稳定基线,随机森林很合适;数据已经比较干净、想进一步提升表格任务效果,再考虑 Boosting 类模型。
在文本分类里怎么用
传统文本分类常见流程:
text
文本清洗
-> 分词
-> TF-IDF
-> 分类器
分类器可以是逻辑回归,也可以是随机森林、梯度提升树等集成模型。但 TF-IDF 特征通常非常高维且稀疏,线性模型往往已经很强,集成树模型不一定更好,需要实际验证。
所以别因为"集成学习听起来更高级"就默认换模型。更稳妥的做法是在同一份数据、同一套指标下统一评估:
python
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.pipeline import Pipeline
from sklearn.model_selection import cross_val_score
# 玩具语料:实际项目替换成你清洗分词后的文本
texts = ["剧情精彩演员在线", "烂片浪费时间", "强烈推荐", "无聊透顶", "好看", "太差了"]
labels = [1, 0, 1, 0, 1, 0]
models = {
"logistic": Pipeline([
("tfidf", TfidfVectorizer()),
("clf", LogisticRegression(max_iter=1000)),
]),
"random_forest": Pipeline([
("tfidf", TfidfVectorizer()),
("clf", RandomForestClassifier(n_estimators=200, random_state=42)),
]),
}
# 同一份数据、同一套交叉验证,比的是同一把尺子
for name, model in models.items():
score = cross_val_score(model, texts, labels, cv=2, scoring="accuracy").mean()
print(f"{name}: accuracy = {score:.3f}")
常见错误
以为模型越多一定越好
集成学习不是简单堆数量。模型越多训练成本越高,收益也会递减,到了某个规模再加树基本不再提升。
忽略数据质量
Boosting 会重点学习错误样本。如果错误来自脏数据、错标签或数据泄漏,模型可能把错误学得更认真,反而更糟。
只看准确率
类别不平衡时,集成模型也可能偏向大类。还要看 precision、recall、F1 和混淆矩阵。
小结
- 集成学习不是一种具体模型,而是一类组合思想。
- Bagging 让多个模型并行、独立训练、投票或平均,降低方差,代表是随机森林。
- Boosting 让多个模型接力,后一个专攻前一个的错误,降低偏差,代表是 AdaBoost、GBDT、XGBoost、LightGBM。
- 数据噪声多时 Bagging 更稳;数据干净、偏差大时 Boosting 更强。
下一步进入文本分类项目之前,还要补一课:计算机怎样读取中文文本,以及编码、清洗和标准化为什么是 NLP 项目的第一步。
在线阅读
原文地址:https://bestsdz.xyz/posts/ensemble-learning-bagging-boosting/