摘要 :前面学的每个算法都有各自的优缺点------线性回归简单但只能拟合线性关系,决策树可解释但容易过拟合,SVM 效果好但调参复杂。集成学习的思路是:不纠结于找"最好的单一算法",而是把多个"弱"模型组合起来,得到一个"强"模型。这就是"三个臭皮匠,顶个诸葛亮"的机器学习版本。这篇文章系统介绍三种集成策略:Bagging(并行)、Boosting(串行)、Stacking(混合),以及它们的代表算法。
一、为什么要集成?
"三个臭皮匠"的数学依据
假设有 5 个独立的分类器,每个的准确率都是 60%。如果让它们投票:
投票决定:至少 3 个分类器说"是"才算"是"
每个分类器正确的概率:p = 0.6
至少 3 个正确的概率:P(≥3) = 0.683
5 个 60% 的分类器投票 → 68.3% 的准确率!比单个高了 8 个百分点。
如果增加到 101 个 60% 的分类器:
P(≥51) ≈ 0.972 → 97.2% 的准确率!
这就是集成学习的数学基础------当个体独立且优于随机时,组合能大幅提升性能。
偏差-方差分解
理解集成学习需要先理解模型误差的两个来源:
| 误差来源 | 含义 | 类比 |
|---|---|---|
| 偏差(Bias) | 模型的系统性偏离------预测值的中心是否在真实值附近 | 射箭时,箭都偏离靶心同一个方向 |
| 方差(Variance) | 模型的波动程度------换不同数据训练,结果变化大不大 | 射箭时,箭散落分布在一大片区域 |
低偏差 + 低方差 = 理想(准确且稳定) ⭐
低偏差 + 高方差 = 过拟合(准但波动大) → Bagging 解决
高偏差 + 低方差 = 欠拟合(稳定但不准) → Boosting 解决
高偏差 + 高方差 = 最差(又不准又不稳) ❌
两种集成策略
集成学习的两个基本策略:
Bagging(并行):
训练多个独立模型 → 平均它们的预测
→ 降低方差(保持偏差不变)
→ 适合容易过拟合的模型(如决策树)
Boosting(串行):
逐个训练模型,每个模型修正前一个的错误
→ 降低偏差(同时控制方差)
→ 适合容易欠拟合的模型(如浅层决策树)
二、Bagging:并行集成
Bootstrap Aggregating
Bagging = Bootstrap + Aggregating(自助采样 + 聚合)。
Bootstrap 采样 :从原始数据集中有放回地抽取 N 个样本,得到一个新的训练集。这样重复 M 次,得到 M 个不同的训练集。
原始数据集 [1, 2, 3, 4, 5]
Bootstrap 采样 1: [1, 2, 2, 3, 5] ← 有重复
Bootstrap 采样 2: [1, 3, 4, 4, 5]
Bootstrap 采样 3: [2, 2, 3, 4, 5]
...共 M 次
聚合(Aggregating):
-
分类:M 个模型投票(少数服从多数)
-
回归:M 个模型的平均值
from sklearn.ensemble import BaggingClassifier
from sklearn.tree import DecisionTreeClassifierBagging + 决策树 = 随机森林的雏形
bagging = BaggingClassifier(
estimator=DecisionTreeClassifier(), # 基学习器
n_estimators=100, # 训练 100 棵树
max_samples=1.0, # 每棵树使用 100% 的样本(有放回)
bootstrap=True, # 有放回采样
n_jobs=-1 # 并行训练
)
bagging.fit(X_train, y_train)
随机森林(Random Forest)
随机森林 = Bagging + 决策树 + 特征随机性。
Bagging 只在样本上做随机采样,随机森林增加了一个关键步骤:在每个分裂点,只考虑随机选取的一部分特征。
传统 Bagging 决策树:
每个分裂点从所有特征中选择最佳分裂特征
→ 树的多样性有限(强特征总被选在最前面)
随机森林:
每个分裂点只考虑随机选取的 √n 或 n/3 个特征
→ 树的多样性更大
→ 集成效果更强
from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier(
n_estimators=100, # 树的数量
max_depth=10, # 每棵树的最大深度
min_samples_leaf=2, # 叶子最小样本数
max_features='sqrt', # 每棵树考虑的特征数:√n
random_state=42,
n_jobs=-1
)
rf.fit(X_train, y_train)
为什么特征随机性有效?
如果不加特征随机性:
所有树的根节点几乎都是同一个最强特征
→ 所有树高度相似 → 平均后提升有限
加了特征随机性:
每棵树从不同的角度"看"数据
→ 树的多样性大 → 不同树犯错方向不同 → 投票抵消错误
OOB 评估:不需要单独的验证集
Bootstrap 采样中,每个样本大约有 37% 的概率不被抽到 (当 N 很大时)。这些没被抽到的样本叫做 OOB(Out-Of-Bag)样本。
# 随机森林自动计算 OOB 分数
rf = RandomForestClassifier(
n_estimators=100,
oob_score=True, # 使用 OOB 评估
random_state=42
)
rf.fit(X_train, y_train)
print(f"OOB 准确率: {rf.oob_score_:.3f}")
print(f"测试集准确率: {rf.score(X_test, y_test):.3f}")
# OOB 分数 ≈ 测试准确率,不需要额外的验证集!
三、Boosting:串行集成
与 Bagging 的并行不同,Boosting 是串行 的------每一棵新树都在纠正前一棵树的错误。
3.1 AdaBoost(Adaptive Boosting)
1995 年提出的第一个实用 Boosting 算法,核心思想:增加错误分类样本的权重。
第一步:用等权重训练第一棵弱分类器
第二步:找出被分错的样本,提高它们的权重
第三步:用更新权重后的数据训练第二棵分类器
第四步:重复,直到达到指定数量的分类器
最终:所有分类器加权投票(准确率高的分类器权重大)
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
ada = AdaBoostClassifier(
estimator=DecisionTreeClassifier(max_depth=1), # "树桩"------只有一层的决策树
n_estimators=200,
learning_rate=1.0,
random_state=42
)
ada.fit(X_train, y_train)
3.2 梯度提升(Gradient Boosting)
梯度提升是 AdaBoost 的泛化和改进。核心思想:每一棵新树拟合的是前面所有树的"残差"。
梯度提升的直观理解:
第 1 棵树:拟合原始目标 y
→ 预测值 ŷ₁,残差 r₁ = y - ŷ₁
第 2 棵树:拟合残差 r₁
→ 预测值 ŷ₂,残差 r₂ = r₁ - ŷ₂
第 3 棵树:拟合残差 r₂
...
最终预测:ŷ = ŷ₁ + ŷ₂ + ŷ₃ + ... + ŷₘ
每一棵树都在"补窟窿"------弥补之前所有树的不足。
from sklearn.ensemble import GradientBoostingClassifier
gb = GradientBoostingClassifier(
n_estimators=100,
max_depth=3, # 每棵树较浅(一般 3-5)
learning_rate=0.1, # 学习率(每棵树的贡献)
subsample=0.8, # 每棵树只用 80% 的样本
random_state=42
)
gb.fit(X_train, y_train)
学习率(learning_rate)的作用:
学习率控制每棵树的"贡献大小":
learning_rate=1.0(没有收缩):
每棵树全力拟合残差 → 容易过拟合
learning_rate=0.1(收缩):
每棵树只贡献一小部分 → 需要更多树,但泛化更好
一般策略:learning_rate 小(0.01~0.1)+ n_estimators 大(200~2000)
3.3 XGBoost:梯度提升的工程化巅峰
XGBoost(eXtreme Gradient Boosting)是梯度提升的工程优化版本,2014 年由陈天奇提出。它在几个关键点上做了改进:
| 改进 | 效果 |
|---|---|
| 二阶导数近似 | 比一阶梯度的收敛更快、更精确 |
| 内置正则化 | 在损失函数中加入树的复杂度惩罚 |
| 列采样 | 借鉴随机森林的特征随机性 |
| 并行化 | 在特征维度上并行(虽然树是串行的) |
| 稀疏感知 | 自动处理缺失值 |
| 缓存优化 | 高效的缓存访问模式 |
import xgboost as xgb
# XGBoost 分类器
xgb_model = xgb.XGBClassifier(
n_estimators=200,
max_depth=5,
learning_rate=0.1,
subsample=0.8,
colsample_bytree=0.8, # 每棵树使用的特征比例
reg_alpha=0.1, # L1 正则化
reg_lambda=1.0, # L2 正则化
random_state=42,
n_jobs=-1
)
xgb_model.fit(X_train, y_train)
# 特征重要性
importance = xgb_model.feature_importances_
# XGBoost 的特征重要性有三种计算方式:
# 'weight': 特征被用来分裂的次数
# 'gain': 特征带来的平均信息增益
# 'cover': 特征覆盖的样本数
xgb.plot_importance(xgb_model, importance_type='gain')
XGBoost vs 普通梯度提升:
普通梯度提升: 每棵树用所有特征 → 容易过拟合
XGBoost: 列采样 + 正则化 → 泛化更强
普通梯度提升: 一阶导数近似 → 收敛较慢
XGBoost: 二阶导数近似 → 收敛更快、更准
普通梯度提升: 无法并行 → 训练慢
XGBoost: 特征并行 + 缓存优化 → 训练快 10 倍
3.4 LightGBM 与 CatBoost
| 算法 | 年份 | 核心创新 | 优势场景 |
|---|---|---|---|
| XGBoost | 2014 | 二阶导数 + 正则化 + 并行 | 结构化数据的通用王者 |
| LightGBM | 2017 | 基于直方图的决策树算法 + GOSS | 海量数据、高维特征 |
| CatBoost | 2017 | 有序提升 + 自动处理类别特征 | 含大量类别特征的数据 |
import lightgbm as lgb
# LightGBM 在数据量大时比 XGBoost 快几倍
lgb_model = lgb.LGBMClassifier(
n_estimators=200,
max_depth=5,
learning_rate=0.1,
num_leaves=31, # LightGBM 特有的参数
subsample=0.8,
colsample_bytree=0.8,
random_state=42
)
四、Bagging vs Boosting 对比
| 对比维度 | Bagging / 随机森林 | Boosting / XGBoost |
|---|---|---|
| 训练方式 | 并行(同时训练) | 串行(逐个训练) |
| 主要目标 | 降低方差(防止过拟合) | 降低偏差(提升准确性) |
| 基学习器 | 强学习器(可不剪枝) | 弱学习器(浅树) |
| 对噪声敏感度 | 不敏感(平均抵消噪声) | 敏感(会拟合噪声) |
| 训练速度 | 快(可并行) | 慢(必须串行) |
| 代表性算法 | 随机森林 | XGBoost / LightGBM |
| 调参难度 | 低(2-3 个关键参数) | 高(更多参数需配合) |
选型指南
数据量大且有噪声 → 随机森林(鲁棒、并行、不易过拟合)
追求极致准确率 → XGBoost / LightGBM(串行修正,更强)
数据含大量类别特征 → CatBoost(原生支持)
需要快速原型 → 随机森林(默认参数表现就不错)
五、Stacking:混合集成
Stacking 不是用同一种模型做集成,而是混合不同类型的模型。
第一层(基学习器):
模型 1(逻辑回归) → 输出概率 P₁
模型 2(SVM) → 输出概率 P₂
模型 3(随机森林) → 输出概率 P₃
模型 4(XGBoost) → 输出概率 P₄
第二层(元学习器):
输入 [P₁, P₂, P₃, P₄] → 逻辑回归 → 最终预测
from sklearn.ensemble import StackingClassifier
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
import xgboost as xgb
stacking = StackingClassifier(
estimators=[
('lr', LogisticRegression(max_iter=1000)),
('svm', SVC(kernel='rbf', probability=True)),
('rf', RandomForestClassifier(n_estimators=100)),
('xgb', xgb.XGBClassifier(n_estimators=100)),
],
final_estimator=LogisticRegression(), # 元学习器
cv=5, # 交叉验证防止过拟合
stack_method='predict_proba' # 使用概率作为元特征
)
stacking.fit(X_train, y_train)
print(f"Stacking 准确率: {stacking.score(X_test, y_test):.3f}")
# Stacking 通常比单个模型高 1-3%,但计算成本也高几倍
六、完整实战:多种集成方法对比
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.ensemble import (RandomForestClassifier,
GradientBoostingClassifier,
AdaBoostClassifier,
StackingClassifier)
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score
import xgboost as xgb
import time
# ===== 1. 生成数据 =====
X, y = make_classification(
n_samples=2000, n_features=20, n_informative=15,
n_redundant=3, random_state=42
)
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
# ===== 2. 定义模型 =====
models = {
'单棵决策树': DecisionTreeClassifier(max_depth=5, random_state=42),
'随机森林': RandomForestClassifier(n_estimators=200, max_depth=10, random_state=42, n_jobs=-1),
'AdaBoost': AdaBoostClassifier(n_estimators=200, random_state=42),
'梯度提升': GradientBoostingClassifier(n_estimators=200, max_depth=3, learning_rate=0.1, random_state=42),
'XGBoost': xgb.XGBClassifier(n_estimators=200, max_depth=3, learning_rate=0.1, random_state=42, n_jobs=-1),
'Stacking': StackingClassifier(
estimators=[
('rf', RandomForestClassifier(n_estimators=100, random_state=42)),
('xgb', xgb.XGBClassifier(n_estimators=100, random_state=42)),
('svm', SVC(kernel='rbf', probability=True, random_state=42)),
],
final_estimator=LogisticRegression(),
cv=3
),
}
# ===== 3. 训练与评估 =====
results = []
for name, model in models.items():
start = time.time()
model.fit(X_train, y_train)
train_time = time.time() - start
train_acc = accuracy_score(y_train, model.predict(X_train))
test_acc = accuracy_score(y_test, model.predict(X_test))
# 交叉验证(XGBoost 和 Stacking 较慢,跳过 cv)
if 'Stacking' not in name and 'XGBoost' not in name:
cv_scores = cross_val_score(model, X_train, y_train, cv=3)
cv_mean = cv_scores.mean()
else:
cv_mean = test_acc # 近似值
results.append({
'模型': name,
'训练准确率': f"{train_acc:.3f}",
'测试准确率': f"{test_acc:.3f}",
'训练时间(秒)': f"{train_time:.2f}"
})
results_df = pd.DataFrame(results)
print(results_df.to_string(index=False))
输出示例:
模型 训练准确率 测试准确率 训练时间(秒)
单棵决策树 0.942 0.888 0.02
随机森林 1.000 0.938 1.85
AdaBoost 0.956 0.915 0.45
梯度提升 0.982 0.930 2.10
XGBoost 0.988 0.938 0.55
Stacking 0.976 0.945 4.80
结果解读:
单棵决策树(88.8%):最差,但速度快、可解释
随机森林(93.8%):决策树的显著提升 ✅
AdaBoost(91.5%):比单棵树好,但不如 RF 和 Boosting
梯度提升(93.0%):和 RF 接近
XGBoost(93.8%):与 RF 持平,训练更快 ✅
Stacking(94.5%):通常最优,但最慢
数据量对集成方法的影响
小数据(<1000):随机森林 > XGBoost(RF 在小数据上更稳定)
中数据(1000-1万):XGBoost > 随机森林(GB 的串行修正优势体现)
大数据(>1万):LightGBM > XGBoost > 随机森林(速度差异明显)
超大数据(>10万):LightGBM 或神经网络(XGBoost 也变慢了)
七、集成学习的调参指南
随机森林
# 优先调这三个参数
rf = RandomForestClassifier(
n_estimators=500, # 越多越好,但边际递减(先固定 100-500)
max_depth=None, # 不限制深度(靠随机性防止过拟合)
min_samples_leaf=1, # 保持较小(默认为 1)
max_features='sqrt', # 关键参数:sqrt(n) 或 log2(n)
)
# 随机森林默认参数通常表现就不错
XGBoost
xgb_model = xgb.XGBClassifier(
# 核心参数
n_estimators=500, # 树的数量(与 learning_rate 配合)
learning_rate=0.05, # 学习率(越小需要越多树)
max_depth=5, # 树的深度(通常 3-8)
# 正则化
reg_alpha=0.1, # L1 正则化
reg_lambda=1.0, # L2 正则化
# 采样
subsample=0.8, # 行采样(每棵树用 80% 样本)
colsample_bytree=0.8, # 列采样(每棵树用 80% 特征)
# 停止条件
early_stopping_rounds=20, # 验证集 20 轮不提升就停止
)
XGBoost 调参顺序:
第一步:固定 learning_rate=0.1,调 max_depth 和 min_child_weight
第二步:调 subsample 和 colsample_bytree(防过拟合)
第三步:调 reg_alpha 和 reg_lambda(正则化)
第四步:降低 learning_rate(0.01~0.05),增加 n_estimators
八、集成学习在 2026 年的地位
| 任务类型 | 2026 年推荐方案 |
|---|---|
| 结构化数据分类/回归 | ✅ XGBoost / LightGBM------仍然是最强选择 |
| 中小规模数据 | ✅ 随机森林------稳健、不易过拟合 |
| 包含大量类别特征 | ✅ CatBoost------原生支持类别特征 |
| 图像/音频/文本 | ❌ 已被深度学习取代 |
| 超高精度需求(比赛) | ✅ Stacking------多个模型融合 |
| 可解释性要求高 | ⚠️ 随机森林(特征重要性)或单棵决策树 |
一个事实:在 Kaggle 竞赛中,XGBoost / LightGBM 是结构化数据任务上使用最频繁、表现最稳定的算法之一。即使到了 2026 年,深度学习在结构化数据上仍然没有全面超越梯度提升树。
九、总结
| 概念 | 一句话理解 |
|---|---|
| Bagging | 并行训练多个模型,平均它们的预测------降低方差 |
| 随机森林 | Bagging + 决策树 + 特征随机性------简单、稳定、强大 |
| Boosting | 串行训练,每棵树修正前一个的错误------降低偏差 |
| XGBoost | Boosting 的工程化巅峰------二阶导数 + 正则化 + 并行 |
| Stacking | 混合不同类型模型------集成的集成 |
| 三大参数 | n_estimators(多少个)、learning_rate(每步多大)、max_depth(多深) |
集成学习的核心思想链条:
单棵决策树(不稳定、易过拟合)
↓ Bagging(并行平均)
随机森林(稳定、泛化好)
↓ Boosting(串行修正)
XGBoost / LightGBM(精准、高效)
↓ Stacking(混合)
多个模型的融合(集成的极限)
下一篇文章:无监督学习------当数据没有标签时,如何发现隐藏的结构?