混合是一种集成机器学习算法。
它是堆叠泛化或堆叠集成的一种俗称,但它不是在基础模型产生的折外预测上拟合元模型,而是在保留数据集上产生的预测上进行拟合。
在价值 100 万美元的 Netflix 机器学习竞赛中,参赛者将混合技术用于描述结合数百个预测模型的堆叠模型,因此,在 Kaggle 社区等竞争性机器学习圈子中,它仍然是一种流行的技术和堆叠名称。
在本教程中,您将学习如何使用 Python 开发和评估混合集成模型。
完成本教程后,您将了解:
- 混合集成模型是一种堆叠类型,其中元模型是使用保留验证数据集上的预测进行拟合的,而不是使用折外预测。
- 如何开发混合集成模型,包括用于训练模型和对新数据进行预测的函数。
- 如何评估用于分类和回归预测建模问题的混合集成模型。
通过我的新书《使用 Python 的集成学习算法》启动您的项目,其中包括逐步教程和所有示例的 Python 源代码文件。
让我们开始吧。

Python 中的混合集成机器学习
图片由 Nathalie 拍摄,保留部分权利。
教程概述
本教程分为四个部分;它们是
- 混合集成
- 开发混合集成
- 用于分类的混合集成
- 用于回归的混合集成
混合集成
混合是一种集成机器学习技术,它使用机器学习模型来学习如何最好地组合来自多个贡献集成成员模型的预测。
因此,混合与堆叠泛化(广义上的堆叠)相同。通常,混合和堆叠在同一篇论文或模型描述中可互换使用。
许多机器学习从业者通过使用堆叠和相关技术成功地提高了预测精度,超越了任何单个模型所达到的水平。在某些情况下,堆叠也称为混合,我们在此将这些术语互换使用。
— 特征加权线性堆叠,2009 年。
堆叠模型的架构包括两个或更多个基础模型(通常称为第 0 级模型)和一个元模型(用于组合基础模型的预测,称为第 1 级模型)。元模型是在基础模型对样本外数据进行预测的基础上训练的。
- 第 0 级模型(基础模型):在训练数据上拟合的模型,其预测结果被编译。
- 第 1 级模型(元模型):学习如何最佳组合基础模型预测的模型。
然而,混合对于如何构建堆叠集成模型有特定的含义。
混合可能建议开发一个堆叠集成,其中基础模型可以是任何类型的机器学习模型,而元模型是“混合”基础模型预测的线性模型。
例如,当预测数值时,线性回归模型或当预测类别标签时,逻辑回归模型将计算基础模型所做预测的加权和,并被认为是预测的混合。
- 混合集成:在堆叠集成中使用线性模型,例如线性回归或逻辑回归,作为元模型。
在2009年的Netflix奖期间,“混合”是堆叠集成模型的常用术语。该奖项涉及团队寻求比原生Netflix算法表现更好的电影推荐预测,获得10%性能提升的团队将获得100万美元奖金。
我们的 RMSE=0.8643^2 解决方案是 100 多个结果的线性混合。[...] 在方法的描述中,我们强调了参与最终混合解决方案的具体预测器。
— BellKor 2008 Netflix 大奖解决方案,2008 年。
因此,混合是具有堆叠式架构模型的集成学习的俗称。除了与竞争性机器学习相关的文本或学术论文外,它很少(甚至从未)在教科书或学术论文中使用。
最常见的是,混合用于描述堆叠的特定应用,其中元模型是在基础模型对保留验证数据集所做的预测上进行训练的。在这种情况下,堆叠专用于在交叉验证过程中对折外预测进行训练的元模型。
- 混合:元模型在保留数据集上的预测结果上进行训练的堆叠式集成。
- 堆叠:元模型在 k 折交叉验证期间生成的折外预测上进行训练的堆叠式集成。
这种区别在 Kaggle 竞争性机器学习社区中很常见。
混合是Netflix获奖者引入的一个词。它与堆叠泛化非常相似,但更简单,信息泄露的风险更小。[...] 使用混合时,你不是为训练集创建折外预测,而是创建一个小的保留集,例如训练集的 10%。然后,堆叠模型只在这个保留集上进行训练。
— Kaggle 集成指南,MLWave,2015 年。
我们将使用混合的后一种定义。
接下来,我们来看看如何实现混合。
想开始学习集成学习吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
开发混合集成
在撰写本文时,scikit-learn 库不原生支持混合。
相反,我们可以使用 scikit-learn 模型自行实现。
首先,我们需要创建一些基础模型。对于回归或分类问题,它们可以是任何我们喜欢的模型。我们可以定义一个 `get_models()` 函数,它返回一个模型列表,其中每个模型都定义为一个元组,包含名称和配置的分类器或回归对象。
例如,对于分类问题,我们可能使用逻辑回归、kNN、决策树、SVM 和朴素贝叶斯模型。
1 2 3 4 5 6 7 8 9 |
# 获取基础模型列表 定义 获取_模型(): models = list() models.append(('lr', LogisticRegression())) models.append(('knn', KNeighborsClassifier())) models.append(('cart', DecisionTreeClassifier())) models.append(('svm', SVC(probability=True))) models.append(('bayes', GaussianNB())) 返回 models |
接下来,我们需要拟合混合模型。
回想一下,基础模型是在训练数据集上拟合的。元模型是在每个基础模型对保留数据集所做的预测上拟合的。
首先,我们可以枚举模型列表,然后依次在训练数据集上拟合每个模型。在此循环中,我们还可以使用拟合模型对保留(验证)数据集进行预测,并存储预测结果以备后用。
1 2 3 4 5 6 7 8 9 10 11 12 |
... # 在训练集上拟合所有模型并在保留集上进行预测 meta_X = list() for name, model in models: # 在训练集上拟合 model.fit(X_train, y_train) # 在保留集上进行预测 yhat = model.predict(X_val) # 将预测结果重塑为只有一列的矩阵 yhat = yhat.reshape(len(yhat), 1) # 存储预测结果作为混合的输入 meta_X.append(yhat) |
现在我们有了“meta_X”,它代表可以用于训练元模型的输入数据。每个列或特征代表一个基础模型的输出。
每一行代表保留数据集中的一个样本。我们可以使用hstack() 函数来确保此数据集是机器学习模型期望的二维 numpy 数组。
1 2 3 |
... # 从预测结果创建二维数组,每组预测结果都是一个输入特征 meta_X = hstack(meta_X) |
我们现在可以训练我们的元模型。这可以是任何我们喜欢的机器学习模型,例如用于分类的逻辑回归。
1 2 3 4 5 |
... # 定义混合模型 blender = LogisticRegression() # 基于基础模型的预测结果进行拟合 blender.fit(meta_X, y_val) |
我们可以将所有这些内容组合到一个名为 `fit_ensemble()` 的函数中,该函数使用训练数据集和保留验证数据集来训练混合模型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# 拟合混合集成模型 def fit_ensemble(models, X_train, X_val, y_train, y_val): # 在训练集上拟合所有模型并在保留集上进行预测 meta_X = list() for name, model in models: # 在训练集上拟合 model.fit(X_train, y_train) # 在保留集上进行预测 yhat = model.predict(X_val) # 将预测结果重塑为只有一列的矩阵 yhat = yhat.reshape(len(yhat), 1) # 存储预测结果作为混合的输入 meta_X.append(yhat) # 从预测结果创建二维数组,每组预测结果都是一个输入特征 meta_X = hstack(meta_X) # 定义混合模型 blender = LogisticRegression() # 基于基础模型的预测结果进行拟合 blender.fit(meta_X, y_val) return blender |
下一步是使用混合集成模型对新数据进行预测。
这是一个两步过程。第一步是使用每个基础模型进行预测。然后将这些预测收集起来,并作为输入传递给混合模型以进行最终预测。
我们可以使用与训练模型时相同的循环结构。也就是说,我们可以将每个基础模型的预测结果收集到一个训练数据集中,将这些预测结果堆叠起来,然后使用这个元级数据集在混合模型上调用 `predict()`。
下面的 `predict_ensemble()` 函数实现了这一点。给定拟合的基础模型列表、拟合的混合集成模型和一个数据集(例如测试数据集或新数据),它将返回该数据集的一组预测结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# 使用混合集成模型进行预测 def predict_ensemble(models, blender, X_test): # 使用基础模型进行预测 meta_X = list() for name, model in models: # 使用基础模型进行预测 yhat = model.predict(X_test) # 将预测结果重塑为只有一列的矩阵 yhat = yhat.reshape(len(yhat), 1) # 存储预测结果 meta_X.append(yhat) # 从预测结果创建二维数组,每组预测结果都是一个输入特征 meta_X = hstack(meta_X) # 预测 return blender.predict(meta_X) |
现在我们拥有实现分类或回归预测建模问题的混合集成所需的所有元素。
用于分类的混合集成
在本节中,我们将探讨如何在分类问题中使用混合技术。
首先,我们可以使用 `make_classification()` 函数创建一个具有 10,000 个样本和 20 个输入特征的合成二元分类问题。
完整的示例如下所示。
1 2 3 4 5 6 |
# 测试分类数据集 from sklearn.datasets import make_classification # 定义数据集 X, y = make_classification(n_samples=10000, n_features=20, n_informative=15, n_redundant=5, random_state=7) # 汇总数据集 print(X.shape, y.shape) |
运行示例会创建数据集并总结输入和输出组件的形状。
1 |
(10000, 20) (10000,) |
接下来,我们需要分割数据集,首先将其分为训练集和测试集,然后将训练集分为用于训练基础模型的子集和用于训练元模型的子集。
在这种情况下,我们将对训练集和测试集使用 50-50 的划分,然后对训练集和验证集使用 67-33 的划分。
1 2 3 4 5 6 7 |
... # 将数据集分割为训练集和测试集 X_train_full, X_test, y_train_full, y_test = train_test_split(X, y, test_size=0.5, random_state=1) # 将训练集分割为训练集和验证集 X_train, X_val, y_train, y_val = train_test_split(X_train_full, y_train_full, test_size=0.33, random_state=1) # 总结数据分割 print('Train: %s, Val: %s, Test: %s' % (X_train.shape, X_val.shape, X_test.shape)) |
然后,我们可以使用上一节中的 `get_models()` 函数创建集成中使用的分类模型。
然后可以调用 `fit_ensemble()` 函数来在训练集和验证集上拟合混合集成模型,并使用 `predict_ensemble()` 函数对保留集进行预测。
1 2 3 4 5 6 7 |
... # 创建基础模型 模型 = 获取_模型() # 训练混合集成模型 blender = fit_ensemble(models, X_train, X_val, y_train, y_val) # 对测试集进行预测 yhat = predict_ensemble(models, blender, X_test) |
最后,我们可以通过报告测试数据集上的分类准确率来评估混合模型的性能。
1 2 3 4 |
... # 评估预测 score = accuracy_score(y_test, yhat) print('Blending Accuracy: %.3f' % score) |
综合以上所有内容,下面列出了在合成二元分类问题上评估混合集成的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# 使用硬投票进行分类的混合集成 from numpy import hstack from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score from sklearn.linear_model import LogisticRegression from sklearn.neighbors import KNeighborsClassifier from sklearn.tree import DecisionTreeClassifier from sklearn.svm import SVC from sklearn.naive_bayes import GaussianNB # 获取数据集 定义 获取_数据集(): X, y = make_classification(n_samples=10000, n_features=20, n_informative=15, n_redundant=5, random_state=7) 返回 X, y # 获取基础模型列表 定义 获取_模型(): models = list() models.append(('lr', LogisticRegression())) models.append(('knn', KNeighborsClassifier())) models.append(('cart', DecisionTreeClassifier())) models.append(('svm', SVC())) models.append(('bayes', GaussianNB())) 返回 模型 # 拟合混合集成模型 def fit_ensemble(models, X_train, X_val, y_train, y_val): # 在训练集上拟合所有模型并在保留集上进行预测 meta_X = list() for name, model in models: # 在训练集上拟合 model.fit(X_train, y_train) # 在保留集上进行预测 yhat = model.predict(X_val) # 将预测结果重塑为只有一列的矩阵 yhat = yhat.reshape(len(yhat), 1) # 存储预测结果作为混合的输入 meta_X.append(yhat) # 从预测结果创建二维数组,每组预测结果都是一个输入特征 meta_X = hstack(meta_X) # 定义混合模型 blender = LogisticRegression() # 基于基础模型的预测结果进行拟合 blender.fit(meta_X, y_val) return blender # 使用混合集成模型进行预测 def predict_ensemble(models, blender, X_test): # 使用基础模型进行预测 meta_X = list() for name, model in models: # 使用基础模型进行预测 yhat = model.predict(X_test) # 将预测结果重塑为只有一列的矩阵 yhat = yhat.reshape(len(yhat), 1) # 存储预测结果 meta_X.append(yhat) # 从预测结果创建二维数组,每组预测结果都是一个输入特征 meta_X = hstack(meta_X) # 预测 return blender.predict(meta_X) # 定义数据集 X, y = get_dataset() # 将数据集分割为训练集和测试集 X_train_full, X_test, y_train_full, y_test = train_test_split(X, y, test_size=0.5, random_state=1) # 将训练集分割为训练集和验证集 X_train, X_val, y_train, y_val = train_test_split(X_train_full, y_train_full, test_size=0.33, random_state=1) # 总结数据分割 print('Train: %s, Val: %s, Test: %s' % (X_train.shape, X_val.shape, X_test.shape)) # 创建基础模型 模型 = 获取_模型() # 训练混合集成模型 blender = fit_ensemble(models, X_train, X_val, y_train, y_val) # 对测试集进行预测 yhat = predict_ensemble(models, blender, X_test) # 评估预测 score = accuracy_score(y_test, yhat) print('Blending Accuracy: %.3f' % (score*100)) |
运行示例首先报告训练集、验证集和测试集的数据形状,然后报告集成模型在测试集上的准确率。
注意:鉴于算法或评估过程的随机性,或数值精度上的差异,您的结果可能会有所不同。建议多次运行示例并比较平均结果。
在这种情况下,我们可以看到混合集成模型达到了约 97.900% 的分类准确率。
1 2 |
训练集: (3350, 20), 验证集: (1650, 20), 测试集: (5000, 20) 混合准确度:97.900 |
在前面的例子中,清晰的类标签预测是使用混合模型组合的。这是一种硬投票类型。
另一种方法是让每个模型预测类概率,并使用元模型来混合这些概率。这是一种软投票类型,在某些情况下可以带来更好的性能。
首先,我们必须将模型配置为返回概率,例如 SVM 模型。
1 2 3 4 5 6 7 8 9 |
# 获取基础模型列表 定义 获取_模型(): models = list() models.append(('lr', LogisticRegression())) models.append(('knn', KNeighborsClassifier())) models.append(('cart', DecisionTreeClassifier())) models.append(('svm', SVC(probability=True))) models.append(('bayes', GaussianNB())) 返回 models |
接下来,我们必须将基础模型更改为预测概率而不是清晰的类别标签。
这可以通过在 `fit_ensemble()` 函数中拟合基础模型时调用 `predict_proba()` 函数来实现。
1 2 3 4 5 6 7 8 9 10 |
... # 在训练集上拟合所有模型并在保留集上进行预测 meta_X = list() for name, model in models: # 在训练集上拟合 model.fit(X_train, y_train) # 在保留集上进行预测 yhat = model.predict_proba(X_val) # 存储预测结果作为混合的输入 meta_X.append(yhat) |
这意味着用于训练元模型的元数据集每个分类器将有 n 列,其中 n 是预测问题中的类别数量,在我们的例子中是两个。
在使用混合模型对新数据进行预测时,我们还需要更改基础模型的预测结果。
1 2 3 4 5 6 7 8 |
... # 使用基础模型进行预测 meta_X = list() for name, model in models: # 使用基础模型进行预测 yhat = model.predict_proba(X_test) # 存储预测结果 meta_X.append(yhat) |
综合以上所有内容,下面列出了在合成二元分类问题上,使用混合技术对预测类别概率进行处理的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# 使用软投票进行分类的混合集成 from numpy import hstack from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score from sklearn.linear_model import LogisticRegression from sklearn.neighbors import KNeighborsClassifier from sklearn.tree import DecisionTreeClassifier from sklearn.svm import SVC from sklearn.naive_bayes import GaussianNB # 获取数据集 定义 获取_数据集(): X, y = make_classification(n_samples=10000, n_features=20, n_informative=15, n_redundant=5, random_state=7) 返回 X, y # 获取基础模型列表 定义 获取_模型(): models = list() models.append(('lr', LogisticRegression())) models.append(('knn', KNeighborsClassifier())) models.append(('cart', DecisionTreeClassifier())) models.append(('svm', SVC(probability=True))) models.append(('bayes', GaussianNB())) 返回 模型 # 拟合混合集成模型 def fit_ensemble(models, X_train, X_val, y_train, y_val): # 在训练集上拟合所有模型并在保留集上进行预测 meta_X = list() for name, model in models: # 在训练集上拟合 model.fit(X_train, y_train) # 在保留集上进行预测 yhat = model.predict_proba(X_val) # 存储预测结果作为混合的输入 meta_X.append(yhat) # 从预测结果创建二维数组,每组预测结果都是一个输入特征 meta_X = hstack(meta_X) # 定义混合模型 blender = LogisticRegression() # 基于基础模型的预测结果进行拟合 blender.fit(meta_X, y_val) return blender # 使用混合集成模型进行预测 def predict_ensemble(models, blender, X_test): # 使用基础模型进行预测 meta_X = list() for name, model in models: # 使用基础模型进行预测 yhat = model.predict_proba(X_test) # 存储预测结果 meta_X.append(yhat) # 从预测结果创建二维数组,每组预测结果都是一个输入特征 meta_X = hstack(meta_X) # 预测 return blender.predict(meta_X) # 定义数据集 X, y = get_dataset() # 将数据集分割为训练集和测试集 X_train_full, X_test, y_train_full, y_test = train_test_split(X, y, test_size=0.5, random_state=1) # 将训练集分割为训练集和验证集 X_train, X_val, y_train, y_val = train_test_split(X_train_full, y_train_full, test_size=0.33, random_state=1) # 总结数据分割 print('Train: %s, Val: %s, Test: %s' % (X_train.shape, X_val.shape, X_test.shape)) # 创建基础模型 模型 = 获取_模型() # 训练混合集成模型 blender = fit_ensemble(models, X_train, X_val, y_train, y_val) # 对测试集进行预测 yhat = predict_ensemble(models, blender, X_test) # 评估预测 score = accuracy_score(y_test, yhat) print('Blending Accuracy: %.3f' % (score*100)) |
运行示例首先报告训练集、验证集和测试集的数据形状,然后报告集成模型在测试集上的准确率。
注意:鉴于算法或评估过程的随机性,或数值精度上的差异,您的结果可能会有所不同。建议多次运行示例并比较平均结果。
在这种情况下,我们可以看到混合类概率使分类准确率提升到约 98.240%。
1 2 |
训练集: (3350, 20), 验证集: (1650, 20), 测试集: (5000, 20) 混合准确度:98.240 |
混合集成模型只有在性能优于任何单个贡献模型时才有效。
我们可以通过单独评估每个基础模型来证实这一点。每个基础模型都可以在整个训练数据集上拟合(与混合集成模型不同),并在测试数据集上进行评估(与混合集成模型相同)。
下面的示例演示了这一点,单独评估了每个基础模型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# 在整个训练数据集上评估基础模型 from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score from sklearn.linear_model import LogisticRegression from sklearn.neighbors import KNeighborsClassifier from sklearn.tree import DecisionTreeClassifier from sklearn.svm import SVC from sklearn.naive_bayes import GaussianNB # 获取数据集 定义 获取_数据集(): X, y = make_classification(n_samples=10000, n_features=20, n_informative=15, n_redundant=5, random_state=7) 返回 X, y # 获取基础模型列表 定义 获取_模型(): models = list() models.append(('lr', LogisticRegression())) models.append(('knn', KNeighborsClassifier())) models.append(('cart', DecisionTreeClassifier())) models.append(('svm', SVC(probability=True))) models.append(('bayes', GaussianNB())) 返回 模型 # 定义数据集 X, y = get_dataset() # 将数据集分割为训练集和测试集 X_train_full, X_test, y_train_full, y_test = train_test_split(X, y, test_size=0.5, random_state=1) # 总结数据分割 print('Train: %s, Test: %s' % (X_train_full.shape, X_test.shape)) # 创建基础模型 模型 = 获取_模型() # 评估独立模型 for name, model in models: # 在训练数据集上拟合模型 model.fit(X_train_full, y_train_full) # 对测试数据集进行预测 yhat = model.predict(X_test) # 评估预测结果 score = accuracy_score(y_test, yhat) # 报告分数 print('>%s Accuracy: %.3f' % (name, score*100)) |
运行示例首先报告完整训练集和测试集的数据形状,然后报告每个基础模型在测试集上的准确率。
注意:鉴于算法或评估过程的随机性,或数值精度上的差异,您的结果可能会有所不同。建议多次运行示例并比较平均结果。
在这种情况下,我们可以看到所有模型的性能都比混合集成模型差。
有趣的是,我们可以看到 SVM 的准确率非常接近 98.200%,而混合集成模型的准确率达到了 98.240%。
1 2 3 4 5 6 |
训练集: (5000, 20), 测试集: (5000, 20) >lr 准确率: 87.800 >knn 准确率: 97.380 >cart 准确率: 88.200 >svm 准确率: 98.200 >bayes 准确率: 87.300 |
我们可能会选择使用混合集成模型作为我们的最终模型。
这包括在整个训练数据集上拟合集成模型,并对新样本进行预测。具体来说,整个训练数据集被分割成训练集和验证集,分别用于训练基础模型和元模型,然后可以使用集成模型进行预测。
下面列出了使用混合集成模型对新数据进行分类预测的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# 分类混合集成模型对新数据进行预测的示例 from numpy import hstack from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.neighbors import KNeighborsClassifier from sklearn.tree import DecisionTreeClassifier from sklearn.svm import SVC from sklearn.naive_bayes import GaussianNB # 获取数据集 定义 获取_数据集(): X, y = make_classification(n_samples=10000, n_features=20, n_informative=15, n_redundant=5, random_state=7) 返回 X, y # 获取基础模型列表 定义 获取_模型(): models = list() models.append(('lr', LogisticRegression())) models.append(('knn', KNeighborsClassifier())) models.append(('cart', DecisionTreeClassifier())) models.append(('svm', SVC(probability=True))) models.append(('bayes', GaussianNB())) 返回 模型 # 拟合混合集成模型 def fit_ensemble(models, X_train, X_val, y_train, y_val): # 在训练集上拟合所有模型并在保留集上进行预测 meta_X = list() for _, model in models: # 在训练集上拟合 model.fit(X_train, y_train) # 在保留集上进行预测 yhat = model.predict_proba(X_val) # 存储预测结果作为混合的输入 meta_X.append(yhat) # 从预测结果创建二维数组,每组预测结果都是一个输入特征 meta_X = hstack(meta_X) # 定义混合模型 blender = LogisticRegression() # 基于基础模型的预测结果进行拟合 blender.fit(meta_X, y_val) return blender # 使用混合集成模型进行预测 def predict_ensemble(models, blender, X_test): # 使用基础模型进行预测 meta_X = list() for _, model in models: # 使用基础模型进行预测 yhat = model.predict_proba(X_test) # 存储预测结果 meta_X.append(yhat) # 从预测结果创建二维数组,每组预测结果都是一个输入特征 meta_X = hstack(meta_X) # 预测 return blender.predict(meta_X) # 定义数据集 X, y = get_dataset() # 将数据集分割为训练集和验证集 X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.33, random_state=1) # 总结数据分割 print('Train: %s, Val: %s' % (X_train.shape, X_val.shape)) # 创建基础模型 模型 = 获取_模型() # 训练混合集成模型 blender = fit_ensemble(models, X_train, X_val, y_train, y_val) # 对新数据行进行预测 row = [-0.30335011, 2.68066314, 2.07794281, 1.15253537, -2.0583897, -2.51936601, 0.67513028, -3.20651939, -1.60345385, 3.68820714, 0.05370913, 1.35804433, 0.42011397, 1.4732839, 2.89997622, 1.61119399, 7.72630965, -2.84089477, -1.83977415, 1.34381989] yhat = predict_ensemble(models, blender, [row]) # 总结预测 print('Predicted Class: %d' % (yhat)) |
运行示例会在数据集上拟合混合集成模型,然后用于对新的数据行进行预测,这与我们在应用程序中使用模型时可能会做的一样。
1 2 |
训练集: (6700, 20), 验证集: (3300, 20) 预测类别:1 |
接下来,我们探讨如何评估回归问题的混合集成。
用于回归的混合集成
在本节中,我们将探讨如何在回归问题中使用堆叠技术。
首先,我们可以使用 `make_regression()` 函数创建一个具有 10,000 个样本和 20 个输入特征的合成回归问题。
完整的示例如下所示。
1 2 3 4 5 6 |
# 测试回归数据集 from sklearn.datasets import make_regression # 定义数据集 X, y = make_regression(n_samples=10000, n_features=20, n_informative=10, noise=0.3, random_state=7) # 汇总数据集 print(X.shape, y.shape) |
运行示例会创建数据集并总结输入和输出组件的形状。
1 |
(10000, 20) (10000,) |
接下来,我们可以定义用于作为基础模型的回归模型列表。在这种情况下,我们将使用线性回归、kNN、决策树和 SVM 模型。
1 2 3 4 5 6 7 8 |
# 获取基础模型列表 定义 获取_模型(): models = list() models.append(('lr', LinearRegression())) models.append(('knn', KNeighborsRegressor())) models.append(('cart', DecisionTreeRegressor())) models.append(('svm', SVR())) 返回 models |
用于训练混合集成模型的 `fit_ensemble()` 函数与分类问题中的函数相同,只是用于混合的模型必须更改为回归模型。
在这种情况下,我们将使用线性回归模型。
1 2 3 |
... # 定义混合模型 blender = LinearRegression() |
鉴于这是一个回归问题,我们将使用误差度量来评估模型的性能,在本例中,是平均绝对误差,简称 MAE。
1 2 3 4 |
... # 评估预测 score = mean_absolute_error(y_test, yhat) print('Blending MAE: %.3f' % score) |
综合以上所有内容,下面列出了用于合成回归预测建模问题的混合集成的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# 评估回归混合集成 from numpy import hstack from sklearn.datasets import make_regression from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error 来自 sklearn.linear_model 导入 LinearRegression from sklearn.neighbors import KNeighborsRegressor 来自 sklearn.tree 导入 DecisionTreeRegressor from sklearn.svm import SVR # 获取数据集 定义 获取_数据集(): X, y = make_regression(n_samples=10000, n_features=20, n_informative=10, noise=0.3, random_state=7) 返回 X, y # 获取基础模型列表 定义 获取_模型(): models = list() models.append(('lr', LinearRegression())) models.append(('knn', KNeighborsRegressor())) models.append(('cart', DecisionTreeRegressor())) models.append(('svm', SVR())) 返回 模型 # 拟合混合集成模型 def fit_ensemble(models, X_train, X_val, y_train, y_val): # 在训练集上拟合所有模型并在保留集上进行预测 meta_X = list() for name, model in models: # 在训练集上拟合 model.fit(X_train, y_train) # 在保留集上进行预测 yhat = model.predict(X_val) # 将预测结果重塑为只有一列的矩阵 yhat = yhat.reshape(len(yhat), 1) # 存储预测结果作为混合的输入 meta_X.append(yhat) # 从预测结果创建二维数组,每组预测结果都是一个输入特征 meta_X = hstack(meta_X) # 定义混合模型 blender = LinearRegression() # 基于基础模型的预测结果进行拟合 blender.fit(meta_X, y_val) return blender # 使用混合集成模型进行预测 def predict_ensemble(models, blender, X_test): # 使用基础模型进行预测 meta_X = list() for name, model in models: # 使用基础模型进行预测 yhat = model.predict(X_test) # 将预测结果重塑为只有一列的矩阵 yhat = yhat.reshape(len(yhat), 1) # 存储预测结果 meta_X.append(yhat) # 从预测结果创建二维数组,每组预测结果都是一个输入特征 meta_X = hstack(meta_X) # 预测 return blender.predict(meta_X) # 定义数据集 X, y = get_dataset() # 将数据集分割为训练集和测试集 X_train_full, X_test, y_train_full, y_test = train_test_split(X, y, test_size=0.5, random_state=1) # 将训练集分割为训练集和验证集 X_train, X_val, y_train, y_val = train_test_split(X_train_full, y_train_full, test_size=0.33, random_state=1) # 总结数据分割 print('Train: %s, Val: %s, Test: %s' % (X_train.shape, X_val.shape, X_test.shape)) # 创建基础模型 模型 = 获取_模型() # 训练混合集成模型 blender = fit_ensemble(models, X_train, X_val, y_train, y_val) # 对测试集进行预测 yhat = predict_ensemble(models, blender, X_test) # 评估预测 score = mean_absolute_error(y_test, yhat) print('Blending MAE: %.3f' % score) |
运行示例首先报告训练集、验证集和测试集的数据形状,然后报告集成模型在测试集上的 MAE。
注意:鉴于算法或评估过程的随机性,或数值精度上的差异,您的结果可能会有所不同。建议多次运行示例并比较平均结果。
在这种情况下,我们可以看到混合集成模型在测试集上实现了约 0.237 的 MAE。
1 2 |
训练集: (3350, 20), 验证集: (1650, 20), 测试集: (5000, 20) 混合 MAE:0.237 |
与分类一样,混合集成只有在性能优于构成集成的任何基础模型时才有用。
我们可以通过单独评估每个基础模型来检查这一点,首先在整个训练数据集上拟合它(与混合集成不同),然后对测试数据集进行预测(与混合集成相同)。
下面的示例单独评估了合成回归预测建模数据集上的每个基础模型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# 单独评估回归数据集上的基础模型 from numpy import hstack from sklearn.datasets import make_regression from sklearn.model_selection import train_test_split from sklearn.metrics import mean_absolute_error 来自 sklearn.linear_model 导入 LinearRegression from sklearn.neighbors import KNeighborsRegressor 来自 sklearn.tree 导入 DecisionTreeRegressor from sklearn.svm import SVR # 获取数据集 定义 获取_数据集(): X, y = make_regression(n_samples=10000, n_features=20, n_informative=10, noise=0.3, random_state=7) 返回 X, y # 获取基础模型列表 定义 获取_模型(): models = list() models.append(('lr', LinearRegression())) models.append(('knn', KNeighborsRegressor())) models.append(('cart', DecisionTreeRegressor())) models.append(('svm', SVR())) 返回 模型 # 定义数据集 X, y = get_dataset() # 将数据集分割为训练集和测试集 X_train_full, X_test, y_train_full, y_test = train_test_split(X, y, test_size=0.5, random_state=1) # 总结数据分割 print('Train: %s, Test: %s' % (X_train_full.shape, X_test.shape)) # 创建基础模型 模型 = 获取_模型() # 评估独立模型 for name, model in models: # 在训练数据集上拟合模型 model.fit(X_train_full, y_train_full) # 对测试数据集进行预测 yhat = model.predict(X_test) # 评估预测结果 score = mean_absolute_error(y_test, yhat) # 报告分数 print('>%s MAE: %.3f' % (name, score)) |
运行示例首先报告完整训练集和测试集的数据形状,然后报告每个基础模型在测试集上的 MAE。
注意:鉴于算法或评估过程的随机性,或数值精度上的差异,您的结果可能会有所不同。建议多次运行示例并比较平均结果。
在这种情况下,我们可以看到线性回归模型的性能确实略优于混合集成模型,MAE 为 0.236,而集成模型的 MAE 为 0.237。这可能是由于合成数据集的构建方式造成的。
然而,在这种情况下,我们会选择直接在这个问题上使用线性回归模型。这突出了在采用集成模型作为最终模型之前检查贡献模型性能的重要性。
1 2 3 4 5 |
训练集: (5000, 20), 测试集: (5000, 20) >lr MAE:0.236 >knn MAE: 100.169 >cart MAE: 133.744 >svm MAE: 138.195 |
同样,我们可能会选择使用混合集成模型作为我们的最终回归模型。
这包括将整个数据集分割为训练集和验证集,分别用于拟合基础模型和元模型,然后可以使用集成模型对新数据行进行预测。
下面列出了使用混合集成模型对新数据进行回归预测的完整示例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# 回归混合集成模型对新数据进行预测的示例 from numpy import hstack from sklearn.datasets import make_regression from sklearn.model_selection import train_test_split 来自 sklearn.linear_model 导入 LinearRegression from sklearn.neighbors import KNeighborsRegressor 来自 sklearn.tree 导入 DecisionTreeRegressor from sklearn.svm import SVR # 获取数据集 定义 获取_数据集(): X, y = make_regression(n_samples=10000, n_features=20, n_informative=10, noise=0.3, random_state=7) 返回 X, y # 获取基础模型列表 定义 获取_模型(): models = list() models.append(('lr', LinearRegression())) models.append(('knn', KNeighborsRegressor())) models.append(('cart', DecisionTreeRegressor())) models.append(('svm', SVR())) 返回 模型 # 拟合混合集成模型 def fit_ensemble(models, X_train, X_val, y_train, y_val): # 在训练集上拟合所有模型并在保留集上进行预测 meta_X = list() for _, model in models: # 在训练集上拟合 model.fit(X_train, y_train) # 在保留集上进行预测 yhat = model.predict(X_val) # 将预测结果重塑为只有一列的矩阵 yhat = yhat.reshape(len(yhat), 1) # 存储预测结果作为混合的输入 meta_X.append(yhat) # 从预测结果创建二维数组,每组预测结果都是一个输入特征 meta_X = hstack(meta_X) # 定义混合模型 blender = LinearRegression() # 基于基础模型的预测结果进行拟合 blender.fit(meta_X, y_val) return blender # 使用混合集成模型进行预测 def predict_ensemble(models, blender, X_test): # 使用基础模型进行预测 meta_X = list() for _, model in models: # 使用基础模型进行预测 yhat = model.predict(X_test) # 将预测结果重塑为只有一列的矩阵 yhat = yhat.reshape(len(yhat), 1) # 存储预测结果 meta_X.append(yhat) # 从预测结果创建二维数组,每组预测结果都是一个输入特征 meta_X = hstack(meta_X) # 预测 return blender.predict(meta_X) # 定义数据集 X, y = get_dataset() # 将数据集分割为训练集和验证集 X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.33, random_state=1) # 总结数据分割 print('Train: %s, Val: %s' % (X_train.shape, X_val.shape)) # 创建基础模型 模型 = 获取_模型() # 训练混合集成模型 blender = fit_ensemble(models, X_train, X_val, y_train, y_val) # 对新数据行进行预测 row = [-0.24038754, 0.55423865, -0.48979221, 1.56074459, -1.16007611, 1.10049103, 1.18385406, -1.57344162, 0.97862519, -0.03166643, 1.77099821, 1.98645499, 0.86780193, 2.01534177, 2.51509494, -1.04609004, -0.19428148, -0.05967386, -2.67168985, 1.07182911] yhat = predict_ensemble(models, blender, [row]) # 总结预测 print('Predicted: %.3f' % (yhat[0])) |
运行示例会在数据集上拟合混合集成模型,然后用于对新的数据行进行预测,这与我们在应用程序中使用模型时可能会做的一样。
1 2 |
训练集: (6700, 20), 验证集: (3300, 20) 预测值:359.986 |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
相关教程
论文
- 特征加权线性堆叠, 2009.
- 贝尔科尔 2008 年 Netflix 大奖解决方案, 2008.
- Kaggle 集成指南,MLWave, 2015.
文章
总结
在本教程中,您学习了如何使用 Python 开发和评估混合集成模型。
具体来说,你学到了:
- 混合集成模型是一种堆叠类型,其中元模型是使用保留验证数据集上的预测进行拟合的,而不是使用折外预测。
- 如何开发混合集成模型,包括用于训练模型和对新数据进行预测的函数。
- 如何评估用于分类和回归预测建模问题的混合集成模型。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
顺便说一句,您提到 scikit-learn 不原生支持“混合”,这并非严格正确。您可以将任何交叉验证策略传递给 StackingRegressor 或 StackingClassifier,因此您可以轻松地传递 ShuffleSplit CV,并从堆叠分类器或回归器中获得“混合”行为。
…
谢谢。
由于基础模型是在训练数据集上拟合的,并且您需要良好的预测才能进一步进行流程,我认为您必须在 get_models 上设置超参数... 我说的对吗?
抱歉,我不太明白。您能详细说明一下吗?
抱歉。详细说明一下……你的 `get_models` 构建的模型过于简单,根本没有参数。难道不需要用每个模型找到的最佳超参数来填充吗?
我做到了,最终我的所有工作都得到了最好的预测。
你可以随心所欲。
通常,集成中经过高度调优的模型是脆弱的,并且不会产生最佳的整体性能。并非总是如此,但通常如此。
嗨,Jason,感谢您的帖子。
有什么方法可以将交叉验证(如 sickit learn 包中实现的)与混合(如您帖子中实现的)结合使用吗?
对我来说,这并非显而易见,因为训练折叠必须拆分为子训练集和验证集(分别用于训练 0 级模型和拟合混合器)。
谢谢。
也许可以用 for 循环手动枚举折叠来包装整个过程。
好的,这就是我所做的
做得好。抱歉,我没有精力审查/调试代码。
嗨,Jason,
这只是为了分享。我已经测试了代码。我认为它可以工作。主要的一点是将训练折叠分成两部分,一部分用于 0 级模型,另一部分用于混合器(1 级模型)。
谢谢。
解释得很棒。对像我这样的新手非常有帮助。您的所有解释都非常清晰易懂。谢谢
谢谢!
Brownlee 博士,
感谢这篇全面而清晰的帖子!您的帖子对我们总是很有帮助。只想知道一件事。使用混合集成分类器进行嵌套交叉验证是否值得?
不客气。
我认为在混合集成中调整模型不值得,它会使集成模型脆弱,并导致更差的性能。
我们正在使用基础模型在验证集上做出的预测来训练元模型,而这些基础模型又在 X_train 和 y_train 上进行训练。但是,当我们比较元模型与基础模型的性能时,为什么要再次在 X_train_full 和 y_train_full 上训练基础模型呢?这不公平。因为我们是在较少数据上训练的基础模型的结果上训练元模型,而另一方面,我们又在整个训练数据上再次训练基础模型。我希望我能表达清楚。谢谢。
该模型仍在测试集上进行评估,即在训练期间未见过的数据。
好的。谢谢。
不客气。
非常感谢这篇有趣的文章。
如果有人想知道何时选择堆叠(Stacking)优于混合(Blending),反之亦然,我在这里留下了一篇我们测试了不同场景的论文。
“实证研究:用于比较堆叠与混合集成学习的可视化分析”
可通过 IEEEXplore 访问:https://doi.org/10.1109/CSCS52396.2021.00008
感谢分享。
好文章,我在这里的 make_classification 方法中卡住了,您传递的是随机样本,能否传递一个包含 X 和 Y 值的实际数据集?
是的,您可以使用数据集,使用模型的代码是相同的。
先生,感谢您出色的工作,我从您这里学到了很多东西。但是先生,我目前正在处理一个分类问题,发现 xgboost 优于其他最先进的模型。我想问您几个问题
1. 先生,在这种技术中使用 xgboost 可以吗?
2. 在您看来,对于这种混合技术,xgboost 与其他模型的最佳组合是什么?
3. 先生,如何决定应该选择哪个模型作为元分类器和基本分类器?
XGBoost 在 Kaggle 竞赛中以表现出色而闻名。您看到同样的情况我并不感到惊讶,当然,您可以在这种技术中使用它。对于您的其他问题,没有经验法则。您最好用您的数据进行实验,看看哪种方法效果最好。
在混合了3或4个回归模型(我使用pycaret完成)之后,如何获得最终模型(方程)的最终输出,例如系数和截距。
嗨,gyanesh...以下内容可能会让您感兴趣
https://stackoverflow.com/questions/68809169/how-to-obtain-the-coefficient-estimates-in-stackingclassifier
嗨 Jason,感谢您总是提供有用且有趣的文章。
您在评论中提到,调整基础学习器的超参数可能会引入一些脆弱性。那么调整元模型呢?
嗨 Max...非常欢迎您!在堆叠集成模型中调整元模型的超参数确实会引入一些复杂性和潜在的脆弱性,但如果谨慎操作,它也可以带来显著的好处。以下是调整元模型时需要考虑的因素。
### 调整元模型的潜在脆弱性
1. **过拟合**
– 元模型是在基础学习器的预测结果上进行训练的,而这些预测结果本身就是潜在复杂模型的输出。如果元模型过于复杂(例如,使用具有多层的深度神经网络或高度正则化的模型),它可能会对基础学习器的预测结果过拟合,而不是很好地泛化到新数据。
– 如果基础学习器本身过拟合,或者元模型的超参数调整过于激进,这种过拟合可能会更加明显。
2. **对基础学习器性能的依赖**
– 元模型的性能与基础学习器的多样性和质量密切相关。如果基础学习器没有经过良好调整或不够多样化,即使是经过良好调整的元模型也可能无法显著提高整体性能。
– 在不确保基础学习器已良好优化的情况下调整元模型可能会导致糟糕的结果。
3. **复杂性和计算成本**
– 元模型的超参数调整为整个模型训练过程增加了一层额外的复杂性。这会增加计算成本,并使模型更难解释和调试。
– 元模型越复杂,有效调整它就越具挑战性,尤其是在处理大量基础学习器时。
4. **交互效应**
– 元模型的性能取决于它如何有效地组合基础学习器的输出。调整其超参数可能会导致意想不到的交互效应,即元模型的变化对某些基础学习器的影响大于其他基础学习器,从而导致不可预测的结果。
– 确保元模型经过调整以适当平衡基础学习器的贡献可能很棘手。
### 调整元模型的好处
1. **性能提升**
– 经过良好调整的元模型可以显著提高集成的整体性能。它可以通过学习给更强的基础学习器更多权重,并弥补其他学习器的弱点,从而实现更稳健和准确的最终预测。
– 调整元模型的超参数可以帮助它更好地捕获基础学习器输出之间的关系,从而改善泛化能力。
2. **定制化**
– 调整允许您定制元模型,以更好地处理数据集的特定特征。例如,您可以调整正则化参数以防止过拟合,或调整学习率以确保平滑收敛。
– 这种灵活性在简单堆叠可能不足的复杂数据集中尤其有用。
3. **集成策略的优化**
– 通过调整元模型,您可以优化它用于组合基础学习器的策略。例如,您可能希望在特征空间的特定区域优先考虑某些基础学习器,这是经过良好调整的元模型可以实现的。
### 调整元模型的最佳实践
– **交叉验证**:使用交叉验证确保调整过程不会导致过拟合。元模型应在未见过的数据上进行验证,以确保其泛化能力。
– **从简单开始**:从简单的元模型开始,例如线性回归或基本的决策树,然后再转向更复杂的模型,如梯度提升或神经网络。这种方法可以帮助您了解元模型如何与基础学习器交互。
– **顺序调整**:考虑首先调整基础学习器以确保它们表现最佳,然后再专注于元模型。这种逐步方法可以防止调整过程变得过于复杂。
– **正则化**:在元模型中引入正则化技术(如 L1、L2 正则化)以防止过拟合,尤其是在处理大量基础学习器时。
– **超参数搜索**:使用网格搜索、随机搜索或更高级的方法(如贝叶斯优化)来找到元模型的最佳超参数。确保搜索足够广泛,以探索元模型的潜力而不至于过拟合。
总之,虽然调整元模型可能会引入脆弱性,但如果谨慎操作,它也可以带来显著的性能提升。关键在于平衡元模型的复杂性与基础学习器的质量和多样性,确保整个集成模型保持稳健并能很好地泛化到新数据。