6. 机器学习 - 集成学习

1) 什么是

集成学习(Ensemble Learning)是一种机器学习的范式,它不是指代某一种具体的算法,而是一种算法思想。其核心在于通过组合多个基学习器(Base Learners) 的预测结果,来构建一个更强的“集成模型”(即强学习器),以期获得比单一模型更好的性能。

简单来说:“三个臭皮匠,顶个诸葛亮” —— 多个弱模型协同工作,可以超越单个强大模型的表现。

💡 常见的集成方法包括:Bagging、Boosting、Stacking、Voting 等。


2) 为什么

集成学习的核心思想是“集思广益”,即利用多个模型之间的差异性和互补性,减少偏差(bias)和方差(variance),从而提升整体模型的:

  • 预测性能(Accuracy, AUC 等指标更高)
  • 鲁棒性(对噪声、异常值更不敏感)
  • 泛化能力(在未见过的数据上表现更好)

举个例子:

  • 单个决策树容易过拟合 → 使用 Bagging(如 Random Forest)降低方差。
  • 单个模型欠拟合 → 使用 Boosting(如 XGBoost)逐步修正错误,降低偏差。

因此,集成学习能有效解决单一模型的局限性,在实践中广泛应用于各类竞赛和工业场景。


3) 什么时候用?

✅ 推荐使用集成学习的情况包括:

场景 说明
数据量较大 集成方法通常需要足够样本训练多个模型,大数据下效果更佳。
特征较多且复杂 如金融、医疗、推荐系统等高维数据,集成模型能捕捉复杂模式。
追求高精度 在 Kaggle、比赛或关键业务中,集成常用于冲击 Top 分数。
模型不稳定 若基础模型波动大(如单棵决策树),可用 Bagging 提升稳定性。
已有多个模型 可通过 Stacking 将不同模型融合,进一步提升性能。

📌 特别提示:当你是初学者或时间有限时,优先尝试 Random Forest 或 XGBoost,它们开箱即用、性能优秀。


4) 什么时候不用?

❌ 不推荐使用集成学习的情况包括:

场景 说明
数据量很小 模型数量多会加剧过拟合风险,且每个子模型都可能因样本不足而失效。
实时性要求极高 集成模型推理速度慢(需多个模型并行/串行计算),不适合低延迟场景(如自动驾驶)。
解释性要求强 集成模型(尤其是 Boosting)黑盒程度高,难以解释决策过程。
资源受限 训练和部署成本高,内存占用大,不适合边缘设备或轻量级系统。
已有高性能模型 如果单个模型已满足需求,无需增加复杂度。

💡 建议:先从简单模型开始,再考虑是否引入集成。


5) 总结

集成学习是一种强大的机器学习策略,它不依赖于某一特定算法,而是通过组合多个弱学习器来构建一个更强大、更稳定的模型。它的优势体现在:

  • ✅ 提升预测精度
  • ✅ 增强模型鲁棒性
  • ✅ 改善泛化能力

但也要注意其代价:更高的计算成本、更复杂的调参流程、较低的可解释性

📌 适用原则

有余力则集成,无必要不集成。”

在实际项目中,可以根据数据规模、业务需求、性能目标等因素灵活选择是否使用集成学习,并结合具体方法(如 Bagging、Boosting)进行优化。


📚 常见集成方法对比

方法 类型 代表算法 特点
Bagging 并行 Random Forest, Bagged Trees 降低方差,防过拟合
Boosting 串行 AdaBoost, GBDT, XGBoost, LightGBM 降低偏差,逐步优化
Stacking 混合 叠加多个模型 + 元学习器 更灵活,潜力大
Voting 投票 Hard/Voting Classifier 简单易用,适合多模型融合

★概念★

6.1 Bagging思想

🌲 Bagging(Bootstrap Aggregating)核心思想:
Bagging 是一种并行式集成方法,通过有放回地随机抽样(Bootstrap Sampling)生成多个训练子集,分别训练多个基学习器(通常是决策树),最后对它们的预测结果进行平均(回归)或投票(分类),以得到最终输出。

💡 目标:降低模型方差(variance),防止过拟合。

🔍 关键特点:
所有基学习器独立训练,彼此之间无依赖。
每个模型使用不同的数据子集,增加多样性。
对于不稳定的学习器(如决策树)效果显著。
⚠️ 注意:
Bagging 不会减少偏差,主要解决的是“高方差”问题。
基学习器通常选择同质模型(如都用决策树)。


6.1.1 随机森林

