集成学习:从 Bagging 到 XGBoost

摘要 :前面学的每个算法都有各自的优缺点------线性回归简单但只能拟合线性关系,决策树可解释但容易过拟合,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 DecisionTreeClassifier

    Bagging + 决策树 = 随机森林的雏形

    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(混合)
多个模型的融合(集成的极限)

下一篇文章:无监督学习------当数据没有标签时,如何发现隐藏的结构?

相关推荐
Hanniel1 小时前
Python描述符(下):内置机制揭秘
开发语言·python·机器学习
果丁智能1 小时前
从人工值守到云端智控:物联网智能锁重塑公寓与集团宿舍管理体系
大数据·人工智能·物联网·智能家居
Inhand陈工1 小时前
污水泵站PLC数据上云实战:西门子PLC + 映翰通IG502 + DM平台全流程
人工智能·物联网·网络安全·阿里云·信息与通信·iot
冷小鱼1 小时前
PyTorch 2.12 完全指南:从动态图到编译优化的深度学习框架演进
人工智能·pytorch·深度学习
Cloud_Shy6181 小时前
解读《Effective Python 3rd Edition》:从练气到老魔(第七章 Item 52 - 53)
开发语言·人工智能·笔记·python
AI焦点1 小时前
2026年AI大模型中转横评实测:跨越价格陷阱,重构生产级聚合平台的评估基准
人工智能·重构
我不是FD1 小时前
OpenAI vs Anthropic API 对比:流式返回 + Adapt 适配层完整方案
java·人工智能·python
AI客栈1 小时前
容器启动调优:基于 Go 原生的冷启动时延评估与优化
人工智能
yyuuuzz2 小时前
2026游戏云服务器推荐的技术判断思路
运维·服务器·开发语言·网络·人工智能·游戏·php