【机器学习精通】第4章 | 集成学习进阶:Bagging、Boosting与Stacking
摘要:集成学习是机器学习领域的核心技术之一,通过组合多个弱学习器构建强学习器。本章深入讲解Bagging、Boosting、Stacking三大集成范式,剖析随机森林、XGBoost、LightGBM、CatBoost等主流算法的原理与差异,并提供完整的实战项目代码。
环境声明
- Python版本:Python 3.12+
- 核心库:Scikit-learn 1.4+、XGBoost 2.0+、LightGBM 4.0+、CatBoost 1.2+
- 开发工具:PyCharm / VS Code / Jupyter Notebook
- 操作系统:Windows / macOS / Linux(通用)
学习目标
完成本章学习后,你将能够:
- 理解集成学习的核心思想与三大范式(Bagging、Boosting、Stacking)
- 掌握随机森林算法的Bootstrap采样与OOB评估机制
- 深入理解AdaBoost与GBDT的数学推导
- 对比分析XGBoost、LightGBM、CatBoost的算法差异与适用场景
- 实现Stacking集成策略与元学习器
- 完成一个Kaggle风格的集成学习实战项目
1. 集成学习概述
1.1 强学习器与弱学习器
集成学习的核心思想源于一个基本问题:能否将多个弱学习器组合成一个强学习器?
弱学习器(Weak Learner):
- 定义:预测性能仅略优于随机猜测的学习器
- 典型代表:单层决策树(决策树桩)、浅层神经网络
- 特点:计算简单、容易训练、单独使用时精度有限
强学习器(Strong Learner):
- 定义:能够以较高准确率进行预测的学习器
- 典型代表:深度决策树、复杂神经网络、集成模型
- 特点:表达能力强大、但容易过拟合
补充:集成学习的理论基础来自Valiant提出的PAC(Probably Approximately Correct)学习框架,Schapire证明了弱学习器等价于强学习器。
1.2 集成策略分类
| 集成范式 | 基学习器关系 | 代表算法 | 核心思想 |
|---|---|---|---|
| Bagging | 并行、独立 | 随机森林 | Bootstrap采样降低方差 |
| Boosting | 串行、依赖 | AdaBoost、GBDT、XGBoost | 序列化训练降低偏差 |
| Stacking | 层级结构 | 堆叠泛化 | 元学习器组合基模型 |
1.3 多样性增强策略
集成学习的关键在于基学习器的多样性。常用策略包括:
数据层面:
- 数据采样扰动(Bootstrap、Bagging)
- 输入属性扰动(随机子空间法)
- 输出表示扰动(输出调制、ECOC编码)
算法层面:
- 基学习器类型多样化(决策树、SVM、神经网络混合)
- 超参数随机化
- 训练过程随机化(随机初始化、Dropout)
2. Bagging原理与随机森林
2.1 Bootstrap采样
Bagging(Bootstrap Aggregating)由Leo Breiman于1996年提出,其核心是有放回抽样。
Bootstrap采样原理:
给定包含m个样本的数据集D,进行m次有放回抽样,得到采样集D’。
样本在m次抽样中始终不被抽到的概率:
P(未被抽样)=(1−1m)m≈1e≈0.368P(\text{未被抽样}) = \left(1 - \frac{1}{m}\right)^m \approx \frac{1}{e} \approx 0.368P(未被抽样)=(1−m1)m≈e1≈0.368
这意味着每个Bootstrap采样集大约包含原始数据集的63.2%的样本,剩余36.8%的样本形成袋外样本(Out-of-Bag, OOB)。
2.2 Bagging算法流程
# Bagging算法伪代码示意
"""
输入:训练集 D = {(x1, y1), (x2, y2), ..., (xm, ym)}
基学习器算法 L
训练轮数 T
过程:
for t = 1 to T do
# 1. Bootstrap采样
Dt = BootstrapSample(D)
# 2. 训练基学习器
ht = L(Dt)
end for
输出:H(x) = argmax_y Σ_t I(ht(x) = y) # 分类:投票
H(x) = (1/T) Σ_t ht(x) # 回归:平均
"""
2.3 随机森林算法
随机森林(Random Forest)是Bagging的扩展,在决策树训练时引入随机特征选择。
随机森林的随机性来源:
- 样本随机性:Bootstrap采样
- 特征随机性:每个节点分裂时,从d个特征中随机选择k个候选特征(通常k = log₂d)
随机森林算法步骤:
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | Bootstrap采样 | 生成T个训练子集 |
| 2 | 构建决策树 | 对每个子集训练决策树 |
| 3 | 随机特征选择 | 节点分裂时随机选择k个特征 |
| 4 | 不剪枝生长 | 每棵树生长到最大深度 |
| 5 | 集成预测 | 投票(分类)或平均(回归) |
2.4 OOB评估
随机森林可以利用OOB样本进行无交叉验证的性能评估。
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
# 生成示例数据
X, y = make_classification(n_samples=1000, n_features=20,
n_informative=10, random_state=42)
# 训练随机森林,启用OOB评估
rf = RandomForestClassifier(
n_estimators=100,
max_depth=10,
oob_score=True, # 启用OOB评估
random_state=42,
n_jobs=-1
)
rf.fit(X, y)
print(f"OOB Score: {rf.oob_score_:.4f}")
OOB评估的优势:
- 无需划分验证集,充分利用训练数据
- 计算效率高,训练过程中自动完成
- 与交叉验证结果高度一致
3. Boosting原理
3.1 Boosting核心思想
Boosting是一类将弱学习器提升为强学习器的算法家族,核心思想是:
自适应地调整样本权重,使后续学习器专注于前面学习器分错的样本。
3.2 AdaBoost算法推导
AdaBoost(Adaptive Boosting)是Boosting家族的开创性算法。
算法初始化:
- 样本权重均匀分布:D1(i)=1mD_1(i) = \frac{1}{m}D1(i)=m1
第t轮迭代:
-
训练基学习器:在权重分布DtD_tDt下训练学习器hth_tht
-
计算加权错误率:
ϵt=∑i=1mDt(i)⋅I[ht(xi)≠yi]\epsilon_t = \sum_{i=1}^{m} D_t(i) \cdot \mathbb{I}[h_t(x_i) \neq y_i]ϵt=i=1∑mDt(i)⋅I[ht(xi)=yi] -
计算学习器权重:
αt=12ln(1−ϵtϵt)\alpha_t = \frac{1}{2} \ln\left(\frac{1-\epsilon_t}{\epsilon_t}\right)αt=21ln(ϵt1−ϵt) -
更新样本权重:
Dt+1(i)=Dt(i)Zt⋅exp(−αtyiht(xi))D_{t+1}(i) = \frac{D_t(i)}{Z_t} \cdot \exp(-\alpha_t y_i h_t(x_i))Dt+1(i)=ZtDt(i)⋅exp(−αtyiht(xi))其中ZtZ_tZt是归一化因子。
最终预测:
H(x)=sign(∑t=1Tαtht(x))H(x) = \text{sign}\left(\sum_{t=1}^{T} \alpha_t h_t(x)\right)H(x)=sign(t=1∑Tαtht(x))
from sklearn.ensemble import AdaBoostClassifier
from sklearn.tree import DecisionTreeClassifier
# 使用决策树桩作为基学习器
base_estimator = DecisionTreeClassifier(max_depth=1)
ada = AdaBoostClassifier(
estimator=base_estimator,
n_estimators=100,
learning_rate=0.5,
random_state=42
)
ada.fit(X, y)
3.3 GBDT原理
GBDT(Gradient Boosting Decision Tree)是Boosting的另一种实现,基于梯度下降思想。
核心思想:
将Boosting视为函数空间中的梯度下降,每一轮训练的新树拟合的是当前模型的负梯度(伪残差)。
数学推导:
假设损失函数为L(y,F(x))L(y, F(x))L(y,F(x)),当前模型为Ft−1(x)F_{t-1}(x)Ft−1(x)。
在第t轮,我们需要找到新树ht(x)h_t(x)ht(x)使得:
Ft(x)=Ft−1(x)+ρtht(x)F_t(x) = F_{t-1}(x) + \rho_t h_t(x)Ft(x)=Ft−1(x)+ρtht(x)
其中ht(x)h_t(x)ht(x)拟合负梯度:
ht(x)≈−[∂L(y,F(x))∂F(x)]F(x)=Ft−1(x)h_t(x) \approx -\left[\frac{\partial L(y, F(x))}{\partial F(x)}\right]_{F(x)=F_{t-1}(x)}ht(x)≈−[∂F(x)∂L(y,F(x))]F(x)=Ft−1(x)
不同损失函数对应的梯度:
| 损失函数 | 表达式 | 负梯度(伪残差) |
|---|---|---|
| 平方损失 | 12(y−F)2\frac{1}{2}(y-F)^221(y−F)2 | y−Fy - Fy−F |
| 绝对损失 | ∣y−F∣|y-F|∣y−F∣ | sign(y−F)\text{sign}(y-F)sign(y−F) |
| 对数损失 | log(1+e−yF)\log(1+e^{-yF})log(1+e−yF) | y−σ(yF)y - \sigma(yF)y−σ(yF) |
3.4 梯度提升框架
from sklearn.ensemble import GradientBoostingClassifier
gbdt = GradientBoostingClassifier(
n_estimators=100, # 树的数量
learning_rate=0.1, # 学习率(收缩因子)
max_depth=3, # 树的最大深度
subsample=0.8, # 子采样比例(随机梯度提升)
random_state=42
)
gbdt.fit(X, y)
关键超参数:
- n_estimators:基学习器数量,过多会过拟合
- learning_rate:学习率,与n_estimators trade-off
- max_depth:树深度,控制模型复杂度
- subsample:子采样比例,引入随机性防止过拟合
4. XGBoost/LightGBM/CatBoost深度对比
4.1 算法演进脉络
三大梯度提升框架的发布时间线:
| 算法 | 发布时间 | 开发团队 | 核心突破 |
|---|---|---|---|
| XGBoost | 2016年 | DMLC | 工程优化、正则化、并行计算 |
| LightGBM | 2017年 | Microsoft | 直方图算法、Leaf-wise生长、GOSS/EFB |
| CatBoost | 2017年 | Yandex | 原生类别特征、Ordered TS编码、对称树 |
4.2 XGBoost算法详解
XGBoost(eXtreme Gradient Boosting)在GBDT基础上进行了多项改进。
目标函数:
Obj=∑i=1nL(yi,y^i)+∑k=1KΩ(fk)\text{Obj} = \sum_{i=1}^{n} L(y_i, \hat{y}_i) + \sum_{k=1}^{K} \Omega(f_k)Obj=i=1∑nL(yi,y^i)+k=1∑KΩ(fk)
其中Ω(f)=γT+12λ∥w∥2\Omega(f) = \gamma T + \frac{1}{2}\lambda \|w\|^2Ω(f)=γT+21λ∥w∥2是正则化项,T为叶子节点数,w为叶子权重。
二阶泰勒展开:
XGBoost使用损失函数的二阶泰勒展开进行优化:
Obj(t)≈∑i=1n[gift(xi)+12hift2(xi)]+Ω(ft)\text{Obj}^{(t)} \approx \sum_{i=1}^{n} [g_i f_t(x_i) + \frac{1}{2} h_i f_t^2(x_i)] + \Omega(f_t)Obj(t)≈i=1∑n[gift(xi)+21hift2(xi)]+Ω(ft)
其中gi=∂y^(t−1)L(yi,y^(t−1))g_i = \partial_{\hat{y}^{(t-1)}} L(y_i, \hat{y}^{(t-1)})gi=∂y^(t−1)L(yi,y^(t−1)),hi=∂y^(t−1)2L(yi,y^(t−1))h_i = \partial^2_{\hat{y}^{(t-1)}} L(y_i, \hat{y}^{(t-1)})hi=∂y^(t−1)2L(yi,y^(t−1))
最优叶子权重:
wj∗=−∑i∈Ijgi∑i∈Ijhi+λw_j^* = -\frac{\sum_{i \in I_j} g_i}{\sum_{i \in I_j} h_i + \lambda}wj∗=−∑i∈Ijhi+λ∑i∈Ijgi
分裂增益计算:
Gain=12[GL2HL+λ+GR2HR+λ−(GL+GR)2HL+HR+λ]−γ\text{Gain} = \frac{1}{2}\left[\frac{G_L^2}{H_L+\lambda} + \frac{G_R^2}{H_R+\lambda} - \frac{(G_L+G_R)^2}{H_L+H_R+\lambda}\right] - \gammaGain=21[HL+λGL2+HR+λGR2−HL+HR+λ(GL+GR)2]−γ
import xgboost as xgb
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
# 生成数据
X, y = make_classification(n_samples=10000, n_features=50,
n_informative=20, random_state=42)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
# 转换为DMatrix格式(XGBoost专用数据结构)
dtrain = xgb.DMatrix(X_train, label=y_train)
dtest = xgb.DMatrix(X_test, label=y_test)
# 参数配置
params = {
'objective': 'binary:logistic',
'max_depth': 6,
'learning_rate': 0.1,
'subsample': 0.8,
'colsample_bytree': 0.8,
'reg_alpha': 0.1, # L1正则化
'reg_lambda': 1.0, # L2正则化
'eval_metric': 'auc'
}
# 训练模型
model = xgb.train(
params,
dtrain,
num_boost_round=100,
evals=[(dtest, 'test')],
early_stopping_rounds=10,
verbose_eval=False
)
4.3 LightGBM算法详解
LightGBM通过直方图算法和Leaf-wise树生长策略实现高效训练。
直方图算法(Histogram-based Algorithm):
- 将连续特征离散化为k个bin(默认255)
- 构建直方图统计梯度信息
- 基于直方图寻找最优分裂点
复杂度对比:
| 操作 | 传统GBDT | LightGBM直方图 |
|---|---|---|
| 寻找分裂点 | O(#data × #features) | O(#bin × #features) |
| 内存占用 | 存储所有数据 | 存储直方图(压缩) |
Leaf-wise vs Level-wise:
- Level-wise(XGBoost):按层生长,每层所有节点同时分裂
- Leaf-wise(LightGBM):选择分裂增益最大的叶子节点分裂
Leaf-wise优势:在相同节点数下,能获得更低的损失;但需要限制最大深度防止过拟合。
GOSS(Gradient-based One-Side Sampling):
保留梯度大的样本,对梯度小的样本随机采样,在保持数据分布的同时加速训练。
EFB(Exclusive Feature Bundling):
将互斥特征(不会同时取非零值)捆绑在一起,减少特征数量。
import lightgbm as lgb
# 创建Dataset
lgb_train = lgb.Dataset(X_train, y_train)
lgb_test = lgb.Dataset(X_test, y_test, reference=lgb_train)
# 参数配置
params = {
'objective': 'binary',
'metric': 'auc',
'boosting_type': 'gbdt',
'num_leaves': 31, # 叶子节点数
'learning_rate': 0.05,
'feature_fraction': 0.8, # 特征采样比例
'bagging_fraction': 0.8, # 数据采样比例
'bagging_freq': 5,
'verbose': -1
}
# 训练模型
model = lgb.train(
params,
lgb_train,
num_boost_round=100,
valid_sets=[lgb_test],
callbacks=[lgb.early_stopping(10), lgb.log_evaluation(0)]
)
4.4 CatBoost算法详解
CatBoost(Categorical Boosting)针对类别型特征进行了深度优化。
Ordered Target Statistics(Ordered TS):
解决类别特征编码时的目标泄露问题。
传统Target Encoding:
TS=∑iyi⋅I[xi=c]+a⋅p∑iI[xi=c]+a\text{TS} = \frac{\sum_{i} y_i \cdot \mathbb{I}[x_i = c] + a \cdot p}{\sum_{i} \mathbb{I}[x_i = c] + a}TS=∑iI[xi=c]+a∑iyi⋅I[xi=c]+a⋅p
Ordered TS改进:
- 引入随机排列,仅使用排列中前面的样本计算统计量
- 类似时序数据,避免信息泄露
对称树(Symmetric Trees):
CatBoost使用相同的分裂条件对所有节点进行分裂,形成完全二叉树结构。
优势:
- 预测速度更快(减少分支判断)
- 减少过拟合
from catboost import CatBoostClassifier
# CatBoost支持原生类别特征
cat_features = [0, 1, 2] # 类别特征索引
model = CatBoostClassifier(
iterations=100,
learning_rate=0.1,
depth=6,
loss_function='Logloss',
eval_metric='AUC',
random_seed=42,
verbose=False
)
model.fit(X_train, y_train, eval_set=(X_test, y_test), verbose=False)
4.5 三大框架深度对比
| 特性 | XGBoost | LightGBM | CatBoost |
|---|---|---|---|
| 训练速度 | 快 | 极快 | 中等 |
| 内存占用 | 较高 | 低 | 中等 |
| 类别特征 | 需预处理(One-hot/Label) | 需预处理 | 原生支持 |
| 缺失值处理 | 自动学习分裂方向 | 自动处理 | 自动处理 |
| 并行方式 | 特征并行 + 数据并行 | 特征并行 + 数据并行 | 特征并行 |
| 树生长策略 | Level-wise | Leaf-wise | Symmetric |
| GPU支持 | 支持 | 支持 | 支持 |
| 过拟合倾向 | 较低 | 中等(需调参) | 较低 |
适用场景建议:
- XGBoost:追求稳定性、需要详细调参控制、数据量中等
- LightGBM:大规模数据、追求训练速度、特征维度高
- CatBoost:类别特征丰富、追求开箱即用、对过拟合敏感
5. Stacking与Blending
5.1 Stacking原理
Stacking(堆叠泛化)是一种两层(或多层)的集成策略,使用**元学习器(Meta-learner)**来组合基学习器的预测结果。
Stacking架构:
输入特征 X
|
v
+------------------+
| 第一层(基学习器) |
| [模型A] [模型B] |
| [模型C] [模型D] |
+------------------+
|
v
+------------------+
| 第二层(元学习器) |
| 逻辑回归/线性模型 |
+------------------+
|
v
最终预测
关键问题:避免信息泄露
使用交叉验证生成元特征,确保元学习器的训练数据与基学习器的训练数据隔离。
5.2 Stacking实现
from sklearn.ensemble import StackingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
# 定义基学习器
estimators = [
('rf', RandomForestClassifier(n_estimators=100, random_state=42)),
('gbdt', GradientBoostingClassifier(n_estimators=100, random_state=42)),
('svc', SVC(probability=True, random_state=42))
]
# 定义元学习器
final_estimator = LogisticRegression()
# 创建Stacking分类器
stacking_clf = StackingClassifier(
estimators=estimators,
final_estimator=final_estimator,
cv=5, # 5折交叉验证生成元特征
stack_method='predict_proba', # 使用概率作为元特征
n_jobs=-1
)
stacking_clf.fit(X_train, y_train)
score = stacking_clf.score(X_test, y_test)
print(f"Stacking Accuracy: {score:.4f}")
5.3 Blending方法
Blending是Stacking的简化版本,使用**留出集(Hold-out Set)**而非交叉验证。
Blending流程:
- 将训练集划分为两部分:训练子集和验证子集
- 在训练子集上训练基学习器
- 基学习器对验证子集进行预测,生成元特征
- 元学习器在元特征上训练
Stacking vs Blending:
| 特性 | Stacking | Blending |
|---|---|---|
| 数据划分 | K折交叉验证 | 单次留出 |
| 数据利用率 | 高(所有数据参与) | 较低(验证集固定) |
| 计算成本 | 高(K倍训练) | 低 |
| 过拟合风险 | 较低 | 较高 |
6. Voting与加权集成
6.1 硬投票与软投票
Voting是最简单的集成策略,直接对基学习器的预测结果进行投票。
硬投票(Hard Voting):
y^=mode(h1(x),h2(x),...,hT(x))\hat{y} = \text{mode}(h_1(x), h_2(x), ..., h_T(x))y^=mode(h1(x),h2(x),...,hT(x))
选择预测类别最多的作为最终结果。
软投票(Soft Voting):
y^=argmaxy∑t=1Twt⋅Pt(y∣x)\hat{y} = \arg\max_y \sum_{t=1}^{T} w_t \cdot P_t(y|x)y^=argymaxt=1∑Twt⋅Pt(y∣x)
对预测概率进行加权平均,选择概率最高的类别。
from sklearn.ensemble import VotingClassifier
# 定义基学习器
clf1 = RandomForestClassifier(n_estimators=100, random_state=42)
clf2 = GradientBoostingClassifier(n_estimators=100, random_state=42)
clf3 = LogisticRegression(max_iter=1000, random_state=42)
# 硬投票
voting_hard = VotingClassifier(
estimators=[('rf', clf1), ('gb', clf2), ('lr', clf3)],
voting='hard'
)
# 软投票(需要基学习器支持predict_proba)
voting_soft = VotingClassifier(
estimators=[('rf', clf1), ('gb', clf2), ('lr', clf3)],
voting='soft',
weights=[2, 2, 1] # 加权投票
)
6.2 动态加权策略
基于验证集性能的加权:
import numpy as np
from sklearn.metrics import accuracy_score
# 训练基学习器
models = {
'rf': RandomForestClassifier(n_estimators=100, random_state=42),
'gbdt': GradientBoostingClassifier(n_estimators=100, random_state=42),
'xgb': xgb.XGBClassifier(n_estimators=100, random_state=42)
}
# 在验证集上评估性能
weights = {}
for name, model in models.items():
model.fit(X_train, y_train)
val_pred = model.predict(X_val)
acc = accuracy_score(y_val, val_pred)
weights[name] = acc
# 归一化权重
total = sum(weights.values())
weights = {k: v/total for k, v in weights.items()}
print(f"动态权重: {weights}")
7. 实战案例:Kaggle风格集成学习项目
7.1 项目背景
本案例使用经典的泰坦尼克号生存预测数据集,演示完整的集成学习流程。
7.2 完整代码实现
"""
集成学习实战:泰坦尼克号生存预测
使用Bagging、Boosting、Stacking等多种集成策略
"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split, cross_val_score, StratifiedKFold
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.ensemble import (
RandomForestClassifier,
GradientBoostingClassifier,
AdaBoostClassifier,
VotingClassifier,
StackingClassifier
)
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, classification_report, roc_auc_score
import xgboost as xgb
import lightgbm as lgb
from catboost import CatBoostClassifier
import warnings
warnings.filterwarnings('ignore')
# 设置随机种子
RANDOM_STATE = 42
np.random.seed(RANDOM_STATE)
# ==========================================
# 1. 数据加载与预处理
# ==========================================
print("=" * 50)
print("步骤1: 数据加载与预处理")
print("=" * 50)
# 加载数据(使用sklearn内置数据集模拟)
from sklearn.datasets import make_classification
# 生成模拟数据(模拟泰坦尼克号特征分布)
X, y = make_classification(
n_samples=891, # 泰坦尼克号乘客数
n_features=10,
n_informative=6,
n_redundant=2,
n_classes=2,
weights=[0.6, 0.4], # 生存率约40%
flip_y=0.1, # 添加噪声
random_state=RANDOM_STATE
)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=RANDOM_STATE, stratify=y
)
print(f"训练集大小: {X_train.shape}")
print(f"测试集大小: {X_test.shape}")
print(f"训练集生存率: {y_train.mean():.3f}")
# 特征标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
# ==========================================
# 2. 基学习器定义
# ==========================================
print("\n" + "=" * 50)
print("步骤2: 定义基学习器")
print("=" * 50)
# 定义各种基学习器
base_learners = {
'RandomForest': RandomForestClassifier(
n_estimators=200,
max_depth=10,
min_samples_split=5,
random_state=RANDOM_STATE,
n_jobs=-1
),
'GradientBoosting': GradientBoostingClassifier(
n_estimators=200,
learning_rate=0.1,
max_depth=4,
random_state=RANDOM_STATE
),
'AdaBoost': AdaBoostClassifier(
n_estimators=200,
learning_rate=0.5,
random_state=RANDOM_STATE
),
'XGBoost': xgb.XGBClassifier(
n_estimators=200,
max_depth=5,
learning_rate=0.1,
subsample=0.8,
colsample_bytree=0.8,
random_state=RANDOM_STATE,
eval_metric='logloss'
),
'LightGBM': lgb.LGBMClassifier(
n_estimators=200,
num_leaves=31,
learning_rate=0.05,
feature_fraction=0.8,
bagging_fraction=0.8,
random_state=RANDOM_STATE,
verbose=-1
),
'CatBoost': CatBoostClassifier(
iterations=200,
learning_rate=0.1,
depth=6,
random_seed=RANDOM_STATE,
verbose=False
),
'SVM': SVC(
probability=True,
kernel='rbf',
C=1.0,
random_state=RANDOM_STATE
),
'LogisticRegression': LogisticRegression(
max_iter=1000,
C=1.0,
random_state=RANDOM_STATE
)
}
# ==========================================
# 3. 基学习器交叉验证评估
# ==========================================
print("\n" + "=" * 50)
print("步骤3: 基学习器交叉验证评估")
print("=" * 50)
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=RANDOM_STATE)
cv_results = {}
for name, model in base_learners.items():
# 使用标准化数据(SVM和LR需要)
if name in ['SVM', 'LogisticRegression']:
scores = cross_val_score(model, X_train_scaled, y_train, cv=cv, scoring='accuracy')
else:
scores = cross_val_score(model, X_train, y_train, cv=cv, scoring='accuracy')
cv_results[name] = {
'mean': scores.mean(),
'std': scores.std()
}
print(f"{name:20s}: {scores.mean():.4f} (+/- {scores.std()*2:.4f})")
# ==========================================
# 4. 投票集成
# ==========================================
print("\n" + "=" * 50)
print("步骤4: 投票集成")
print("=" * 50)
# 选择表现最好的几个模型进行投票
top_models = ['RandomForest', 'XGBoost', 'LightGBM', 'CatBoost']
# 硬投票
estimators_for_voting = [
(name, base_learners[name]) for name in top_models
]
voting_hard = VotingClassifier(
estimators=estimators_for_voting,
voting='hard'
)
# 软投票
voting_soft = VotingClassifier(
estimators=estimators_for_voting,
voting='soft'
)
# 评估投票集成
for name, model in [('Voting_Hard', voting_hard), ('Voting_Soft', voting_soft)]:
scores = cross_val_score(model, X_train, y_train, cv=cv, scoring='accuracy')
cv_results[name] = {'mean': scores.mean(), 'std': scores.std()}
print(f"{name:20s}: {scores.mean():.4f} (+/- {scores.std()*2:.4f})")
# ==========================================
# 5. Stacking集成
# ==========================================
print("\n" + "=" * 50)
print("步骤5: Stacking集成")
print("=" * 50)
# 第一层:基学习器
stacking_estimators = [
('rf', RandomForestClassifier(n_estimators=100, random_state=RANDOM_STATE)),
('xgb', xgb.XGBClassifier(n_estimators=100, random_state=RANDOM_STATE, eval_metric='logloss')),
('lgb', lgb.LGBMClassifier(n_estimators=100, random_state=RANDOM_STATE, verbose=-1)),
('cat', CatBoostClassifier(iterations=100, random_seed=RANDOM_STATE, verbose=False))
]
# 第二层:元学习器
meta_learner = LogisticRegression(max_iter=1000, random_state=RANDOM_STATE)
# Stacking分类器
stacking_clf = StackingClassifier(
estimators=stacking_estimators,
final_estimator=meta_learner,
cv=5,
stack_method='predict_proba',
n_jobs=-1
)
scores = cross_val_score(stacking_clf, X_train, y_train, cv=cv, scoring='accuracy')
cv_results['Stacking'] = {'mean': scores.mean(), 'std': scores.std()}
print(f"{'Stacking':20s}: {scores.mean():.4f} (+/- {scores.std()*2:.4f})")
# ==========================================
# 6. 最终模型训练与评估
# ==========================================
print("\n" + "=" * 50)
print("步骤6: 最终模型训练与评估")
print("=" * 50)
# 选择最佳模型(这里以Stacking为例)
best_model = stacking_clf
best_model.fit(X_train, y_train)
# 测试集预测
y_pred = best_model.predict(X_test)
y_pred_proba = best_model.predict_proba(X_test)[:, 1]
# 评估指标
accuracy = accuracy_score(y_test, y_pred)
auc = roc_auc_score(y_test, y_pred_proba)
print(f"\n测试集准确率: {accuracy:.4f}")
print(f"测试集AUC: {auc:.4f}")
print("\n分类报告:")
print(classification_report(y_test, y_pred))
# ==========================================
# 7. 结果可视化
# ==========================================
print("\n" + "=" * 50)
print("步骤7: 结果可视化")
print("=" * 50)
# 绘制各模型性能对比
plt.figure(figsize=(12, 6))
models = list(cv_results.keys())
means = [cv_results[m]['mean'] for m in models]
stds = [cv_results[m]['std'] for m in models]
x_pos = np.arange(len(models))
plt.bar(x_pos, means, yerr=stds, align='center', alpha=0.7, capsize=5)
plt.xticks(x_pos, models, rotation=45, ha='right')
plt.ylabel('Accuracy')
plt.title('Model Comparison - Cross Validation Results')
plt.ylim([0.5, 1.0])
plt.grid(axis='y', alpha=0.3)
plt.tight_layout()
plt.savefig('ensemble_comparison.png', dpi=150)
print("对比图已保存为: ensemble_comparison.png")
# ==========================================
# 8. 特征重要性分析(以RandomForest为例)
# ==========================================
print("\n" + "=" * 50)
print("步骤8: 特征重要性分析")
print("=" * 50)
rf_model = RandomForestClassifier(n_estimators=200, random_state=RANDOM_STATE)
rf_model.fit(X_train, y_train)
feature_importance = pd.DataFrame({
'feature': [f'Feature_{i}' for i in range(X.shape[1])],
'importance': rf_model.feature_importances_
}).sort_values('importance', ascending=False)
print("\nTop 5 重要特征:")
print(feature_importance.head())
print("\n" + "=" * 50)
print("项目完成!")
print("=" * 50)
7.3 运行结果示例
==================================================
步骤3: 基学习器交叉验证评估
==================================================
RandomForest : 0.8236 (+/- 0.0452)
GradientBoosting : 0.8191 (+/- 0.0387)
AdaBoost : 0.8056 (+/- 0.0423)
XGBoost : 0.8315 (+/- 0.0398)
LightGBM : 0.8340 (+/- 0.0412)
CatBoost : 0.8291 (+/- 0.0376)
SVM : 0.7986 (+/- 0.0489)
LogisticRegression : 0.7923 (+/- 0.0512)
==================================================
步骤4: 投票集成
==================================================
Voting_Hard : 0.8365 (+/- 0.0389)
Voting_Soft : 0.8410 (+/- 0.0367)
==================================================
步骤5: Stacking集成
==================================================
Stacking : 0.8456 (+/- 0.0354)
8. 避坑小贴士
8.1 数据泄露问题
问题描述:在集成学习中,如果基学习器的预测结果与训练数据存在信息泄露,会导致Stacking等方法的评估结果虚高。
解决方案:
- 使用交叉验证生成元特征,而非直接在整个训练集上预测
- 确保训练集和测试集的划分在时间序列数据中遵循时序
8.2 过拟合陷阱
问题描述:集成模型复杂度高,容易在训练集上过拟合。
预防措施:
| 策略 | 适用场景 | 具体方法 |
|---|---|---|
| 早停法 | Boosting类模型 | 监控验证集损失,连续N轮不下降则停止 |
| 正则化 | XGBoost/LightGBM | 设置reg_alpha、reg_lambda |
| 子采样 | Bagging/Boosting | 设置subsample < 1.0 |
| 限制树深度 | 树模型 | max_depth控制在3-10之间 |
8.3 类别不平衡处理
问题描述:集成学习对类别不平衡敏感,多数类样本可能主导预测结果。
解决方案:
# 方法1: 类别权重
model = XGBClassifier(scale_pos_weight=len(neg)/len(pos))
# 方法2: 样本权重
sample_weight = compute_sample_weight('balanced', y_train)
model.fit(X_train, y_train, sample_weight=sample_weight)
# 方法3: SMOTE过采样
from imblearn.over_sampling import SMOTE
smote = SMOTE(random_state=42)
X_resampled, y_resampled = smote.fit_resample(X_train, y_train)
8.4 超参数调优建议
调优优先级:
- 学习率(learning_rate)与迭代次数(n_estimators):两者trade-off,学习率小则迭代次数多
- 树深度(max_depth/num_leaves):控制模型复杂度,先粗调再细调
- 采样比例(subsample/colsample):增加随机性,防止过拟合
- 正则化参数:最后微调
推荐调参工具:
- Optuna:基于贝叶斯优化的超参数搜索
- Ray Tune:分布式超参数调优
- Scikit-optimize:序列模型优化
9. 本章小结
本章深入讲解了集成学习的三大范式及其主流实现:
核心知识点回顾:
- Bagging:通过Bootstrap采样和并行训练降低方差,随机森林是其典型代表
- Boosting:通过序列化训练和样本加权降低偏差,AdaBoost、GBDT、XGBoost、LightGBM、CatBoost各有特色
- Stacking:通过元学习器组合基模型,实现更高层次的集成
算法选择指南:
- 数据量小、追求稳定性:选择XGBoost
- 数据量大、追求速度:选择LightGBM
- 类别特征多、追求易用性:选择CatBoost
- 需要解释性:选择随机森林
一句话总结:集成学习的本质是"三个臭皮匠,顶个诸葛亮",关键在于如何有效地组合多个学习器,使整体性能超越任何单一模型。
参考资料:
- Breiman L. Bagging predictors[J]. Machine learning, 1996.
- Freund Y, Schapire R E. A decision-theoretic generalization of on-line learning[J]. 1997.
- Chen T, Guestrin C. XGBoost: A scalable tree boosting system[C]. KDD 2016.
- Ke G, et al. LightGBM: A highly efficient gradient boosting decision tree[C]. NIPS 2017.
- Prokhorenkova L, et al. CatBoost: unbiased boosting with categorical features[C]. NIPS 2018.
本文首发于CSDN专栏《机器学习精通》,转载请注明出处。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)