🌳 随机森林 = Bagging + 随机特征选择
在 Bagging 的基础上,随机森林进一步引入了“特征随机性”:

  • 在每个节点分裂时,只从随机选取的特征子集中选择最优分割特征(比如从 m 个特征中随机选 √m 个)。
  • 这增加了模型之间的差异性,进一步提升了泛化能力。

✅ 优点:

  • 能有效防止过拟合
  • 支持并行训练
  • 可评估特征重要性
  • 对异常值和缺失值鲁棒性强

❌ 缺点:

  • 解释性较差(黑盒)
  • 训练时间较长(尤其样本大时)
  • 不擅长处理排序任务(如推荐系统中的排序)
    📊 典型应用场景:
  • 分类与回归任务(如客户流失预测、房价预测)
  • 特征工程前的快速建模
  • 数据质量一般但样本量较大的场景

💡 小贴士:sklearn.ensemble.RandomForestClassifier 是最常用的实现方式。


6.2 Boosting思想

🔥 Boosting 核心思想:
Boosting 是一种串行式集成方法,它通过逐步迭代训练弱学习器,每一轮都关注前一轮预测错误的样本,赋予其更高的权重,从而让后续模型专注于“难学”的样本。

💡 目标:逐步降低偏差(bias),提升整体性能。

🔍 关键特点:

  • 模型是顺序构建的,后一个模型依赖前一个的结果。
  • 弱学习器通常是简单的模型(如浅层决策树)。
  • 最终预测是所有弱学习器加权组合的结果。
    🧠 类比:就像老师教学生,第一次没学会的地方,下次重点讲解。

6.2.1 Adaboost

🔄 AdaBoost 流程简述:

  • 初始化所有样本权重相等。
  • 训练第一个弱学习器(如决策树桩)。
  • 计算该学习器的误差率,调整样本权重:错误分类的样本权重上升。
  • 使用新的样本权重训练下一个弱学习器。
  • 重复以上步骤,直到达到指定轮数或误差足够小。
  • 最终预测结果由所有弱学习器按误差率反向加权投票决定。

✅ 优点:

  • 简单高效,理论基础扎实
  • 对离群点敏感度较低
  • 可用于分类和回归
    ❌ 缺点:
  • 对噪声和异常值敏感(因为会不断放大错误样本权重)
  • 容易过拟合(尤其在后期迭代)
  • 不能并行训练(串行)
    📊 应用场景:
  • 二分类任务(如垃圾邮件识别)
  • 图像识别早期应用(如人脸检测)
    💡 实现:sklearn.ensemble.AdaBoostClassifier

6.2.2 GDBT

📈 GDBT 核心思想:
GDBT 是 Boosting 的一种通用框架,它将梯度下降的思想应用于函数空间,把“优化损失函数”看作一个连续过程。

每一步都训练一个新的弱学习器来拟合上一轮预测结果与真实值之间的残差(负梯度)。

🔁 训练流程简化:

  • 初始化模型预测值(如均值)。
  • 对当前模型计算损失函数关于预测值的梯度(即残差)。
  • 训练一个弱学习器(通常是决策树)去拟合这个梯度。
  • 将新学习器加入整体模型,并更新预测值。
  • 重复上述过程,直到收敛或达到最大迭代次数。

✅ 优点:

  • 可以处理任意可微损失函数(如 MSE、Log Loss)
  • 模型表现强,常用于竞赛
  • 支持特征重要性分析

❌ 缺点:

  • 训练速度慢(串行)
  • 参数调优复杂
  • 易过拟合(需正则化控制)

💡 实现:sklearn.ensemble.GradientBoostingClassifier 或 xgboost, lightgbm 等优化版本。


6.2.3XGBoost(重点)

🚀 XGBoost 全称:eXtreme Gradient Boosting
XGBoost 是对 GBDT 的高度优化版本,在 Kaggle 和工业界广泛使用,被誉为“最强选手之一”。

🔍 核心改进点:

改进 说明
二阶导数优化 使用Hessian(二阶导数)进行更精确的参数更新,加快收敛速度
正则化项 在目标函数中加入L1/L2正则项,抑制过拟合
并行计算 特征预排序+并行计算,大幅提升训练效率
缺失值处理 自动学习缺失值的最佳分裂方向
内置早停机制 支持交叉验证和early stopping,避免过拟合
灵活的目标函数 支持回归、分类、排序等多种任务

🎯 优势总结:

  • 准确率高 → 多次赢得 Kaggle 比赛冠军
  • 效率高 → 比传统 GBDT 快 10~100 倍
  • 可扩展性强 → 支持分布式训练(如 Spark/XGBoost)
    📊 典型应用场景:
  • 金融风险评估
  • 推荐系统排序
  • 用户行为预测
  • 结构化数据建模(表格数据)
    🛠️ Python 实现示例:
