欢迎光临
我们一直在努力

Stacked Generalization 堆叠泛化

目录

  • 引言:为什么需要 Stacking?
  • Stacking 的核心概念
  • 算法原理与数学形式化
  • 完整的 Stacking 流程
  • 关键设计决策
  • 变体与演进
  • 常见问题
  • 代码示例

  • 引言:为什么需要 Stacking?

    在机器学习竞赛(如 Kaggle)和工业实践中,集成学习(Ensemble Learning) 已成为提升模型性能的标准手段。传统的集成方法如 Bagging(随机森林)和 Boosting(XGBoost、LightGBM)通过组合同质基学习器来降低方差或偏差。

    然而,当面对异质模型(如神经网络、梯度提升树、支持向量机的组合)时,简单的投票(Voting)或平均(Averaging)往往无法充分挖掘各模型的互补性。Stacking(Stacked Generalization,堆叠泛化) 应运而生,它通过引入元学习器(Meta-Learner) 来学习如何最优地组合基学习器的预测,实现了从"简单平均"到"智能加权"的跃迁。

    核心洞见: Stacking 的本质是将模型预测作为特征,构建更高层次的特征表示,再由元学习器进行最终决策。


    Stacking 的核心概念

    2.1 定义

    Stacking 是一种两层(或多层)的集成学习架构,由 Wolpert 于 1992 年在论文 “Stacked Generalization” 中首次提出。其核心思想是:

    • 第一层(Level-0): 训练多个 diverse 的基学习器(Base Learners)
    • 第二层(Level-1): 使用基学习器的输出作为输入特征,训练一个元学习器(Meta-Learner)进行最终预测

    2.2 与相关概念的区分

    方法组合方式学习过程异质性支持
    Voting/Averaging 固定权重(等权或手工设定) 无学习 支持
    Weighted Averaging 优化权重(如通过验证集性能) 浅层优化 支持
    Stacking 元学习器动态学习组合策略 深层学习 支持
    Blending 使用 hold-out 验证集训练元学习器 单层学习 支持
    Boosting 序列训练,关注错误样本 自适应重加权 通常同质

    关键区别: Stacking 通过学习来发现基学习器之间的非线性交互,而非预设组合规则。


    算法原理与数学形式化

    3.1 符号定义

    设训练集为

    D

    =

    {

    (

    x

    i

    ,

    y

    i

    )

    }

    i

    =

    1

    N

    \\mathcal{D} = \\{(\\mathbf{x}_i, y_i)\\}_{i=1}^N

    D={(xi​,yi​)}i=1N​,其中

    x

    i

    ∈

    R

    d

    \\mathbf{x}_i \\in \\mathbb{R}^d

    xi​∈Rd,

    y

    i

    ∈

    Y

    y_i \\in \\mathcal{Y}

    yi​∈Y。

    • 基学习器集合:

      M

      =

      {

      M

      1

      ,

      M

      2

      ,

      …

      ,

      M

      K

      }

      \\mathcal{M} = \\{M_1, M_2, \\dots, M_K\\}

      M={M1​,M2​,…,MK​}

    • 基学习器

      M

      k

      M_k

      Mk​ 的预测:

      f

      k

      (

      x

      )

      ∈

      R

      C

      f_k(\\mathbf{x}) \\in \\mathbb{R}^{C}

      fk​(x)∈RC(

      C

      C

      C 为类别数,回归时

      C

      =

      1

      C=1

      C=1)

    • 元学习器:

      M

      m

      e

      t

      a

      M_{meta}

      Mmeta​

    3.2 训练阶段

    Step 1: 生成元特征(Meta-Features)

    为避免信息泄露(Data Leakage),必须使用交叉验证生成元特征:

    对于每个基学习器

    M

    k

    M_k

    Mk​,进行

    T

    T

    T-折交叉验证:

    • 将训练集划分为

      T

      T

      T 折:

      D

      =

      ⋃

      t

      =

      1

      T

      D

      t

      v

      a

      l

      \\mathcal{D} = \\bigcup_{t=1}^T \\mathcal{D}_t^{val}

      D=⋃t=1T​Dtval​

    • 对第

      t

      t

      t 折,使用

      D

      ∖

      D

      t

      v

      a

      l

      \\mathcal{D} \\setminus \\mathcal{D}_t^{val}

      D∖Dtval​ 训练

      M

      k

      (

      t

      )

      M_k^{(t)}

      Mk(t)​

    • 对

      D

      t

      v

      a

      l

      \\mathcal{D}_t^{val}

      Dtval​ 中的样本

      x

      i

      \\mathbf{x}_i

      xi​,生成预测

      f

      k

      (

      t

      )

      (

      x

      i

      )

      f_k^{(t)}(\\mathbf{x}_i)

      fk(t)​(xi​)

    最终,样本

    x

    i

    \\mathbf{x}_i

    xi​ 的元特征向量为:

    z

    i

    =

    [

    f

    1

    (

    x

    i

    )

    ,

    f

    2

    (

    x

    i

    )

    ,

    …

    ,

    f

    K

    (

    x

    i

    )

    ]

    ⊤

    ∈

    R

    K

    ⋅

    C

    \\mathbf{z}_i = [f_1(\\mathbf{x}_i), f_2(\\mathbf{x}_i), \\dots, f_K(\\mathbf{x}_i)]^\\top \\in \\mathbb{R}^{K \\cdot C}

    zi​=[f1​(xi​),f2​(xi​),…,fK​(xi​)]⊤∈RK⋅C

    Step 2: 训练元学习器

    构建元训练集

    D

    m

    e

    t

    a

    =

    {

    (

    z

    i

    ,

    y

    i

    )

    }

    i

    =

    1

    N

    \\mathcal{D}_{meta} = \\{(\\mathbf{z}_i, y_i)\\}_{i=1}^N

    Dmeta​={(zi​,yi​)}i=1N​,训练:

    M

    m

    e

    t

    a

    :

    R

    K

    ⋅

    C

    →

    Y

    M_{meta}: \\mathbb{R}^{K \\cdot C} \\to \\mathcal{Y}

    Mmeta​:RK⋅C→Y

    3.3 预测阶段

    对于新样本

    x

    \\mathbf{x}

    x:

  • 所有基学习器在完整训练集上重新训练,得到

    {

    M

    k

    ∗

    }

    k

    =

    1

    K

    \\{M_k^*\\}_{k=1}^K

    {Mk∗​}k=1K​

  • 生成元特征:

    z

    =

    [

    M

    1

    ∗

    (

    x

    )

    ,

    …

    ,

    M

    K

    ∗

    (

    x

    )

    ]

    ⊤

    \\mathbf{z} = [M_1^*(\\mathbf{x}), \\dots, M_K^*(\\mathbf{x})]^\\top

    z=[M1∗​(x),…,MK∗​(x)]⊤

  • 元学习器预测:

    y

    ^

    =

    M

    m

    e

    t

    a

    ∗

    (

    z

    )

    \\hat{y} = M_{meta}^*(\\mathbf{z})

    y^​=Mmeta∗​(z)

  • 3.4 数学优化视角

    Stacking 可视为最小化泛化误差的优化问题:

    min

    ⁡

    M

    m

    e

    t

    a

    E

    (

    x

    ,

    y

    )

    ∼

    P

    [

    L

    (

    y

    ,

    M

    m

    e

    t

    a

    (

    f

    1

    (

    x

    )

    ,

    …

    ,

    f

    K

    (

    x

    )

    )

    )

    ]

    \\min_{M_{meta}} \\mathbb{E}_{(\\mathbf{x},y) \\sim \\mathcal{P}} \\left[ \\mathcal{L}\\left(y, M_{meta}(f_1(\\mathbf{x}), \\dots, f_K(\\mathbf{x}))\\right) \\right]

    Mmeta​min​E(x,y)∼P​[L(y,Mmeta​(f1​(x),…,fK​(x)))]

    其中

    L

    \\mathcal{L}

    L 为损失函数。元学习器学习的是条件期望的估计:

    M

    m

    e

    t

    a

    ∗

    (

    z

    )

    ≈

    E

    [

    Y

    ∣

    Z

    =

    z

    ]

    M_{meta}^*(\\mathbf{z}) \\approx \\mathbb{E}[Y | Z=\\mathbf{z}]

    Mmeta∗​(z)≈E[Y∣Z=z]


    完整的 Stacking 流程

    ┌─────────────────────────────────────────────────────────────┐
    │ Stacking 架构示意图 │
    ├─────────────────────────────────────────────────────────────┤
    │ │
    │ Level-0 (Base Learners) Level-1 (Meta-Learner) │
    │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
    │ │ Model 1 │ │ Model 2 │ │ Model K │ ┌─────────┐ │
    │ │ (RF) │ │ (LGB) │ │ (NN) │───────▶│ LR/ │ │
    │ └────┬────┘ └────┬────┘ └────┬────┘ │ Ridge │ │
    │ │ │ │ └────┬────┘ │
    │ └────────────┴────────────┘ │ │
    │ │ │ │
    │ ▼ ▼ │
    │ ┌─────────────┐ ┌─────────┐ │
    │ │ Meta-Features │ │ Final │ │
    │ │ (Z-matrix) │──────────────▶│ Output │ │
    │ └─────────────┘ └─────────┘ │
    │ │
    └─────────────────────────────────────────────────────────────┘

    4.1 算法流程

    训练阶段:

    # 伪代码表示
    def train_stacking(X_train, y_train, base_models, meta_model, n_folds=5):
    # 1. 初始化元特征矩阵
    n_samples = len(X_train)
    n_models = len(base_models)
    Z = np.zeros((n_samples, n_models)) # 元特征矩阵

    # 2. 交叉验证生成元特征
    kfold = KFold(n_splits=n_folds, shuffle=True)

    for i, model in enumerate(base_models):
    for train_idx, val_idx in kfold.split(X_train):
    # 划分数据
    X_tr, X_val = X_train[train_idx], X_train[val_idx]
    y_tr = y_train[train_idx]

    # 训练基学习器并预测验证集
    model.fit(X_tr, y_tr)
    Z[val_idx, i] = model.predict(X_val)

    # 3. 在完整数据上重新训练基学习器(用于后续预测)
    fitted_base_models = []
    for model in base_models:
    model_copy = clone(model)
    model_copy.fit(X_train, y_train)
    fitted_base_models.append(model_copy)

    # 4. 训练元学习器
    meta_model.fit(Z, y_train)

    return fitted_base_models, meta_model

    预测阶段:

    def predict_stacking(X_new, fitted_base_models, meta_model):
    # 1. 生成元特征
    Z_new = np.column_stack([
    model.predict(X_new) for model in fitted_base_models
    ])

    # 2. 元学习器预测
    return meta_model.predict(Z_new)


    关键设计决策

    5.1 基学习器的选择:多样性与准确性的权衡

    多样性(Diversity) 是 Stacking 成功的关键。理想情况下,基学习器应:

    • 算法不同: 树模型、线性模型、神经网络、KNN 等
    • 数据视角不同: 不同特征子集、不同样本权重
    • 超参数不同: 同一算法的不同配置

    常用组合:

    • 经典组合: Random Forest + Gradient Boosting + SVM + Neural Network
    • 竞赛组合: XGBoost + LightGBM + CatBoost + Neural Network
    • 深度学习组合: ResNet + EfficientNet + Vision Transformer

    5.2 元学习器的选择

    元学习器适用场景优点缺点
    逻辑回归/线性回归 分类/回归基准 简单、可解释、不易过拟合 只能学习线性组合
    Ridge/Lasso 多重共线性严重 正则化防止过拟合 仍是线性模型
    梯度提升树 需要非线性交互 学习复杂模式 容易过拟合
    神经网络 大规模数据 表达能力强 需要大量数据、调参复杂
    随机森林 鲁棒性要求高 抗噪声 可解释性较差

    推荐实践: 从简单的 Ridge 回归(回归任务)或 逻辑回归(分类任务)开始,逐步尝试更复杂的元学习器。

    5.3 交叉验证策略

    必须避免: 使用训练集预测训练集自身(In-sample prediction),这会导致严重的信息泄露和过拟合。

    推荐策略:

    • K-Fold CV: 标准选择,

      K

      =

      5

      K=5

      K=5 或

      10

      10

      10

    • Stratified K-Fold: 分类任务中保持类别比例
    • Time Series Split: 时序数据避免未来信息泄露

    5.4 特征增强策略

    除了基学习器的预测概率/值,元特征还可包括:

    • 原始特征: 将

      x

      \\mathbf{x}

      x 与

      z

      \\mathbf{z}

      z 拼接,形成

      (

      x

      ,

      z

      )

      (\\mathbf{x}, \\mathbf{z})

      (x,z)

    • 统计特征: 基学习器预测的标准差、最大值、最小值
    • 高阶交互: 基学习器预测的乘积、比值

    变体与演进

    6.1 Blending

    Blending 是 Stacking 的简化版本,使用固定的 hold-out 验证集生成元特征,而非交叉验证。

    优点: 实现简单、计算成本低 缺点: 元学习器训练数据较少、验证集划分敏感、信息利用不充分

    适用: 快速原型验证或数据量极大的场景。

    6.2 Multi-Level Stacking

    将 Stacking 扩展到多层:

    Level-0: 基学习器 (RF, GBDT, NN)
    ↓
    Level-1: 元学习器1 (RF, GBDT) ← 可多个
    ↓
    Level-2: 元学习器2 (LR) ← 最终输出

    风险: 随着层数增加,过拟合风险急剧上升,可解释性下降。

    6.3 Feature-Weighted Linear Stacking (FWLS)

    Netflix 竞赛中提出的变体,元学习器为:

    y

    ^

    =

    ∑

    k

    =

    1

    K

    w

    k

    (

    x

    )

    ⋅

    f

    k

    (

    x

    )

    \\hat{y} = \\sum_{k=1}^K w_k(\\mathbf{x}) \\cdot f_k(\\mathbf{x})

    y^​=k=1∑K​wk​(x)⋅fk​(x)

    其中权重

    w

    k

    (

    x

    )

    w_k(\\mathbf{x})

    wk​(x) 依赖于原始特征

    x

    \\mathbf{x}

    x,通过另一组模型学习。

    6.4 深度学习中的 Stacking

    在现代深度学习中,Stacking 思想演变为:

    • Mixture of Experts (MoE): 门控网络动态选择专家
    • Ensemble Distillation: 将 Stacking 集成知识蒸馏到单一模型
    • Stacked Encoders: 自编码器的逐层堆叠预训练

    常见问题

    陷阱表现解决方案
    信息泄露 训练集准确率极高,测试集极差 严格使用交叉验证生成元特征
    同质化基学习器 Stacking 效果不如单一最佳模型 确保基学习器多样性(相关性 < 0.9)
    元学习器过拟合 元特征维度高、样本少 使用简单元学习器、增加正则化
    数据划分不一致 时序数据泄露、类别不平衡 使用 StratifiedKFold、TimeSeriesSplit
    计算资源爆炸 多层 Stacking 训练时间过长 限制层数、使用并行训练

    代码示例

    8.1 使用 Scikit-learn 实现

    from sklearn.datasets import load_breast_cancer
    from sklearn.model_selection import StratifiedKFold, cross_val_score
    from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
    from sklearn.svm import SVC
    from sklearn.linear_model import LogisticRegression
    from sklearn.preprocessing import StandardScaler
    from sklearn.pipeline import Pipeline
    import numpy as np

    # 加载数据
    data = load_breast_cancer()
    X, y = data.data, data.target

    # 定义基学习器
    base_learners = [
    ('rf', RandomForestClassifier(n_estimators=100, random_state=42)),
    ('gbdt', GradientBoostingClassifier(n_estimators=100, random_state=42)),
    ('svc', Pipeline([
    ('scaler', StandardScaler()),
    ('svc', SVC(probability=True, random_state=42))
    ]))
    ]

    # 元学习器
    meta_learner = LogisticRegression(C=1.0, solver='lbfgs', max_iter=1000)

    # Stacking 实现
    class StackingClassifier:
    def __init__(self, base_learners, meta_learner, n_folds=5):
    self.base_learners = base_learners
    self.meta_learner = meta_learner
    self.n_folds = n_folds
    self.base_models_ = []

    def fit(self, X, y):
    n_samples = X.shape[0]
    n_base = len(self.base_learners)

    # 初始化元特征矩阵 (使用概率预测)
    self.n_classes_ = len(np.unique(y))
    Z = np.zeros((n_samples, n_base * self.n_classes_))

    # 交叉验证生成元特征
    kfold = StratifiedKFold(n_splits=self.n_folds, shuffle=True, random_state=42)

    for i, (name, model) in enumerate(self.base_learners):
    print(f"Training base learner: {name}")

    # 存储每折训练的模型
    fold_models = []

    for train_idx, val_idx in kfold.split(X, y):
    X_train, X_val = X[train_idx], X[val_idx]
    y_train = y[train_idx]

    model_copy = clone(model)
    model_copy.fit(X_train, y_train)

    # 预测概率作为元特征
    probs = model_copy.predict_proba(X_val)
    Z[val_idx, i*self.n_classes_:(i+1)*self.n_classes_] = probs

    fold_models.append(model_copy)

    # 在完整数据上重新训练,用于后续预测
    full_model = clone(model)
    full_model.fit(X, y)
    self.base_models_.append(full_model)

    # 训练元学习器
    print("Training meta-learner…")
    self.meta_learner.fit(Z, y)

    return self

    def predict(self, X):
    # 生成元特征
    Z = self._generate_meta_features(X)
    return self.meta_learner.predict(Z)

    def predict_proba(self, X):
    Z = self._generate_meta_features(X)
    return self.meta_learner.predict_proba(Z)

    def _generate_meta_features(self, X):
    n_samples = X.shape[0]
    n_base = len(self.base_learners)
    Z = np.zeros((n_samples, n_base * self.n_classes_))

    for i, model in enumerate(self.base_models_):
    probs = model.predict_proba(X)
    Z[:, i*self.n_classes_:(i+1)*self.n_classes_] = probs

    return Z

    # 使用示例
    from sklearn.base import clone
    from sklearn.metrics import accuracy_score

    stacking_clf = StackingClassifier(base_learners, meta_learner, n_folds=5)
    stacking_clf.fit(X, y)

    # 交叉验证评估
    scores = cross_val_score(stacking_clf, X, y, cv=5, scoring='accuracy')
    print(f"Stacking CV Accuracy: {scores.mean():.4f} (+/- {scores.std():.4f})")

    # 对比单个模型
    for name, model in base_learners:
    scores = cross_val_score(model, X, y, cv=5, scoring='accuracy')
    print(f"{name}: {scores.mean():.4f}")

    8.2 使用 ML-Ensemble 库(生产环境)

    # pip install mlens
    from mlens.ensemble import SuperLearner
    from sklearn.linear_model import Ridge
    from sklearn.ensemble import RandomForestRegressor
    from xgboost import XGBRegressor
    import numpy as np

    # 构建 Super Learner (Stacking 的一种实现)
    ensemble = SuperLearner(
    folds=5, # 5折交叉验证
    random_state=42,
    verbose=2,
    backend="multiprocessing" # 并行训练
    )

    # 添加第一层(基学习器)
    ensemble.add([
    RandomForestRegressor(n_estimators=100),
    XGBRegressor(n_estimators=100),
    Ridge()
    ])

    # 添加第二层(元学习器)
    ensemble.add_meta(Ridge())

    # 训练与预测
    ensemble.fit(X_train, y_train)
    predictions = ensemble.predict(X_test)

    赞(0)
    未经允许不得转载:171主机测评 » Stacked Generalization 堆叠泛化
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址