import xgboost as xgb
from sklearn.model_selection import train_test_split

# 构建 DMatrix
dtrain = xgb.DMatrix(X_train, label=y_train)
dtest = xgb.DMatrix(X_test, label=y_test)

# 参数设置
params = {
    'objective': 'binary:logistic',
    'eval_metric': 'logloss',
    'max_depth': 6,
    'learning_rate': 0.1,
    'subsample': 0.8,
    'colsample_bytree': 0.8,
    'n_estimators': 100
}

# 训练
model = xgb.train(
    params,
    dtrain,
    num_boost_round=100,
    evals=[(dtrain, 'train'), (dtest, 'eval')],
    early_stopping_rounds=10
)

💡 提示:XGBoost 也支持 scikit-learn 接口:XGBClassifier, XGBRegressor


6.3 Bagging 与 Boosting 的对比

维度 Bagging Boosting
训练方式 并行 串行
样本权重 每个样本被均匀采样(Bootstrap) 错误样本权重递增
模型依赖 各模型独立 后续模型依赖前面模型
目标 降低方差(防过拟合) 降低偏差(提升精度)
适用模型 决策树、神经网络等 通常为弱学习器(如树桩)
容错性 强(个别模型出错不影响整体) 弱(前面模型错误会影响后面)
代表性算法 Random Forest AdaBoost, GBDT, XGBoost
训练速度 快(可并行) 慢(必须串行)
过拟合风险 较低 较高(需正则化控制)
解释性 一般 更差

🎯 总结一句话:
Bagging 是“百花齐放”,Boosting 是“精益求精”。

6.4 小结

方法 适用场景 推荐程度
Bagging (Random Forest) 数据量大、特征多、追求稳定性 ★★★★☆
Boosting (XGBoost) 追求最高精度、结构化数据、竞赛/生产环境 ★★★★★
AdaBoost 二分类、简单任务、快速原型 ★★☆☆☆
GBDT 中等规模任务、需要可解释性 ★★★★☆

💡 实际建议:

  • 初学者先掌握 Random Forest 和 XGBoost
  • 在比赛中优先尝试 XGBoost + 超参调优
  • 工业部署中注意模型大小与推理延迟

6.5案例

6.5.1 随机森林—泰坦尼克号幸存者特征案例

# 导包
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score,precision_score,recall_score,f1_score,roc_auc_score
# TODO 1.获取数据
df = pd.read_csv("data/train.csv")
# TODO 2.数据预处理
# 特征选择和标签选择
x = df[["Pclass", "Age", "Sex"]]
y = df["Survived"]
# 因为age数据中有缺失值, 所以需要处理缺失值
x = x.copy()
x["Age"] = x["Age"].fillna(x["Age"].mean())
# 因为sex数据是文本数据, 所以需要转换成数值
x = pd.get_dummies(x, drop_first=True)
# 因为数据是一个整体,所以需要将数据集进行切分
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=0)
# TODO 3.特征标准化
ss = StandardScaler()
# 注意: 训练用fit_transform()和测试用transform(),如果混乱使用,容易数据泄漏
new_x_train = ss.fit_transform(x_train)
new_x_test = ss.transform(x_test)
# TODO 4.模型训练
rf = RandomForestClassifier(n_estimators=10,criterion="gini",bootstrap=True)
rf.fit(new_x_train,y_train)
# TODO 5.模型预测/评估
y_pred = rf.predict(new_x_test)
print("准确率:",accuracy_score(y_test,y_pred))
print("精确率:",precision_score(y_test,y_pred))
print("召回率:",recall_score(y_test,y_pred))
print("F1值:",f1_score(y_test,y_pred))
print("AUC值:",roc_auc_score(y_test,y_pred))

6.5.2 Adboost-葡萄酒案例

# 导包
import pandas as pd
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import LabelEncoder, StandardScaler
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import AdaBoostClassifier

# 1.读取数据/了解数据
df = pd.read_csv("data/wine0501.csv")
print(df.shape)
print(df.columns)
print(df["Class label"].value_counts())
# 2.数据预处理
# 因为数据是3分类,咱们要改成2分类
new_df = df[df["Class label"] != 1]
# 提前划分特征和标签
x = new_df[['Alcohol', 'Malic acid', 'Hue']]
y = new_df["Class label"]
# 一般2分类是0和1,要把剩下的两类转换成0和1,todo 此处不用get_dummies(),咱们用LabelEncoder()
encoder = LabelEncoder()
y = encoder.fit_transform(y)
print(pd.DataFrame(y).value_counts())
# 划分数据集
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=0)
# 3.特征标准化
ss = StandardScaler()
new_x_train = ss.fit_transform(x_train)
new_x_test = ss.transform(x_test)
# 4.模型训练
model1 = DecisionTreeClassifier(criterion="gini", random_state=66)
model1.fit(new_x_train, y_train)
model2 = AdaBoostClassifier(n_estimators=7, random_state=66)
model2.fit(new_x_train, y_train)
# 5.模型预测/评估
y_pred1 = model1.predict(new_x_test)
print("准确率:", accuracy_score(y_test, y_pred1))
y_pred2 = model2.predict(new_x_test)
print("准确率:", accuracy_score(y_test, y_pred2))

6.5.3 GBDT-泰坦尼克号幸存者特征案例

# 导包
import pandas as pd
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.tree import DecisionTreeClassifier

# 1.读取数据
df = pd.read_csv("data/train.csv")
# 2.数据预处理
# 提前分割特征和标签
x = df[["Pclass", "Age", "Sex"]]
y = df["Survived"]
# 处理age列缺失值'
x = x.copy()
x["Age"] = x["Age"].fillna(x["Age"].mean())
# 处理sex列文本数据, 转换成数值
x = pd.get_dummies(x, drop_first=True)
# 数据切分4份
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=5)
# 3.特征标准化
ss = StandardScaler()
new_x_train = ss.fit_transform(x_train)
new_x_test = ss.transform(x_test)
# 4.模型训练
model1 = DecisionTreeClassifier(criterion="gini", random_state=66)
model1.fit(new_x_train, y_train)
model2 = GradientBoostingClassifier(n_estimators=500,loss="log_loss",random_state=66)
model2.fit(new_x_train, y_train)
# 5.模型预测/评估
y_pred = model1.predict(new_x_test)
print("准确率:", accuracy_score(y_test, y_pred))
y_pred = model2.predict(new_x_test)
print("准确率:", accuracy_score(y_test, y_pred))

6.5.4 XGBoost红葡萄酒案例

# 导包
import pandas as pd
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report, roc_auc_score, f1_score, \
    precision_score, recall_score
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 注意: 需要提前安装xgboost : pip install xgboost
from xgboost import XGBClassifier
import joblib

# 1.读取数据
df = pd.read_csv("data/红酒品质分类.csv")
print(df.shape)
print(df.columns)
print(df["quality"].value_counts())
# TODO 发现数据不均衡? 解决方案:  参数  选择合适的算法   重采样(过采样和欠采样)
# 2.数据预处理
# 提前分割特征和标签
x = df.iloc[:, :-1]
y = df.iloc[:, -1]
print(y.unique())
# 因为标签是345678,咱们要改成012345
y = y - 3
print(y.unique())
# 数据切分4份
# 注意: train_test_split()中有一个参数:TODO stratify=y表示训练集和测试集的类别均衡
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=5, stratify=y)
# 3.特征标准化
ss = StandardScaler()
# 注意: 训练集特征用fit_transform(),测试集只能用transform()
new_x_train = ss.fit_transform(x_train)
new_x_test = ss.transform(x_test)
# 4.模型训练
"""
scale_pos_weight 主要用于树模型(XGBoost、LightGBM),二分类问题
class_weight 在传统机器学习算法中更常见
"""
model = XGBClassifier(objective="multi:softmax")
model.fit(new_x_train, y_train)
# 5.模型预测/评估
y_pred = model.predict(new_x_test)
print(y_pred)
print("准确率:", accuracy_score(y_test, y_pred))
print("精确率:", precision_score(y_test, y_pred, average="weighted"))
print("召回率:", recall_score(y_test, y_pred, average="weighted"))
# TODO 6.模型保存
model.save_model("data/xgb_model1.json")
joblib.dump(model, "data/xgb_model2.pkl")


"""
XGBClassifier(objective=?)
"binary:logistic":
    用于二分类问题
    使用 logistic 损失函数
    输出概率值范围在 [0,1]
    适用于只有两个类别的分类任务
"multi:softmax":
    专为多分类设计的目标函数
    输出预测的类别标签(整数形式)
    计算交叉熵损失进行优化
"multi:softprob":
    同样用于多分类问题
    输出每个类别的预测概率
    返回一个概率分布矩阵
"reg:squarederror":
    用于回归任务
    使用平方误差作为损失函数
    输出连续数值而非类别
"""

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