模型平均是一种集成技术,其中多个子模型对组合预测做出同等贡献。
可以通过根据子模型的预期性能来加权每个子模型对组合预测的贡献,从而改进模型平均。这可以通过训练一个全新的模型来学习如何最好地组合每个子模型的贡献来进一步扩展。这种方法称为堆叠泛化,简称堆叠,可以产生比任何单个贡献模型更好的预测性能。
在本教程中,您将学习如何为深度学习神经网络开发一个堆叠泛化集成。
完成本教程后,您将了解:
- 堆叠泛化是一种集成方法,其中一个新模型学习如何最好地组合来自多个现有模型的预测。
- 如何使用神经网络作为子模型和 scikit-learn 分类器作为元学习器开发堆叠模型。
- 如何开发一个堆叠模型,其中神经网络子模型嵌入到更大的堆叠集成模型中进行训练和预测。
用我的新书《更好的深度学习》来启动你的项目,书中包含分步教程和所有示例的 Python 源代码文件。
让我们开始吧。
- 2019 年 10 月更新:更新至 Keras 2.3 和 TensorFlow 2.0。
- **2020年1月更新**:已针对 scikit-learn v0.22 API 的变更进行更新。
- 2020 年 8 月更新:针对 Keras 2.4.3 和 TensorFlow 2.3 进行了更新

如何在 Python 中使用 Keras 为深度学习神经网络开发堆叠集成
图片由 David Law 提供,保留部分权利。
教程概述
本教程分为六个部分;它们是:
- 堆叠泛化集成
- 多类别分类问题
- 多层感知器模型
- 训练并保存子模型
- 独立堆叠模型
- 集成堆叠模型
堆叠泛化集成
模型平均集成结合了多个训练模型的预测。
这种方法的局限性在于,每个模型对集成预测的贡献是相同的,无论模型的表现如何。这种方法的一个变体,称为加权平均集成,根据模型在保留数据集上的置信度或预期性能来加权每个集成成员的贡献。这使得表现良好的模型贡献更多,表现不佳的模型贡献更少。加权平均集成改进了模型平均集成。
这种方法的进一步推广是用任何学习算法替代用于组合子模型预测的线性加权和(例如线性回归)模型。这种方法被称为堆叠泛化,简称堆叠。
在堆叠中,算法将子模型的输出作为输入,并尝试学习如何最好地组合输入预测以做出更好的输出预测。
将堆叠过程想象成两个级别可能会有所帮助:级别 0 和级别 1。
- 级别 0:级别 0 数据是训练数据集输入,级别 0 模型学习从这些数据中进行预测。
- 级别 1:级别 1 数据将级别 0 模型的输出作为输入,单个级别 1 模型或元学习器学习从这些数据中进行预测。
堆叠泛化通过推断泛化器相对于提供的学习集的偏差来工作。这种推断通过在第二个空间中进行泛化来完成,该空间的输入是(例如)原始泛化器在学习集的一部分上进行训练并试图猜测其余部分时的猜测,其输出是(例如)正确的猜测。
—— 堆叠泛化,1992 年。
与加权平均集成不同,堆叠泛化集成可以将预测集用作上下文,并有条件地决定以不同方式加权输入预测,从而可能带来更好的性能。
有趣的是,尽管堆叠被描述为具有两个或更多级别 0 模型的集成学习方法,但它也可以在只有一个级别 0 模型的情况下使用。在这种情况下,级别 1 或元学习器模型学习纠正级别 0 模型的预测。
……尽管它也可以在只有一个泛化器的情况下使用,作为改进该单个泛化器的技术
—— 堆叠泛化,1992 年。
重要的是,元学习器在与用于训练级别 0 模型的示例不同的数据集上进行训练,以避免过拟合。
实现这一点的一种简单方法是将训练数据集分成训练集和验证集。然后,级别 0 模型在训练集上进行训练。然后,级别 1 模型使用验证集进行训练,其中原始输入首先通过级别 0 模型以获得用作级别 1 模型输入的预测。
用于训练堆叠模型的保留验证集方法的局限性在于级别 0 和级别 1 模型未在完整数据集上进行训练。
一种更复杂的堆叠模型训练方法涉及使用 k 折交叉验证来为元学习器模型开发训练数据集。每个级别 0 模型都使用 k 折交叉验证(甚至留一法交叉验证以获得最大效果)进行训练;然后模型被丢弃,但预测被保留。这意味着对于每个模型,都有模型版本所做的预测,而该模型版本未在这些示例上进行训练,例如,就像拥有保留示例一样,但在此情况下适用于整个训练数据集。
然后,预测被用作训练元学习器的输入。然后,级别 0 模型在整个训练数据集上进行训练,并与元学习器一起,堆叠模型可用于对新数据进行预测。
在实践中,通常使用不同的算法来准备每个级别 0 模型,以提供多样化的预测集。
……堆叠通常不用于组合相同类型的模型 […] 它应用于由不同学习算法构建的模型。
—— 实用机器学习工具和技术,第二版,2005 年。
使用简单的线性模型来组合预测也很常见。由于使用线性模型很常见,堆叠最近被称为“模型融合”或简称“融合”,尤其是在机器学习竞赛中。
……多响应最小二乘线性回归技术应作为高级泛化器使用。该技术提供了一种组合级别 0 模型置信度的方法
—— 堆叠泛化中的问题,1999 年。
堆叠泛化集成可用于回归和分类问题。在分类问题中,当使用类别概率预测作为元学习器的输入而不是类别标签时,已经观察到更好的结果。
……应该使用类别概率而不是单个预测类别作为更高级别学习的输入属性。类别概率作为预测的置信度度量。
—— 堆叠泛化中的问题,1999 年。
现在我们熟悉了堆叠泛化,我们可以通过一个开发堆叠深度学习模型的案例研究。
想要通过深度学习获得更好的结果吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
多类别分类问题
我们将使用一个小型多类别分类问题作为基础来演示堆叠集成。
scikit-learn 类提供了 make_blobs() 函数,可用于创建具有指定样本数、输入变量、类别和类别内样本方差的多类别分类问题。
该问题有两个输入变量(代表点的 x 和 y 坐标)和每个组内点的标准差为 2.0。我们将使用相同的随机状态(伪随机数生成器的种子)以确保我们始终获得相同的数据点。
1 2 |
# 生成二维分类数据集 X, y = make_blobs(n_samples=1000, centers=3, n_features=2, cluster_std=2, random_state=2) |
结果是我们可以建模的数据集的输入和输出元素。
为了了解问题的复杂性,我们可以将每个点绘制在二维散点图上,并按类别值对每个点进行着色。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# blob 数据集的散点图 from sklearn.datasets import make_blobs from matplotlib import pyplot from pandas import DataFrame # 生成二维分类数据集 X, y = make_blobs(n_samples=1000, centers=3, n_features=2, cluster_std=2, random_state=2) # 散点图,点按类别值着色 df = DataFrame(dict(x=X[:,0], y=X[:,1], label=y)) colors = {0:'red', 1:'blue', 2:'green'} fig, ax = pyplot.subplots() grouped = df.groupby('label') for key, group in grouped: group.plot(ax=ax, kind='scatter', x='x', y='y', label=key, color=colors[key]) pyplot.show() |
运行此示例将生成整个数据集的散点图。我们可以看到,标准差为 2.0 意味着这些类不是线性可分的(不能用一条线分开),导致许多模糊的点。
这是可取的,因为它意味着问题并非微不足道,并且将允许神经网络模型找到许多不同的“足够好”的候选解决方案,从而导致高方差。

具有三个类别且点按类别值着色的 Blob 数据集散点图
多层感知器模型
在定义模型之前,我们需要设计一个适合堆叠集成的问题。
在我们的问题中,训练数据集相对较小。具体来说,训练数据集中的示例与保留数据集中的示例之比为 10:1。这模拟了一种情况,我们可能拥有大量未标记的示例和少量标记的示例,用于训练模型。
我们将从斑点问题中创建 1100 个数据点。模型将在前 100 个点上进行训练,其余 1000 个将保留在测试数据集中,模型无法使用。
该问题是一个多类别分类问题,我们将使用输出层上的 softmax 激活函数对其进行建模。这意味着模型将预测一个包含三个元素的向量,其中包含样本属于三个类别中的每个类别的概率。因此,在将行拆分为训练和测试数据集之前,我们必须对类别值进行一次热编码。我们可以使用 Keras 的 to_categorical() 函数来完成此操作。
1 2 3 4 5 6 7 8 9 |
# 生成二维分类数据集 X, y = make_blobs(n_samples=1100, centers=3, n_features=2, cluster_std=2, random_state=2) # 独热编码输出变量 y = to_categorical(y) # 分割成训练集和测试集 n_train = 100 trainX, testX = X[:n_train, :], X[n_train:, :] trainy, testy = y[:n_train], y[n_train:] print(trainX.shape, testX.shape) |
接下来,我们可以定义和组合模型。
模型将期望具有两个输入变量的样本。然后模型有一个包含 25 个节点和一个修正线性激活函数的单个隐藏层,然后是一个包含三个节点的输出层,用于预测三个类别中每个类别的概率和一个 softmax 激活函数。
由于该问题是多类别问题,我们将使用分类交叉熵损失函数来优化模型,并使用高效的 Adam 风味随机梯度下降。
1 2 3 4 5 |
# 定义模型 model = Sequential() model.add(Dense(25, input_dim=2, activation='relu')) model.add(Dense(3, activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) |
模型将进行 500 个训练周期,我们将在每个周期在测试集上评估模型,将测试集用作验证集。
1 2 |
# 拟合模型 history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, verbose=0) |
运行结束时,我们将评估模型在训练集和测试集上的性能。
1 2 3 4 |
# 评估模型 _, train_acc = model.evaluate(trainX, trainy, verbose=0) _, test_acc = model.evaluate(testX, testy, verbose=0) print('Train: %.3f, Test: %.3f' % (train_acc, test_acc)) |
最后,我们将绘制模型在训练集和验证集上每个训练周期的准确率学习曲线。
1 2 3 4 5 |
# 模型准确率的学习曲线 pyplot.plot(history.history['accuracy'], label='train') pyplot.plot(history.history['val_accuracy'], label='test') pyplot.legend() pyplot.show() |
将所有这些联系在一起,完整的示例如下。
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 |
# 为 blob 数据集开发一个 mlp from sklearn.datasets import make_blobs from keras.utils import to_categorical from keras.models import Sequential from keras.layers import Dense from matplotlib import pyplot # 生成二维分类数据集 X, y = make_blobs(n_samples=1100, centers=3, n_features=2, cluster_std=2, random_state=2) # 独热编码输出变量 y = to_categorical(y) # 分割成训练集和测试集 n_train = 100 trainX, testX = X[:n_train, :], X[n_train:, :] trainy, testy = y[:n_train], y[n_train:] print(trainX.shape, testX.shape) # 定义模型 model = Sequential() model.add(Dense(25, input_dim=2, activation='relu')) model.add(Dense(3, activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) # 拟合模型 history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, verbose=0) # 评估模型 _, train_acc = model.evaluate(trainX, trainy, verbose=0) _, test_acc = model.evaluate(testX, testy, verbose=0) print('Train: %.3f, Test: %.3f' % (train_acc, test_acc)) # 模型准确率的学习曲线 pyplot.plot(history.history['accuracy'], label='train') pyplot.plot(history.history['val_accuracy'], label='test') pyplot.legend() pyplot.show() |
运行示例首先打印每个数据集的形状以进行确认,然后打印最终模型在训练集和测试集上的性能。
注意:由于算法或评估过程的随机性或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
在这种情况下,我们可以看到模型在训练数据集上实现了约 85% 的准确率,我们知道这过于乐观,而在测试数据集上实现了约 80% 的准确率,我们预计这更真实。
1 2 |
(100, 2) (1000, 2) 训练:0.850,测试:0.809 |
还创建了一条线图,显示了模型在训练集和测试集上每个训练周期的准确率学习曲线。
我们可以看到,在大部分运行过程中,训练准确率更加乐观,正如我们对最终分数所注意到的那样。

每个训练周期模型在训练和测试数据集上的准确率学习曲线图
我们现在可以研究将此模型的实例用作堆叠集成的一部分。
训练并保存子模型
为了使本示例保持简单,我们将使用相同模型的多个实例作为堆叠集成中的级别 0 或子模型。
我们还将使用保留验证数据集来训练集成中的级别 1 或元学习器。
更高级的示例可能会使用不同类型的 MLP 模型(更深、更宽等)作为子模型,并使用 k 折交叉验证训练元学习器。
在本节中,我们将训练多个子模型并将它们保存到文件中以供以后在我们的堆叠集成中使用。
第一步是创建一个函数来定义和拟合训练数据集上的 MLP 模型。
1 2 3 4 5 6 7 8 9 10 |
# 在数据集上拟合模型 def fit_model(trainX, trainy): # 定义模型 model = Sequential() model.add(Dense(25, input_dim=2, activation='relu')) model.add(Dense(3, activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) # 拟合模型 model.fit(trainX, trainy, epochs=500, verbose=0) return model |
接下来,我们可以创建一个子目录来存储模型。
注意,如果目录已经存在,您可能需要在重新运行此代码时将其删除。
1 2 |
# 为模型创建目录 makedirs('models') |
最后,我们可以创建 MLP 的多个实例,并将每个实例保存到“models/”子目录中,并使用唯一的文件名。
在这种情况下,我们将创建五个子模型,但您可以尝试不同数量的模型,看看它如何影响模型性能。
1 2 3 4 5 6 7 8 9 |
# 拟合并保存模型 n_members = 5 for i in range(n_members): # 拟合模型 model = fit_model(trainX, trainy) # 保存模型 filename = 'models/model_' + str(i + 1) + '.h5' model.save(filename) print('>Saved %s' % filename) |
我们可以将所有这些元素捆绑在一起;下面列出了训练子模型并将其保存到文件的完整示例。
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 |
# 保存子模型以供以后在堆叠集成中使用的示例 from sklearn.datasets import make_blobs from keras.utils import to_categorical from keras.models import Sequential from keras.layers import Dense from matplotlib import pyplot from os import makedirs # 在数据集上拟合模型 def fit_model(trainX, trainy): # 定义模型 model = Sequential() model.add(Dense(25, input_dim=2, activation='relu')) model.add(Dense(3, activation='softmax')) model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) # 拟合模型 model.fit(trainX, trainy, epochs=500, verbose=0) return model # 生成二维分类数据集 X, y = make_blobs(n_samples=1100, centers=3, n_features=2, cluster_std=2, random_state=2) # 独热编码输出变量 y = to_categorical(y) # 分割成训练集和测试集 n_train = 100 trainX, testX = X[:n_train, :], X[n_train:, :] trainy, testy = y[:n_train], y[n_train:] print(trainX.shape, testX.shape) # 为模型创建目录 makedirs('models') # 拟合并保存模型 n_members = 5 for i in range(n_members): # 拟合模型 model = fit_model(trainX, trainy) # 保存模型 filename = 'models/model_' + str(i + 1) + '.h5' model.save(filename) print('>Saved %s' % filename) |
运行示例将创建“models/”子文件夹并保存五个训练好的模型,每个模型都有一个唯一的文件名。
1 2 3 4 5 6 |
(100, 2) (1000, 2) >已保存 models/model_1.h5 >已保存 models/model_2.h5 >已保存 models/model_3.h5 >已保存 models/model_4.h5 >已保存 models/model_5.h5 |
接下来,我们可以研究训练一个元学习器,以充分利用这些子模型的预测。
独立堆叠模型
我们现在可以训练一个元学习器,它将最好地组合来自子模型的预测,并理想地比任何单个子模型表现得更好。
第一步是加载保存的模型。
我们可以使用 load_model() Keras 函数并创建一个加载模型的 Python 列表。
1 2 3 4 5 6 7 8 9 10 11 12 |
# 从文件加载模型 def load_all_models(n_models): all_models = list() for i in range(n_models): # 为此集成模型定义文件名 filename = 'models/model_' + str(i + 1) + '.h5' # 从文件加载模型 model = load_model(filename) # 添加到成员列表 all_models.append(model) print('>已加载 %s' % filename) return all_models |
我们可以调用此函数从“models/”子目录加载我们保存的五个模型。
1 2 3 4 |
# 加载所有模型 n_members = 5 members = load_all_models(n_members) print('已加载 %d 个模型' % len(members)) |
了解单个模型在测试数据集上的表现会很有用,因为我们期望堆叠模型表现更好。
我们可以轻松评估训练数据集上的每个单个模型并建立性能基线。
1 2 3 4 5 |
# 评估测试数据集上的独立模型 for model in members: testy_enc = to_categorical(testy) _, acc = model.evaluate(testX, testy_enc, verbose=0) print('模型准确率: %.3f' % acc) |
接下来,我们可以训练我们的元学习器。这需要两个步骤:
- 为元学习器准备训练数据集。
- 使用准备好的训练数据集来拟合元学习器模型。
我们将通过向每个子模型提供测试集中的示例并收集预测来为元学习器准备训练数据集。在这种情况下,每个模型将为每个示例输出三个预测,表示给定示例属于三个类别中每个类别的概率。因此,测试集中的 1,000 个示例将产生五个形状为 [1000, 3] 的数组。
我们可以使用 dstack() NumPy 函数 将这些数组组合成一个形状为 [1000, 5, 3] 的三维数组,该函数将堆叠每组新预测。
作为新模型的输入,我们将需要 1,000 个具有一些特征的示例。鉴于我们有五个模型,每个模型为每个示例进行三个预测,那么我们将为提供给子模型的每个示例提供 15 个 (3 x 5) 特征。我们可以使用 reshape() NumPy 函数 并展平最后两个维度,将子模型的 [1000, 5, 3] 形预测转换为 [1000, 15] 形数组,用于训练元学习器。stacked_dataset() 函数实现了这一步骤。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# 创建堆叠模型输入数据集作为集成模型的输出 def stacked_dataset(members, inputX): stackX = None for model in members: # 进行预测 yhat = model.predict(inputX, verbose=0) # 将预测堆叠成 [行, 成员, 概率] if stackX is None: stackX = yhat else: stackX = dstack((stackX, yhat)) # 将预测展平为 [行, 成员 x 概率] stackX = stackX.reshape((stackX.shape[0], stackX.shape[1]*stackX.shape[2])) return stackX |
一旦准备好,我们就可以使用这个输入数据集以及测试集的输出(或 y 部分)来训练一个新的元学习器。
在这种情况下,我们将训练一个来自 scikit-learn 库的简单逻辑回归算法。
逻辑回归只支持二元分类,尽管 scikit-learn 中 LogisticRegression 类的逻辑回归实现使用一对多方案支持多类分类(超过两个类别)。下面的函数 fit_stacked_model() 将通过调用 stacked_dataset() 函数为元学习器准备训练数据集,然后拟合一个逻辑回归模型并返回。
1 2 3 4 5 6 7 8 |
# 基于集成成员的输出拟合模型 def fit_stacked_model(members, inputX, inputy): # 使用集成模型创建数据集 stackedX = stacked_dataset(members, inputX) # 拟合独立模型 model = LogisticRegression() model.fit(stackedX, inputy) return model |
我们可以调用此函数并传入已加载的模型列表和训练数据集。
1 2 |
# 使用集成模型拟合堆叠模型 model = fit_stacked_model(members, testX, testy) |
一旦拟合,我们可以使用堆叠模型,包括成员和元学习器,对新数据进行预测。
这可以通过首先使用子模型为元学习器创建输入数据集(例如,通过调用 stacked_dataset() 函数),然后使用元学习器进行预测来实现。下面的 stacked_prediction() 函数实现了这一点。
1 2 3 4 5 6 7 |
# 使用堆叠模型进行预测 def stacked_prediction(members, model, inputX): # 使用集成模型创建数据集 stackedX = stacked_dataset(members, inputX) # 进行预测 yhat = model.predict(stackedX) return yhat |
我们可以使用此函数对新数据进行预测;在这种情况下,我们可以通过对测试集进行预测来演示它。
1 2 3 4 |
# 在测试集上评估模型 yhat = stacked_prediction(members, model, testX) acc = accuracy_score(testy, yhat) print('堆叠测试准确率: %.3f' % acc) |
将所有这些元素结合在一起,下面列出了 MLP 子模型堆叠集成的线性元学习器拟合的完整示例。
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 |
# 在 blob 数据集上使用线性元模型进行堆叠泛化 from sklearn.datasets import make_blobs from sklearn.metrics import accuracy_score from sklearn.linear_model import LogisticRegression from keras.models import load_model from keras.utils import to_categorical from numpy import dstack # 从文件加载模型 def load_all_models(n_models): all_models = list() for i in range(n_models): # 为此集成模型定义文件名 filename = 'models/model_' + str(i + 1) + '.h5' # 从文件加载模型 model = load_model(filename) # 添加到成员列表 all_models.append(model) print('>已加载 %s' % filename) return all_models # 创建堆叠模型输入数据集作为集成模型的输出 def stacked_dataset(members, inputX): stackX = None for model in members: # 进行预测 yhat = model.predict(inputX, verbose=0) # 将预测堆叠成 [行, 成员, 概率] if stackX is None: stackX = yhat else: stackX = dstack((stackX, yhat)) # 将预测展平为 [行, 成员 x 概率] stackX = stackX.reshape((stackX.shape[0], stackX.shape[1]*stackX.shape[2])) return stackX # 基于集成成员的输出拟合模型 def fit_stacked_model(members, inputX, inputy): # 使用集成模型创建数据集 stackedX = stacked_dataset(members, inputX) # 拟合独立模型 model = LogisticRegression() model.fit(stackedX, inputy) return model # 使用堆叠模型进行预测 def stacked_prediction(members, model, inputX): # 使用集成模型创建数据集 stackedX = stacked_dataset(members, inputX) # 进行预测 yhat = model.predict(stackedX) return yhat # 生成二维分类数据集 X, y = make_blobs(n_samples=1100, centers=3, n_features=2, cluster_std=2, random_state=2) # 分割成训练集和测试集 n_train = 100 trainX, testX = X[:n_train, :], X[n_train:, :] trainy, testy = y[:n_train], y[n_train:] print(trainX.shape, testX.shape) # 加载所有模型 n_members = 5 members = load_all_models(n_members) print('已加载 %d 个模型' % len(members)) # 评估测试数据集上的独立模型 for model in members: testy_enc = to_categorical(testy) _, acc = model.evaluate(testX, testy_enc, verbose=0) print('模型准确率: %.3f' % acc) # 使用集成模型拟合堆叠模型 model = fit_stacked_model(members, testX, testy) # 在测试集上评估模型 yhat = stacked_prediction(members, model, testX) acc = accuracy_score(testy, yhat) print('堆叠测试准确率: %.3f' % acc) |
运行示例首先将子模型加载到列表中并评估每个模型的性能。
注意:由于算法或评估过程的随机性或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
我们可以看到,性能最好的模型是最终模型,准确率约为 81.3%。
1 2 3 4 5 6 7 8 9 10 11 12 |
(100, 2) (1000, 2) >已加载 models/model_1.h5 >已加载 models/model_2.h5 >已加载 models/model_3.h5 >已加载 models/model_4.h5 >已加载 models/model_5.h5 已加载 5 个模型 模型准确率: 0.805 模型准确率: 0.806 模型准确率: 0.804 模型准确率: 0.809 模型准确率: 0.813 |
接下来,在测试集上从每个子模型的预测概率中训练一个逻辑回归元学习器,然后评估整个堆叠模型在测试集上的性能。
我们可以看到,在这种情况下,元学习器在测试集上优于每个子模型,准确率约为 82.4%。
1 |
堆叠测试准确率: 0.824 |
集成堆叠模型
当使用神经网络作为子模型时,可能需要使用神经网络作为元学习器。
具体来说,子网络可以嵌入到更大的多头神经网络中,然后学习如何最好地组合来自每个输入子模型的预测。这使得堆叠集成模型可以被视为一个单一的大型模型。
这种方法的优点是子模型的输出直接提供给元学习器。此外,如果需要,还可以将子模型的权重与元学习器模型一起更新。
这可以使用 Keras 函数式 API 来开发模型来实现。
模型作为列表加载后,可以定义一个更大的堆叠集成模型,其中每个已加载的模型都用作该新模型的独立输入头。这意味着任何输入数据都必须提供 k 份给模型,其中 k 是输入模型的数量,在本例中为 5。Keras 还要求每个层都具有唯一的名称,因此每个已加载模型中每个层的名称都必须更新,以指示它们属于哪个集成成员。
1 2 3 4 5 6 7 8 |
# 更新所有模型中的所有层,使其不可训练 for i in range(len(members)): model = members[i] for layer in model.layers: # 设置为不可训练 layer.trainable = False # 重命名以避免“唯一层名称”问题 layer._name = 'ensemble_' + str(i+1) + '_' + layer.name |
子模型准备好后,我们可以定义堆叠集成模型。
每个子模型的输入层将用作此新模型的独立输入头。这意味着任何输入数据都必须提供 k 份给模型,其中 k 是输入模型的数量,在本例中为 5。
然后可以合并每个模型的输出。在这种情况下,我们将使用简单的连接合并,从 5 个模型中的每个模型预测的三个类概率中创建一个 15 元素向量。
然后,我们将定义一个隐藏层来解释元学习器的此“输入”,以及一个将进行其自身概率预测的输出层。下面的 define_stacked_model() 函数实现了这一点,并将在给定训练好的子模型列表的情况下返回一个堆叠泛化神经网络模型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# 从多个成员输入模型定义堆叠模型 def define_stacked_model(members): # 更新所有模型中的所有层,使其不可训练 for i in range(len(members)): model = members[i] for layer in model.layers: # 设置为不可训练 layer.trainable = False # 重命名以避免“唯一层名称”问题 layer._name = 'ensemble_' + str(i+1) + '_' + layer.name # 定义多头输入 ensemble_visible = [model.input for model in members] # 连接合并每个模型的输出 ensemble_outputs = [model.output for model in members] merge = concatenate(ensemble_outputs) hidden = Dense(10, activation='relu')(merge) output = Dense(3, activation='softmax')(hidden) model = Model(inputs=ensemble_visible, outputs=output) # 绘制集成图 plot_model(model, show_shapes=True, to_file='model_graph.png') # 编译 model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) return model |
调用此函数时会创建一个网络图,以了解集成模型是如何组合的。
1 2 |
# 定义集成模型 stacked_model = define_stacked_model(members) |
创建图需要安装 pygraphviz。
如果这在您的工作站上是一个挑战,您可以注释掉对 plot_model() 函数的调用。

神经网络模型堆叠泛化集成模型可视化
模型定义后,就可以进行拟合。我们可以直接在保留的测试数据集上拟合它。
由于子模型不可训练,它们的权重在训练期间不会更新,只会更新新的隐藏层和输出层的权重。下面的 fit_stacked_model() 函数将拟合堆叠神经网络模型 300 个 epoch。
1 2 3 4 5 6 7 8 |
# 拟合堆叠模型 def fit_stacked_model(model, inputX, inputy): # 准备输入数据 X = [inputX for _ in range(len(model.input))] # 编码输出数据 inputy_enc = to_categorical(inputy) # 拟合模型 model.fit(X, inputy_enc, epochs=300, verbose=0) |
我们可以调用此函数,提供定义的堆叠模型和测试数据集。
1 2 |
# 在测试数据集上拟合堆叠模型 fit_stacked_model(stacked_model, testX, testy) |
拟合后,我们可以使用新的堆叠模型对新数据进行预测。
这就像在模型上调用 predict() 函数一样简单。一个小的更改是,我们需要以列表形式提供 k 份输入数据给模型,以用于 k 个子模型。下面的 predict_stacked_model() 函数简化了使用堆叠模型进行预测的过程。
1 2 3 4 5 6 |
# 使用堆叠模型进行预测 def predict_stacked_model(model, inputX): # 准备输入数据 X = [inputX for _ in range(len(model.input))] # 进行预测 return model.predict(X, verbose=0) |
我们可以调用此函数对测试数据集进行预测并报告准确率。
我们期望神经网络学习器的性能优于任何单个子模型,并且可能与上一节中使用的线性元学习器具有竞争力。
1 2 3 4 5 |
# 进行预测和评估 yhat = predict_stacked_model(stacked_model, testX) yhat = argmax(yhat, axis=1) acc = accuracy_score(testy, yhat) print('堆叠测试准确率: %.3f' % acc) |
将所有这些元素结合在一起,完整的示例如下所示。
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 81 82 83 84 85 |
# 在 blob 数据集上使用神经网络元模型进行堆叠泛化 from sklearn.datasets import make_blobs from sklearn.metrics import accuracy_score from keras.models import load_model from keras.utils import to_categorical from keras.utils import plot_model from keras.models import Model from keras.layers import Input from keras.layers import Dense from keras.layers.merge import concatenate from numpy import argmax # 从文件加载模型 def load_all_models(n_models): all_models = list() for i in range(n_models): # 为此集成模型定义文件名 filename = 'models/model_' + str(i + 1) + '.h5' # 从文件加载模型 model = load_model(filename) # 添加到成员列表 all_models.append(model) print('>已加载 %s' % filename) return all_models # 从多个成员输入模型定义堆叠模型 def define_stacked_model(members): # 更新所有模型中的所有层,使其不可训练 for i in range(len(members)): model = members[i] for layer in model.layers: # 设置为不可训练 layer.trainable = False # 重命名以避免“唯一层名称”问题 layer._name = 'ensemble_' + str(i+1) + '_' + layer.name # 定义多头输入 ensemble_visible = [model.input for model in members] # 连接合并每个模型的输出 ensemble_outputs = [model.output for model in members] merge = concatenate(ensemble_outputs) hidden = Dense(10, activation='relu')(merge) output = Dense(3, activation='softmax')(hidden) model = Model(inputs=ensemble_visible, outputs=output) # 绘制集成图 plot_model(model, show_shapes=True, to_file='model_graph.png') # 编译 model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy']) return model # 拟合堆叠模型 def fit_stacked_model(model, inputX, inputy): # 准备输入数据 X = [inputX for _ in range(len(model.input))] # 编码输出数据 inputy_enc = to_categorical(inputy) # 拟合模型 model.fit(X, inputy_enc, epochs=300, verbose=0) # 使用堆叠模型进行预测 def predict_stacked_model(model, inputX): # 准备输入数据 X = [inputX for _ in range(len(model.input))] # 进行预测 return model.predict(X, verbose=0) # 生成二维分类数据集 X, y = make_blobs(n_samples=1100, centers=3, n_features=2, cluster_std=2, random_state=2) # 分割成训练集和测试集 n_train = 100 trainX, testX = X[:n_train, :], X[n_train:, :] trainy, testy = y[:n_train], y[n_train:] print(trainX.shape, testX.shape) # 加载所有模型 n_members = 5 members = load_all_models(n_members) print('已加载 %d 个模型' % len(members)) # 定义集成模型 stacked_model = define_stacked_model(members) # 在测试数据集上拟合堆叠模型 fit_stacked_model(stacked_model, testX, testy) # 进行预测和评估 yhat = predict_stacked_model(stacked_model, testX) yhat = argmax(yhat, axis=1) acc = accuracy_score(testy, yhat) print('堆叠测试准确率: %.3f' % acc) |
运行示例首先加载五个子模型。
注意:由于算法或评估过程的随机性或数值精度的差异,您的结果可能会有所不同。考虑多次运行示例并比较平均结果。
定义一个更大的堆叠集成神经网络并在测试数据集上拟合,然后使用新模型在测试数据集上进行预测。我们可以看到,在这种情况下,模型实现了约 83.3% 的准确率,优于上一节中的线性模型。
1 2 3 4 5 6 7 8 |
(100, 2) (1000, 2) >已加载 models/model_1.h5 >已加载 models/model_2.h5 >已加载 models/model_3.h5 >已加载 models/model_4.h5 >已加载 models/model_5.h5 已加载 5 个模型 堆叠测试准确率: 0.833 |
扩展
本节列出了一些您可能希望探索的扩展本教程的想法。
- 替换元学习器。更新示例以使用替代元学习器分类模型来代替逻辑回归模型。
- 单个 0 级模型。更新示例以使用单个 0 级模型并比较结果。
- 改变 0 级模型。开发一项研究,演示测试分类准确率与堆叠集成中使用的子模型数量之间的关系。
- 交叉验证堆叠集成。更新示例以使用 k 折交叉验证为元学习器模型准备训练数据集。
- 在元学习器中使用原始输入。更新示例,使元学习器算法接受样本的原始输入数据以及子模型的输出,并比较性能。
如果您探索了这些扩展中的任何一个,我很想知道。
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
书籍
- 第 8.8 节 模型平均和堆叠,统计学习要素:数据挖掘、推理和预测,第二版,2016。
- 第 7.5 节 组合多个模型,数据挖掘:实用机器学习工具和技术,第二版,2005。
- 第 9.8.2 节 堆叠泛化,用于模式识别的神经网络,1995。
论文
API
- Keras 顺序模型入门
- Keras核心层API
- numpy.argmax API
- sklearn.datasets.make_blobs API
- numpy.dstack API
- sklearn.linear_model.LogisticRegression API
文章
文章
总结
在本教程中,您将学习如何为深度学习神经网络开发堆叠泛化集成模型。
具体来说,你学到了:
- 堆叠泛化是一种集成方法,其中一个新模型学习如何最佳地组合来自多个现有模型的预测结果。
- 如何使用神经网络作为子模型和 scikit-learn 分类器作为元学习器开发堆叠模型。
- 如何开发一个堆叠模型,其中神经网络子模型嵌入到更大的堆叠集成模型中进行训练和预测。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
嗨,Jason,
很棒的帖子。我尝试为二分类任务执行堆叠集成。但是,我的 stacked_dataset 定义有问题。它给出了一个值错误,显示“bad_shape (200, 2)”。我有 200 个测试样本,分为 2 类。请建议 stacked_dataset 定义所需的修改。非常感谢。
您可能需要根据您的特定模型和数据集调整示例。
你好,Sivarama,
我也遇到了同样的问题。问题在于我将独热编码列表 (testy_enc) 设置给了 fit_stacked_model 函数,该函数稍后调用 stacked_dataset,但它应该是二进制列表 (testy)。
Raniery
谢谢你
不客气。
Tableau
它怎么样?
既然您使用了多个高度相同的模型,是否可以使用不同的子模型,例如不同的层或不同的类型,VGG、Inception 等作为子模型?
另一个问题是,如果我们对子模型之间的数据集进行洗牌,堆叠集成模型过拟合的可能性有多大?
是的,您可以使用不同深度的模型。
在单独数据上拟合每个模型将不再是堆叠模型,而是袋装类型模型。
嗨,Jason,
感谢您的教程。非常有帮助!
一个问题:是否可以使用相同的算法但使用不同的数据类型来堆叠模型(即,0 级模型是两个逻辑回归模型——一个使用数值数据,一个使用文本,并且两者的输出被堆叠到一个 1 级逻辑回归模型中?)
谢谢你,
是的!
谢谢杰森。尽管我需要消化您所写的一切,因为我是这个领域的新手,但我感谢您分享知识的努力。
谢谢。坚持下去!
谢谢你,Jason。
我也感谢您分享知识的努力。
谢谢。
感谢您提供这篇文章,杰森。我尝试了扩展“在元学习器中使用原始输入”。我的理解是您获取 0 级模型的输出,然后将其与用于生成 0 级模型输出(预测)的数据连接起来。因此,元模型的输入将是 (n, X+Y),其中 n 是验证集中的观测值数量,X 是原始数据中的特征数量,Y 是 0 级模型的数量(对于二分类问题)。我的直觉是这会过度拟合,而且根据元模型的性能来看,它似乎确实如此。元模型难道不会从原始数据中获取“信息”两次吗?一次通过 0 级模型间接获取,然后再次通过原始数据获取?或者这是合法的,因为提供给元模型的“原始数据”仅用于验证,而不是训练 0 级模型?
再次感谢您的文章(以及所有其他文章!)我发现它们非常有用。
输入将是 0 级模型的输入 (X) 和每个 0 级模型的输出 (yhats)。
原始输入 (X) 为 0 级输出 (yhats) 提供了额外的上下文。
它可能会或可能不会过拟合。通常它会导致更好的性能。
嗨,Jason,
感谢您提供这篇有趣的帖子。我对基础学习器和元学习器之间的准确率分数有一个快速问题。
对于基础学习器,您使用 (trainX, trainY) 进行训练,并在 (testX, testY) 上评估准确率。
对于元学习器,您使用 (testX, testY) 进行训练,并再次在 (testX, testY) 上评估准确率。元学习器的准确率不会因此而虚高吗?拥有一个基础学习器和元学习器都未曾见过的“真正的保留集”会不会更好?
谢谢
是的,那将是一个很好的改进。
谢谢您,先生,您能推荐一些使用 MATLAB 的机器学习算法吗,特别是关于再现希尔伯特核空间和核技巧的实现。
抱歉,我没有 MATLAB 示例。
确实是一篇很棒的帖子。我不太完全理解的是,为什么测试数据被用来拟合堆叠模型,然后堆叠模型又用测试数据进行评估?这不是让集成模型过拟合吗?
我遇到的另一个错误是,当我加载模型时,其中一些模型具有相同的层名称。但是,重命名功能不适用于输入层,因此“Model(inputs=ensemble_inputs...)”代码会抛出错误,提示输入层名称中存在重复项。
是的,这就是巧妙之处。每一轮都使用不同的保留集来寻找组合模型的系数——所有数据在系数估计中都是未见的。理想情况下,它不应该过拟合。
也许再检查一下您的 Keras 版本是否是最新版本。
只是想澄清您所说的“每一轮”是什么意思,就像在最后一个示例中,神经网络用作元学习器,没有部署交叉验证。我的理解是子模型被堆叠在一起,并且训练一次完成。
我正在使用最新的 Keras 2.2.4 版本。
在这种情况下,我们加载预训练模型并使用“新”数据集(未用于训练子模型)拟合数据,例如,在这种情况下是测试数据集。
理想情况下,我们不会使用测试数据集,而是使用训练数据集的一个子集,该子集未被子模型看到。
同意。那么在上面的例子中,堆叠模型在测试数据集上训练,然后又用测试数据集进行评估,这不理想,并且可能过度拟合测试数据集吗?
是的,没错。
感谢这篇很棒的教程。正如 Oliver 问你的,我不明白为什么你用相同的数据集 (testX) 来拟合和预测元模型。假设我有训练数据集和测试数据集,并且我只有训练数据集的标签,没有测试数据集的标签。所以我不能用测试数据集来拟合,只能用它来预测。那么我应该划分训练数据集并使用未见过的部分来拟合元模型吗?
谢谢,
为简洁起见,理想情况下您应该在验证数据集上拟合元模型。
在第一组(训练集)上训练基础模型,然后在另一组(验证集)上训练元模型,最后在另一组(测试集)上评估。
那么您能否在帖子中澄清一下,因为它会误导读者。在这种情况下,交叉验证不应该是可选的,而是生成元学习器输入的必需步骤。如果训练集中有 N 行和 L 个子模型,则交叉验证应首先生成一个 N * L 矩阵,以及 y_train 来训练元学习器。
这意味着需要在“fit_stacked_model”函数中实现交叉验证,并输入 trainX 和 trainY。
感谢您的建议。
我的荣幸。感谢您的精彩帖子。继续努力。
嗨,Jason,
感谢您的精彩帖子!我想用它来堆叠我自己的神经网络。它们也是序列模型——但我遇到了以下问题:两个输入张量具有相同的名称。由于张量无法更改名称,因此您提供的更改层名称的代码不会更改它们。因此,我收到错误
ValueError: 名称“dense_7_input”在模型中使用了 2 次。所有层名称都应该是唯一的。
我尝试了很多方法——但都没有成功。然而,我并不真正关心实际的模型,我可以重新训练它们并在设置时更改输入名称。但我找不到任何方法可以为在序列模型的输入层中创建的输入张量提供自定义名称。
不过,我无法在一个循环中创建所有五个模型(是的,我碰巧也有五个模型要连接),因为训练其中一个模型需要几个小时。您有什么建议可以找到解决方法吗?我将不胜感激。
此致,再次感谢,
Wolfgang
您可以在创建图层时通过 name 属性指定图层的名称。
您也可以在加载层之后更改其 name 属性,然后再次保存。
嗨,Jason,
非常感谢您的帮助。我无法更改属性的名称——但我以不同的方式解决了。如果我保存每个模型的权重,从头开始设置一个新模型(明确命名第一层,即不在循环中重命名层),然后将权重添加到重新加载的模型中。它起作用了,我正在测试它。再次感谢,此致,Wolfgang
不错的变通方法,感谢分享。
嗨 Jason
感谢您的评论——抱歉再次打扰您。我还有另一个问题,我想知道您是否有什么“高阶”建议,关于我可以在哪里查找如何操作。
对于我的项目(在计算机视觉领域),我有五个前馈网络,它们具有从图像中提取的特征——以及一个(稍后可能是两三个)CNN。
我可以连接这些前馈网络,并且整体准确率,基本上使用您的博客代码,比我之前结合每个前馈网络输出的方法提高了约 1.5%。以前,我平均了 softmax 概率(有关更多详细信息,请参阅 Geoffrey Hinton 在 Coursera 上的课程,第 10 讲),通过在验证集的保存的 softmax 输出上使用暴力方法找到每个网络的最佳权重,这样模型就不会在测试集上进行优化。
然而,如果我将堆叠模型的 softmax 输出概率预测与我的 CNN softmax 输出概率平均,整体准确率与我之前的方法相比下降了约 2%(即使堆叠的前馈准确率比以前更好)。除了我想知道为什么会这样之外,我还在想也许我可以将 CNN 添加到堆叠模型中,看看是否能提高整体准确率。
问题是:对于 CNN,我必须(由于内存原因)使用数据生成器——我不知道如何使用它来设置堆叠模型。
您是否知道堆叠 CNN 和一些前馈网络,其中第一个使用数据生成器而其他不使用,是否可能?如果可能——您是否有参考页面、博客文章或类似内容,我可以在其中查找?
我的第二个问题与此相关:我是否可以使用每个网络保存的输出概率来设置堆叠神经网络(因为这可以避免数据生成器问题)。
再次感谢您的善意帮助,
此致,
Wolfgang
嗯,如果每个模型都接受相同的输入,那么每个模型都可以使用相同的数据生成器。
否则,您可能需要编写自定义代码来逐个样本生成样本并将其馈送到您的模型中——我猜。可能需要一些原型设计。
是的,您可以使用保存的样本外预测来拟合堆叠模型或多个模型预测的权重,例如:
https://machinelearning.org.cn/weighted-average-ensemble-for-deep-learning-neural-networks/
感谢这篇文章!
即使我像您上面那样为每个 0 级模型设置了新的层名称,我仍然收到“唯一层名称”错误消息。我几乎没有更改您的代码。
错误消息是这样的
—————————————-
ValueError: 名称“dense_1_input”在模型中使用了 5 次。所有层名称都应该是唯一的。
—————————————-
但是当我尝试查找具有该名称的层时,没有一个层具有该名称
—————————————-
model.get_layer("dense_1_input")
>>> ValueError: 没有这样的层:dense_1_input
—————————————-
现在我明白了!我们应该再加一行。
——————–
model.input_names = 'ensemble_' + str(i+1) + '_' + model.input_names
——————–
天哪,那没用……………………
坚持住,我相信这是一个小错误,一旦修复,你的整个模型都会得到修复。
也许在堆叠之前总结(例如 model.summary())模型以确认层名称确实不同?
已经检查过了,名称都改了,但我仍然收到同样的错误。
我想问您另一个问题,如果我进行 10 折交叉验证,那么我将得到 10 个(0 级)模型。在最后一步对测试集(未见数据)进行预测时,我应该将 testX 馈送给这 10 个(0 级)模型中的哪个模型?
如果我使用 5 个(0 级)基于保留数据集的模型,那么在 10 折交叉验证的情况下,模型的总数将是 5*10,对吗?在将测试集输入所有 10 对模型后,我将得到 10 倍多的 yhat…(但实际的 y 值无法准备 10 倍多)
我不确定我是否理解,为什么你要将 testX 馈送给在 9/10 的数据上训练的模型?
因为第一级模型是由通过零级模型馈送的数据训练的?所以如果我想对测试集进行预测,我必须将测试集依次馈送给零级模型,然后馈送给第一级模型。这是正确的吗?
是的,但不涉及 K 折分割。
嗨,我也收到这个错误,因为错误来自模型的类型而不是名称。model.summary() 中的格式是
层名称 (层类型)
ensemble_1_cu_dnnlstm_1 (CuDNNLSTM)
所以我们看到只有名称改变了,但类型仍然是 CuDNNLSTM,这会引发错误。
您找到解决此错误的方法了吗? 🙁
我不知道您为什么会收到此错误,抱歉。
也许可以尝试使用 LSTM,确认它有效,然后更改为 Cuda LSTM。
你好 Jason!感谢您分享您的方法。我正在尝试找到解决我问题的最佳方法,并非常感谢您的建议。我有 3 个不同的神经网络进行相同的预测,但使用不同的数据集:文本、图像和元数据。您的将它们与另一个神经网络堆叠在一起的方法会奏效吗?谢谢!
也许可以尝试一下,看看结果与三个模型的简单平均值相比如何?
在我的情况下,我应该将什么作为 testX 传递给
fit_stacked_model(stacked_model, testX, testy)
中的最终模型?你好,Jason。内容非常丰富。
我有一个关于 stackX 形状的问题。尽管您在 stacked_dataset 函数中提到 [1000, 5, 3],但它的形状不是 [1000, 3, 5] 吗?因为我理解为 [1000,3] *5 (yhat * 5 models)。
是的,这在“独立堆叠模型”部分有所介绍。
对于为 3 个类进行预测的 5 个模型,形状为 [1000, 5, 3]。
感谢教程!
逻辑回归给出值错误:输入形状错误。根据文档,fit() 期望 y: array-like, shape (n_samples,),但这里的 testy 形状为 (1000, 3)。我错过了什么?
很抱歉听到这个消息,我在这里有一些建议。
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
谢谢,杰森!
我是这样做的:我使用 `np.argmax(testy, axis=1)` 来解决这个问题。此外,在 `fit_stacked_model()` 中,额外的分类编码似乎没有必要,因为 `testy` 在数据分割步骤之前已经被编码了。
干得好!
嗨,Jason,
抱歉打扰您。但在运行 `define_stacked_model` 时,我收到以下错误:
模型中“conv2d_1_input”的名称使用了10次。所有层名称都应该是唯一的。
在这一步:
`model = Model(inputs=ensemble_visible, outputs=output)`
我正在堆叠10个模型,我检查了每个模型,并且层的名称都不同,因为您已经在代码中确保重命名了它们。有什么关于如何解决这个问题的建议吗?
谢谢。
是的,请注意我们在模型中更改层名称的部分。
我相信您可能跳过了一段代码。
嗨,Jason!
关于堆叠集成的精彩信息帖!我目前正在使用预训练模型(Inception、VGG、Resnet),并使用迁移学习在特定的医学图像上训练模型。
似乎当我单独使用这些模型时,我的损失相当低(0.0833)。
然而,当我尝试使用堆叠集成时,我的损失高达13!
您知道这可能是什么原因造成的吗?
我正在使用 Adam 优化器,一个带有 ReLU 激活函数的4096个节点的隐藏层。输出层使用 Softmax 激活函数。
也许模型组合的方式存在错误?
也许关注准确率而不是损失?
也许从简单的平均或加权平均开始,然后在此基础上进行改进?
嗯,我就是这么想的,但在某些迭代中(使用留一分组交叉验证),我确实得到了一个很好的低损失。然而,在所有迭代中,准确率都相当糟糕。
发布出来,以防万一这能帮到大家!
我的高损失率是由于隐藏层过多(4096)和数据不足造成的。将节点数量从4096减少到100大大改善了我的损失率!
哇,那真是太多的隐藏层了。
您是指隐藏层中的节点吗?
你好,非常感谢能浏览您的问题。我也正在尝试使用您上面提到的方法来解决我的医学图像分类任务,该任务的数据也很有限。您介意分享您的代码或者您是如何做到的吗?我的邮箱地址是 chenxiao97619@gmail.com 期待您的回复。非常感谢。
你好,先生
我喜欢您的教程。我正在尝试将您的 k-折交叉验证教程与这个堆叠教程结合起来。对于5折验证和5个基础模型,当我尝试生成堆叠集成(总共25个(5*5))时,我收到重命名层的错误。
“ValueError:名称“embedding_input”在模型中使用了3次。所有层名称都应该是唯一的。”
我完全使用了您编写的相同命令。
`for layer in model.layers`
`# make not trainable`
layer.trainable = False
`# rename to avoid ‘unique layer name’ issue`
`layer._name = ‘ensemble_’ + str(i+1) + ‘_’ + layer.name`
我该怎么做才能消除错误?
请帮助
我将这个概念应用到一个简单的CNN中,用于狗品种预测(没有应用数据增强和/或迁移学习),它的准确率从平均20%提高到了51.196%的惊人水平,这相当出乎意料。我觉得这好得有点不真实,所以我将进行一些验证,然后使用准确率约为85%的迁移学习模型进行测试。完成项目后我会分享我的结果。
很棒的文章!
对于这样的问题,如果使用迁移学习和数据增强,我期望准确率接近100%(90年代高位)。
您拿到结果了吗?
谢谢,Jason。这对我很有用。我有一个问题,我可以使用 Pytorch 来实现这个吗?
我看不出为什么不。
Hi Jason,这是一个非常有用的帖子,我很感谢您。只有一个问题:
在 `model = fit_stacked_model(members, testX, testy)` 中,您正在将 LR 模型拟合到子模型的预测上。然后,在 `yhat = stacked_prediction(members, model, testX)` 中,您正在对相同的堆叠测试数据进行预测,`fit_stacked_model` (元学习器) 不应该在训练堆叠预测上进行拟合吗?
是的,为了简洁起见,我使用了相同的数据。
理想情况下,您应该在训练集上拟合基础模型。然后,在验证集上拟合堆叠模型。最后,在测试集上进行预测。
https://machinelearning.org.cn/faq/single-faq/why-do-you-use-the-test-dataset-as-the-validation-dataset
感谢您提供如此出色的教程。我开发了一个 CNN-LSTM 回归模型。
输入形状为 (None, 58, 2),输出为 (None, 1)。
1. 如果我运行我的模型5次并每次保存最好的模型。这意味着我有5个模型可以用于集成。
2. 如果我使用集成堆叠模型连接这些模型的输出形状,形状将是 (None, 5)。我可以使用一个全连接的密集层和输出层来获得集成模型的最终输出。
我的问题如下:
我可以使用相同的训练数据来训练集成模型吗?或者我需要使用不同的数据来训练集成模型。
非常感谢。
Irtaza
干得不错。
好问题。理想情况下,集成模型应该在单独的保留数据集上进行拟合。
嗨。感谢您的教程。
我正在尝试实现以神经网络作为元学习器的部分。
我更改了层名称,但是当我运行它时
`model = Model(inputs=ensemble_visible, outputs=output)`
我收到一个错误 ValueError: The name “sequential_8_input” is used 2 times in the model. All layer names should be unique.
“sequential_8_input”对应于输入张量的名称,我找不到更改它的方法。请帮助。
您可能需要在定义层时或创建模型后更改层的名称。
每个层都有一个可以设置的 `.name` 属性,或者一个可以设置的 `name=` 参数。
你好,
我的堆叠模型也遇到了同样的问题。我更改了所有层的名称,但 model.input 和 model.output 的名称相同。
@Hajer,您成功解决了您的问题吗?
谢谢您的教程。
很抱歉听到这个消息,也许这些建议中的一些会对您有所帮助
https://machinelearning.org.cn/faq/single-faq/why-does-the-code-in-the-tutorial-not-work-for-me
嗨,Jason,
我的小型神经网络执行5折交叉验证(30次迭代)30次,保存30个模型,然后使用单独的堆叠模型(逻辑回归)将它们堆叠在一起。
我运行了一次,我的堆叠测试准确率是87。我再次运行,堆叠测试准确率下降到84。
即使经过这么多次迭代和交叉验证模型,系统仍无法产生可重复结果的原因是什么?
也感谢您最近的帮助!我能够一定程度上应对神经网络的数据方差和模型方差。
干得不错。
我有很多关于减少模型方差的教程,这里可能提供一个有用的起点。
https://machinelearning.org.cn/start-here/#better
谢谢,Jason。我明白了。这是其他问题(没有在测试时使用`just transform`对数据进行缩放)。我认为进行了4次运行的稳定预测,86.5 ±0.98 (±1.13%)。再次感谢这些很棒的教程 🙂
干得漂亮,非常棒!
Jason,感谢您的教程。
我尝试以稍微不同的方式重现您的示例,并遇到了一个问题:假设您需要训练多个模型,在这种情况下,为了避免内存泄漏,您需要插入 `keras.backend.K.clear_session()`,代码如下:
# 拟合堆叠模型
`def fit_stacked_model (model, inputX, inputy)`
` # prepare input data`
` X = [inputX for _ in range (len (model.input))]`
` # encode output data`
` inputy_enc = to_categorical (inputy)`
` # fit model`
` model.fit (X, inputy_enc, epochs = 300, verbose = 0)`
` model.save (‘[NameToSave] .h5’)`
` K.clear_session ()`
当我运行
`fit_stacked_model (stacked_model, testX, testy)`
一切运作完美,但是如果我再次运行
`fit_stacked_model (stacked_model, testX, testy)`
会引发以下错误:
`TypeError: Cannot interpret feed_dict key as Tensor: Tensor Tensor (“dense_1_input_1: 0”, shape = (?, 2), dtype = float32) is not an element of this graph.`
您能帮我找出我遗漏了什么吗?
再次感谢
也许不要清除会话?
嗨,Jason,谢谢您的回复;但是如果我不使用 `clear_session()`,运行多个模型后我会遇到内存问题。
为什么您会遇到内存问题?
您的TF安装是不是出了什么问题?
嗨,Jason,
我不知道为什么会这样,但更改我的tensorflow版本解决了问题,再次感谢!
很高兴听到这个消息。
嗨,Jason,
很棒的帖子。我在这里错过了一些非常基本的东西。您将完全相同的数据传递给子模型校准函数(即,我们没有进行袋装),那么级别0的子模型之间到底有什么不同呢?MLP 的校准是否存在随机性因素?
谢谢!
谢谢。
为了简洁起见,我在相同的数据上拟合了下一级模型,您应该使用单独的验证数据集。
我正在比较5个不同的子模型。它们为什么不同?输入相同……Adam 优化器中是否有随机种子?
因为随机学习算法,详情请看这里。
https://machinelearning.org.cn/faq/single-faq/why-do-i-get-different-results-each-time-i-run-the-code
明白了——谢谢!
很高兴听到这个消息。
你好,谢谢你的帖子。
我有一个问题,我将数据分成训练集和测试集。
然后使用训练数据训练了3个CNN模型。元学习器进一步使用相同的训练数据进行训练,并使用保留的测试数据进行验证,我发现元学习器的测试结果比基础学习器好。
使用相同的训练集训练元学习器并使用与0级模型相同的测试集进行验证是正确的方法吗?
不客气。
好问题。
最好将元学习器拟合到基础学习器的正确保留预测上。我看到两种简单的方法:
1. 在一个数据集上拟合基础学习器。然后获取另一个数据集,将数据通过基础学习器运行,然后使用这些输出拟合元学习器。
2. 使用一个数据集,将其分成k折,并保留基础学习器的折外预测,然后将基础学习器拟合到整个数据集上,并将元学习器拟合到基础学习器的保留预测上。
嗨,Jason!
我正在处理一个 seq2seq 问题,其中输出序列有5个组件。
我分别构建、训练并保存了3个子模型,每个模型预测一个组件。每个模型的单独准确率都很好,但我希望在整个输出序列(5个子模型一起!)上获得良好的准确率。
我简单地将5个子模型给出的5个预测进行拼接(字符串拼接)以构建整个序列,但这在整个输出序列中给出了糟糕的准确率!
有什么建议吗??我应该一起训练5个子模型吗?怎么做?
已知这5个子模型具有相同的输入。
谢谢 Jason 帮助我!
您可以分别训练和保存每个模型,使用它们对一个保留集进行预测,然后训练另一个模型来最好地组合它们的预测。
这可能会或可能不会导致性能提升。还可以尝试将原始输入添加到组合预测的模型中。
谢谢 Jason 的回复。
我不明白如何进行“使用它们对保留集进行预测”?“保留集”是什么意思?
谢谢你
也许这会有帮助。
https://machinelearning.org.cn/out-of-fold-predictions-in-machine-learning/
好的,谢谢先生
不客气。
嗨,Jason,
好帖子。
只有一个问题,您在堆叠模型上使用相同的数据集进行训练和评估。这会偏向性能指标吗,因为模型已经知道真实答案了?
是的,很有可能。
https://machinelearning.org.cn/faq/single-faq/why-do-you-use-the-test-dataset-as-the-validation-dataset
我们可以直接使用 `np.hstack()` 而不是 `np.dstack()` 吗?这样可以省去 reshape 步骤。
您可以随心所欲地连接数组!
我相信我使用 dstack 是为了确保我们将数组堆叠在最后一个维度上。
你好 jason,
我按照您的步骤为我的模型操作,但我收到以下错误:
`ValueError: Error when checking model input: the list of Numpy arrays that you are passing to your model is not the size the model expected. Expected to see 2 array(s), but instead got the following list of 1 arrays`
我的 `X_train` 和 `X_val` 都是包含两个相同类型数组的列表。您知道可能出了什么问题吗?
也许可以从教程中可用的示例开始,然后慢慢地将其修改为您的模型。
你好,
首先,感谢您分享代码,非常有帮助。
我有一个关于集成模型输入数据的问题。据我理解,模型接收5组输入数据,每组对应一个子模型。假设我的子模型都接收相同的输入数据,我如何才能让模型接收相同的数据集,而无需重复馈送5次呢?主要是我希望将相同的输入数据集馈送给这5个子模型。
不客气。
一次加载数据并将其提供给每个模型。也许我不理解问题?
我想将相同的输入数据馈送给模型作为子模型。
我正在使用 TensorFlow Serving 提供集成模型,并且按照现有代码,我不得不每次使用前缀“ensemble_i”调用同一个字段5次。根据您的回复,有没有一种方法可以在这个集成模型之上实现一个层,以便能够一次性馈送数据,使我的模型接收这一组数据并将其复制5次,然后修改字段名称以使其与层名称(ensemble_i_feature1,ensemble_2_feature2)对应?
这5个模型接收相同的数据作为输入。或者有没有其他选择?
是的,您可以将它们封装在一个模型中,即所谓的“多输入模型”。
我们在“集成堆叠模型”部分就是这样做的。也许您可以重新阅读该部分?
抱歉,我没跟上。或许您可以详细说明一下?
是的,我正在遵循集成模型,但我想将其改为单输入而不是多输入,将单个输入馈送到5个子模型。
或许可以写一个for循环?
你好Jason,非常感谢这个教程。我有一些问题:1) 如何可视化模型?2) 可以从模型中提取哪些信息,以及如何提取?我想比较两个模型以了解它们的相似性、相关性等。
再次感谢
或许可以专注于比较它们的预测能力。
嘿Jason,非常感谢你的教程,它非常有帮助!我正在做一个与你在“集成堆叠模型”示例中使用的代码类似的项目,但是使用了3个独立的全连接网络(有些具有不同的输入形状,因为它在相同的数据上收集不同的特征),我只是遇到了一个错误:
ValueError: 模型中名称“dense_1_input”使用了3次。所有层名称都应该是唯一的。
我注意到其他人也遇到了类似的问题,想知道你是否能对此问题提供一些解释。以下是我已经尝试过的方法:
– 在更改层名称前后使用model.summary()检查是否已成功更改(已更改)
– 重命名所有模型的名称本身,因为我在model.summary()中注意到它们都命名为“sequential_1”
– 使用model.get_layer(name=’dense_1_input’).name=”NEWNAME”手动更改每个模型的输入层名称(反而引发错误“ValueError: No such layer: dense_1_input”)
– 复制你已完成的代码块,只更改代码以加载我的3个模型而不是你的5个
我尝试的一切都引发了那个“dense_1_input”错误,即使名称在模型中根本不存在!我唯一看到它出现的地方是当我决定运行
ensemble_visible = [print(model.input) for model in members]时。
运行该代码返回
Tensor(“dense_1_input:0”, shape=(None, 3), dtype=float32)
Tensor(“dense_1_input_1:0”, shape=(None, 1), dtype=float32)
Tensor(“dense_1_input_2:0”, shape=(None, 1), dtype=float32)
注意:这是在更改层名称之后。
这会是问题所在吗?再次提前感谢!!
或许可以尝试为每个模型使用一个单独的输入层,并在层的构造函数中设置一个唯一的名称。
子模型不可训练是有原因的吗?这是Keras的限制还是有意为之?谢谢
这是设计使然——我们训练它们,然后找出如何最好地组合它们。
你好Jason——加权平均集成和使用线性模型元学习器(线性回归)的堆叠有什么区别?在我看来,它们似乎有相似的机制,你找到权重和基础模型输出的线性组合。我显然遗漏了一些显而易见的东西!
除了它们的名称之外,区别很小。
你好,Jason!
你的教程真的很有帮助,但我无法理解如何将两种不同类型的神经网络作为子模型(其中一个是CNN,另一个是深度神经网络)进行组合,当每个子模型具有不同的输入形状(例如 (n_rows, 12) 和 (n_rows, 59)),并且它们在不同的特征上进行训练,但目标是使用神经网络作为元分类器进行一个分类任务。
谢谢。
听到这个消息很抱歉,我不太清楚如何确切地帮助你。或许可以尝试调试故障原因,然后解决它?
嘿,Jason,我对内容印象深刻,它对我的工作帮助巨大,我能够解释集成模型,但有一个问题,因为我将其应用于多标签分类,在运行周期时它会崩溃并给出logits和标签必须可广播的错误:logits_size=[50,5] labels_size=[250,2],这个2表示它所取的标签是2,而我有5个类,问题出在哪里?
它只能用于二分类吗?
谢谢!
您可能需要更改输出层中的节点数量以匹配类数量,并将损失函数更改为分类交叉熵。
你好,
非常感谢您所做的一切,真的很有趣!
我想知道是否有可能将您所做的多类分类适应二分类。
我的意思是,只需设置
model.add(Dense(2, activation=’sigmoid’))
并在生成blob时设置中心=2就足够了吗?
非常感谢
是的,这会有帮助
https://machinelearning.org.cn/faq/single-faq/how-can-i-change-a-neural-network-from-regression-to-classification
感谢您的快速回复!
我的疑问与二分类有关,我创建了两个聚类,当然在输出层我需要两个神经元,否则就不匹配了。
但是通常来说,二分类的输出层不是只有一个神经元吗?
谢谢
在二分类任务中,输出层使用一个节点。
抱歉,但当我插入1个输出神经元时,像这样:
def evaluate_model(X_train_, y_train_, X_validation_, y_validation_)
# 定义模型
model = Sequential()
model.add(Dense(4, input_dim=2, activation=’relu’))
model.add(Dense(4, input_dim=2, activation=’relu’))
model.add(Dense(1, activation=’sigmoid’))
model.compile(loss=’binary_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
# 拟合模型
model.fit(X_train_, y_train_, epochs=100, verbose=0)
# 评估模型
_, test_acc = model.evaluate(X_validation_, y_validation_, verbose=0)
return test_acc
# 生成二维分类数据集
X_new, y_new = make_blobs(n_samples=500, centers=2, n_features=2, cluster_std=2, random_state=2)
y_new = to_categorical(y_new)
我收到这个错误:
检查目标时出错:dense_315 预期形状为 (1,) 但得到了形状为 (2,) 的数组
当我放入2个神经元时就没有这个错误,这看起来真的很奇怪。我一直都在谈论二分类。
听起来您已经对二元标签进行了独热编码。您可以将其使用argmax转换回具有0和1值的单个变量,然后拟合您的模型。
当然还要把categorical改成binary_crossentropy
嗨,Jason,
感谢您的精彩教程
如果我们使用验证集来训练一级模型,那么我们如何调整堆叠模型的超参数(例如隐藏节点的数量)呢?
或许可以对整个过程进行交叉验证。
或许可以使用一个单独的保留数据集。
你能告诉我任何关于集成堆叠集成的研究论文吗?我找不到这样的出版物。
或许可以尝试:scholar.google.com
嗨,Jason,
感谢您分享关于深度学习堆叠集成方法的精彩教程。
有一点让我困扰的是您评估总模型准确性的方式。您在某个测试数据集上训练了一级模型,然后在同一个数据集上计算了准确性。
您如何确定您报告的准确性增加不是由于在测试数据集上过拟合造成的?
同意,我应该使用一个保留数据集
https://machinelearning.org.cn/faq/single-faq/why-do-you-use-the-test-dataset-as-the-validation-dataset
Jason,如何在一个给定的训练集上绘制多个测试集…也就是说,我想要一条训练损失曲线和多条test1 test2 test3…曲线在同一个模型上。
每个数据集都有一条曲线,而不是每个样本。
您可以多次调用绘图函数,使用不同的数据以在同一张图上绘制多条线。
您好,Jason,感谢您的教程。我使用了您在“集成堆叠模型”部分介绍的一些代码来通过堆叠进行联邦学习。不幸的是,我遇到了元模型训练时间过长的问题。在我的设置中,每个伙伴都在自己的数据上训练一个主模型,我使用一个元模型来聚合每个伙伴模型的输出。拟合一个小型元模型所需的时间比进行整个经典联邦学习训练所需的时间长两到十倍,这让我感到惊讶。实际上,与通过联邦学习训练主模型相比,元模型相当小(400个权重)。您对为什么集成堆叠模型训练时间如此之长有什么想法吗?您有什么建议可以加快代码速度吗?
不客气。
由于权重数量庞大,速度可能会很慢。
对于使用示例代码并遇到“AttributeError: can’t set attribute”错误的人,您可以通过将其更改为以下代码来解决:
layer._name = ‘ensemble_’ + str(i + 1) + ‘_’ + layer.name
这是因为存在一个底层隐藏属性 _name,导致了冲突。
PS:我已将所有keras替换为tensorflow.keras
参考:https://stackoverflow.com/a/56444135
谢谢您的提示,您使用的是哪个版本的Keras?
您好,非常感谢您的文章,它对我的硕士论文研究非常有帮助。
我在原始输入数据扩展方面遇到了一点麻烦。这应该被视为元模型的附加层,还是应该在连接不同模型后,元模型再次使用 .fit() 在数据上进行训练?
不,只是元模型的更多输入特征。
你好,
我有一个问题(可能是我理解错了什么)
假设
我们将元学习器更改为XGBoost算法,并设置max_depth=100。
我们得到了几乎完美的拟合。我们在stackX数据上拟合模型,并在stackX数据上进行测试。为什么stackX数据和测试标签没有被分割成额外的测试数据?在训练数据上进行预测不是衡量模型效率的好方法。
理想情况下,我们应该在基础模型(例如通过交叉验证或训练/验证分割)做出的保留预测上拟合元模型,然后在一个单独的保留数据集上评估整个系统。
你好,Jason。谢谢你的文章。我对集成技术有点困惑。
假设数据的形状是 (1316, 108164)。为了训练目的,我将数据分割成 3863 份。因此,每个分割的维度是 (1316, 28),并且创建了 3863 个自动编码器模型。每个模型在 (761, 28) 个样本上训练,损失函数是 'mse'。我已经训练并保存了所有子模型。
通过遵循独立堆叠模型方法,我加载了所有已保存的模型,并在测试数据集 (1316, 28) 上评估了每个单独的模型。测试数据集有 3863 个分割。由于有 3863 个模型,我用每个数据分割评估了每个模型,例如用 split_1 测试数据评估 model_1,用 split_2 数据评估 model_2。
现在我想从子模型中评估整个模型。评估整个模型损失的最佳方法是什么?
模型真多啊!
理想情况下,您希望在未用于拟合基础模型或元模型的数据上评估整个模型。
感谢这篇出色的博客。我测试了上面的代码,发现用堆叠模型在相同数据上进行拟合和测试是不合适的。因为当我在未见过的数据上进行测试时,损失从0.4增加到0.5。
# 定义集成模型
stacked_model = define_stacked_model(members)
# 在测试数据集上拟合堆叠模型
fit_stacked_model(stacked_model, testX, testy)
# 进行预测和评估
yhat = predict_stacked_model(stacked_model, testX)
yhat = argmax(yhat, axis=1)
acc = accuracy_score(testy, yhat)
print(‘Stacked Test Accuracy: %.3f’ % acc)
是的,在保留数据集上进行测试是一个好主意。为了保持示例的简洁性,我没有这样做。
是否可以将sklearn模型与keras模型堆叠,或者将XGBoost与keras模型堆叠?
也许可以。
如果您对每个模型都使用sklearn包装器和内置的堆叠模型。
https://machinelearning.org.cn/stacking-ensemble-machine-learning-with-python/
嗨Jason,感谢您出色的教程!我有一个关于堆叠模型的问题。是否可以将LSTMs和XGBoost/RF模型堆叠起来?:)
可以,尽管不容易。这需要自定义代码。
嗨Jason,感谢您精彩的教程!您有没有发布过与模糊逻辑相关的任何主题或代码?
抱歉,我没有。
在拟合MLP子模型堆叠集成的线性元学习器的完整示例中,第71行
model = fit_stacked_model(members, testX, testy)
这一行本应拟合训练X, y。如果我们在测试数据上拟合,然后预测testX,那肯定会输出好的结果。
抱歉,我不太理解您的问题/评论,您能重新措辞或详细说明一下吗?
嗨,Jason,
首先非常感谢这个教程,总的来说感谢您的工作。
我也有同样的问题,让我试着重新措辞一下。
在第80行:fit_stacked_model(stacked_model, testX, testy),您正在使用测试数据拟合堆叠模型,然后您再次使用相同的数据进行评估。
难道不应该使用不同的数据进行训练和评估吗?
谢谢,
Kostas
是的,这是一个常见问题,我在这里回答了
https://machinelearning.org.cn/faq/single-faq/why-do-you-use-the-test-dataset-as-the-validation-dataset
“每个子模型的输入层将作为这个新模型的独立输入头。”
我正在尝试构建一个只有一个头的集成模型。有可行的解决方案吗?
此致,
埃马努埃莱
或许吧。你可能需要尝试使用函数式API。可能会很混乱。
如何绘制上述神经网络模型堆叠泛化集成的损失和验证损失?
谢谢
这很棘手,因为模型是在不同的数据上训练的。
不过,或许您可以将每个轨迹保存到文件,并在最后绘制它们。
我在训练集成模型中的模型时使用train test split来分离我的数据。用不同的随机状态来训练集成中的每个模型是否有用?这是否能让我捕捉到当有用数据被拉入测试分割而不是训练分割时可能丢失的方差,或者这会导致过拟合?感谢您的精彩文章,这些文章真是救命稻草!
是的。
这可能有助于这种思路
https://machinelearning.org.cn/different-results-each-time-in-machine-learning/
还有这个。
https://machinelearning.org.cn/evaluate-skill-deep-learning-models/
感谢您的宝贵教程。我只是想更多地了解您为什么使用逻辑回归?
谢谢。
使用线性模型来组合预测是很常见的——它效果很好,并且被广泛理解。
我遵循您的教程用于我的时间序列单变量数据。但是估计器根据所使用的子模型数量给出n个预测。但是我应该有一个列来进行评估。我如何解决这个问题?
抱歉,我不知道您问题的原因。或许您可以简化您的示例,以帮助找出故障的原因。
如何集成 CNN、RNN 和 LSTM 模型
与MLP相同,没有真正的区别,可以将预测结果平均,或者使用一个模型来平均预测结果。
我对这个模型有一个疑问,那就是你用训练数据集训练了level0,之后你用测试数据集/验证数据集训练了level1。整个模型现在是用训练和验证数据集训练的,或者你可以说模型已经见过这些数据了。但是如果你对testx进行评估,它会被认为是一个泛化模型吗?模型也看到了testx,以及相应的真实值testy对吗?
是的,模型不应该以任何方式看到测试数据集。
你好!很棒的帖子——有没有关于使用 Keras ImageDataGenerator 而不是加载 x_train, y_train 数据的建议/技巧?
谢谢。
是的,也许从这里开始
https://machinelearning.org.cn/how-to-load-large-datasets-from-directories-for-deep-learning-with-keras/
嘿,很棒的工作!
我有一个错误:ValueError: The name “conv2d_input” is used 3 times in the model. All layer names should be unique. at tf.keras.models.Model(inputs=ensemble_visible, outputs=output)。
我有3个模型(CNN),可以预测8个类别。你知道可能是什么原因吗?
手头没有,也许你可以把你的代码和错误信息发布到 stackoverflow.com。
您好,感谢您的教程,这是我见过最棒的教程。我有一个问题:您使用了level 0的预测输出和测试数据的组合来训练元学习器。然后您使用相同的测试数据评估模型,这会被认为是一个泛化模型吗?
最好使用验证集来训练level0,然后使用单独的测试集来评估整个模型。
我在这两种情况下都使用相同的方法来保持示例的简单性。
https://machinelearning.org.cn/faq/single-faq/why-do-you-use-the-test-dataset-as-the-validation-dataset/
谢谢你,Jason!
祝你有美好的一天!
不客气。
感谢您的教程。我想应用相同的思想,但是使用 ImageDataGenerator,所以我堆叠了通过 preprocessing_function 创建的训练批次训练的模型,但是当我尝试拟合堆叠模型并向其提供 train_generator 时,它给了我一个错误,说没有合适的输入格式与 ImageDataGenerator 列表。
您有什么解决方案或建议使其工作吗?
谢谢
不客气。
也许你可以一次训练一个模型,保存它们,然后基于每个level 0模型所做的预测来训练一个level 1模型。
Jason 先生您好,感谢您的帖子,非常有帮助。
我想将您的代码重新用于多类别问题,我有一些在 X 射线图像上训练的模型,以获得 11 个类别输出,当我拟合堆叠模型时,我得到了以下错误:
y 应该是一个 1D 数组,但得到的是一个形状为 (1085, 11) 的数组。
确保您的数据与模型的预期匹配,或者模型与您的数据形状匹配。
我当时在遵循“集成堆叠模型”部分,因为我的子模型是基于神经网络的模型,所以要训练一个级别1的模型,我需要像您在示例中那样连接各层,堆叠模型需要有多个输入,但是根据您的评论,我是否需要不同的逻辑来处理堆叠模型?
谢谢,
抱歉,我不明白你的问题。也许你可以换个说法或者详细说明一下?
在单独的堆叠模型部分,当在测试数据集上评估独立模型时,为什么我们必须使用 to_categorical() 对 testy 进行编码?
因为神经网络必须处理数字。
我明白了。我之前的印象是,我们使用的是已经通过 to_categorical() 转换过的“y”。但我后来发现,我们使用的是尚未通过 to_categorical() 转换的 testy,为了符合已保存的模型,我们正在使用 to_categorical() 对 testy 进行编码。谢谢。
不客气。
嗨
我使用我的数据库创建了5个子模型(不同的神经网络)。然后,我使用差分进化算法和这5个子模型创建了一个加权平均集成(WAE)模型。
我还使用这5个子模型和一个带有一个隐藏层的神经网络(作为元学习器)创建了一个集成堆叠集成(ISE)模型。
ISE的准确率为0.96,WAE的准确率为0.99。
我无法理解为什么ISE比WAE差?我认为具有值的线性组合会提供相同的结果。您能帮我理解这个问题吗?为什么ISE中的元学习器找不到好的线性组合。也许为附加的NN选择的非线性激活函数使事情变得更糟。我错过了什么吗?
我无法准确解释,但可以肯定的是 ISE 不是线性的。它更像是嵌套函数,进一步处理前一步。有些数据可能更适合使用线性模型。如果这是你的情况,可能 ISE 正在过拟合。
嗨,Json,
请问您为什么使用测试集数据来训练最终模型,而不是训练集(最初用于训练子模型)?
由于最终模型层(可训练层)没有见过训练数据,我们理想情况下应该在训练集上训练,对吗?
我想是这样。那可能是一个打字错误。
你好,
非常感谢您的这份工作,它解释得很清楚。我尝试实现您的堆叠方法来结合(CNN、LSTM、BiLSTM 和 GRU),使用以下架构:
def CNN(input_shape)
X_indices = Input(input_shape)
embeddings = embedding_layer(X_indices)
X = Conv1D(512, 3, activation=’relu’)(embeddings)
X = MaxPooling1D(3)(X)
# X = Conv1D(256, 3, activation=’relu’)(X)
# X = MaxPooling1D(3)(X)
X = Conv1D(256, 3, activation=’relu’)(X)
X = Dropout(0.8)(X)
X = MaxPooling1D(3)(X)
X = GlobalMaxPooling1D()(X)
X = Dense(256, activation=’relu’)(X)
X = Dense(1, activation=’sigmoid’)(X)
model = Model(inputs=X_indices, outputs=X)
return model
def LSTM_(input_shape)
X_indices = Input(input_shape)
embeddings = embedding_layer(X_indices)
X = LSTM(128, return_sequences=True)(embeddings)
X = Dropout(0.6)(X)
# X = LSTM(128, return_sequences=True)(X)
# X = Dropout(0.6)(X)
X = LSTM(128)(X)
X = Dense(1, activation=’sigmoid’)(X)
model = Model(inputs=X_indices, outputs=X)
return model
def BiLSTM(input_shape)
X_indices = Input(input_shape)
embeddings = embedding_layer(X_indices)
X = Bidirectional(LSTM(128, return_sequences=True))(embeddings)
X = Dropout(0.6)(X)
X = Bidirectional(LSTM(128, return_sequences=True))(X)
X = Dense(1, activation=’sigmoid’)(X)
model = Model(inputs=X_indices, outputs=X)
return model
def GRU_(input_shape)
X_indices = Input(input_shape)
embeddings = embedding_layer(X_indices)
X = GRU(128, return_sequences=True)(embeddings)
X = Dropout(0.6)(X)
X = GRU(128, return_sequences=True)(X)
X = Dropout(0.6)(X)
X = Dense(1, activation=’sigmoid’)(X)
model = Model(inputs=X_indices, outputs=X)
return model
maxLen = 150
X_train_indices = tokenizer.texts_to_sequences(X_train)
X_train_indices = pad_sequences(X_train_indices, maxlen=maxLen, padding=’post’)
adam = keras.optimizers.Adam(learning_rate=0.0001)
model1 = CNN((maxLen,))
print(model1.summary())
model1.compile(optimizer=’adam’, loss=’binary_crossentropy’, metrics=[‘accuracy’])
model1.fit(X_train_indices, Y_train, batch_size=64, epochs=1)
model1.save(‘model1.h5′)
print(“model 1 CNN saved”)
model2 = LSTM_((maxLen,))
print(model2.summary())
model2.compile(optimizer=’adam’, loss=’binary_crossentropy’, metrics=[‘accuracy’])
model2.fit(X_train_indices, Y_train, batch_size=64, epochs=1)
model2.save(‘model2.h5′)
print(“model 2 LSTM saved”)
model3 = BiLSTM((maxLen,))
print(model3.summary())
model3.compile(optimizer=’adam’, loss=’binary_crossentropy’, metrics=[‘accuracy’])
model3.fit(X_train_indices, Y_train, batch_size=64, epochs=1)
model3.save(‘model3.h5′)
print(“model 3 BiLSTM saved”)
model4 = GRU_((maxLen,))
print(model4.summary())
model4.compile(optimizer=’adam’, loss=’binary_crossentropy’, metrics=[‘accuracy’])
model4.fit(X_train_indices, Y_train, batch_size=64, epochs=1)
model4.save(‘model4.h5’)
print(“model 4 GRU saved”)
当我组合两个模型时它能工作,但是当我尝试组合这4个模型时它给我这个错误:
连接轴的所有输入数组维度必须完全匹配,但在维度1上,索引0处的数组大小为1,索引1处的数组大小为1
你能帮我解决这个问题吗。谢谢。
你好 Nassera……你的代码中哪一行被识别为有错误?
谢谢您的回复,错误发生在stacked_dataset函数(在reshape函数中)。请注意,我在twitter数据集上实现了您的代码。
我在函数中插入了一些“print”来显示不同的维度,如下所示:
def stacked_dataset(members, inputX)
stackX = None
for model in members
# 进行预测
yhat = model.predict(inputX)
print(“yhat”, yhat.shape)
# y = np.argmax(yhat, axis=1) , verbose=0
# stack predictions into [rows, members, probabilities]
if stackX is None
stackX = yhat #
else
stackX = np.vstack((stackX, yhat))
print(‘stackX’, stackX.shape)
# flatten predictions to [rows, members x probabilities]
stackX = stackX.reshape((stackX.shape[0], (stackX.shape[1]*stackX.shape[2])))
print(‘stackX after reshape’, stackX.shape)
return stackX
我收到了这个错误:
模型4 GRU已保存
>已加载模型
已加载模型
yhat (39891, 1)
yhat (39891, 1)
stackX (79782, 1)
yhat (39891, 150, 1)
回溯(最近一次调用)
文件“C:/Users/admin/PycharmProjects/MyProject/Ensemble DL/MDSA.py”,第229行,位于
modelStacked = fit_stacked_model(members, X_train_indices, Y_train)
文件“C:/Users/admin/PycharmProjects/MyProject/Ensemble DL/MDSA.py”,第220行,位于fit_stacked_model
stackedX = stacked_dataset(members, inputX)
文件“C:/Users/admin/PycharmProjects/MyProject/Ensemble DL/MDSA.py”,第210行,位于stacked_dataset
stackX = np.vstack((stackX, yhat))
文件“”,第5行,位于vstack
文件“C:\Users\admin\PycharmProjects\MyProject\venv\lib\site-packages\numpy\core\shape_base.py”,第283行,位于vstack
返回 _nx.concatenate(arrs, 0)
文件“”,第5行,位于concatenate
ValueError: 所有输入数组必须具有相同的维度数,但索引0处的数组有2个维度,索引1处的数组有3个维度。
你好,
我有4个模型,它们的输出形状如下:
预测CNN模型 (9973, 1)
预测LSTM模型 (9973, 1)
预测BiLSTM模型 (9973, 150, 1)
预测GRU模型 (9973, 150, 1)
我如何堆叠并将这些输出传递给逻辑回归?
谢谢你。
嘿!!!
你能告诉我如何堆叠3个具有不同输入形状数组的模型吗??????
因为我正在使用DNN(2D数组)、CNN(3D数组)、LSTM(3D数组)
你好 Halima……以下内容应该能帮助你更清楚地理解:
https://www.geeksforgeeks.org/stacking-in-machine-learning-2/
你好 Jason,这是一个很棒的教程。我只是想更好地理解它如何(或者是否)适合我的用例。
我有几个数据集,每个数据集都包含相同的特征集。它们属于一组类似的操作机器,其操作由这些特征表示。每台机器一个数据集。
我们目前有许多基于 LSTM Autoencoder 的回归模型,每台机器一个模型。这在建模特征方面效果很好。
现在我正在研究一个类似的模型,它应该能够从所有数据集中学习并通过一个模型进行泛化。它应该将特征集作为输入,无论它属于哪台机器。
堆叠集成是正确的方法吗?有什么建议吗?非常感谢!谢谢
你好 Mike……以下资源可能对你有所帮助。
https://machinelearning.org.cn/ensemble-machine-learning-with-python-7-day-mini-course/
你的元学习器正在作弊,它使用了 test_y,这在实践中通常会在 Kaggle 服务器上,所以你无法访问它。如果你尝试使用训练数据进行元学习器训练,那么子模型将被要求预测它们已经见过的点,因此元学习器的表现会非常差。你可以尝试使用3个不同的数据集,一个用于训练子模型,一个用于元学习器,最后一个用作测试集,但这会使子模型可用的数据更少,性能也更差。
感谢 Timothe 的反馈!
嗨 James,
我手动创建了5个子 NN 模型,然后将您的示例应用于加载已定义的模型,并尝试将其改编为 LinearRegression。
我成功地使用了初始模型,并使用 LinearRegressor 作为元学习器。
yhat = stacked_prediction(members, model, X_val)
mae = mean_absolute_error(y_val, yhat)
print(‘Stacked Mean Abs Error: %.3f’ % mae)
#0.094
所以,是的,我使用 mean_absolute_error 作为我的估计。
然而,当使用神经网络作为元学习器改编示例时,我得到
fit_stacked_model(stacked_model, X_train, y_train)
# 进行预测和评估
yhat = predict_stacked_model(stacked_model, X_val)
mae = mean_absolute_error(y_val, yhat)
print(‘Stacked Mean abs Error: %.3f’ % mae)
#mae = 2.1
此外,将 verbose = 1 更改后,我可以看到我的模型在经过几个 epoch 的训练后并没有真正改善。
Epoch 1/300
1000/1000 [==============================] – 7s 5ms/step – loss: 0.3456 – mae: 0.4919
Epoch 2/300
1000/1000 [==============================] – 4s 4ms/step – loss: 0.2513 – mae: 0.4444
Epoch 3/300
1000/1000 [==============================] – 4s 4ms/step – loss: 0.2513 – mae: 0.4444
Epoch 4/300
1000/1000 [==============================] – 4s 4ms/step – loss: 0.2512 – mae: 0.4444
Epoch 5/300
1000/1000 [==============================] – 4s 4ms/step – loss: 0.2512 – mae: 0.4444
..
..
Epoch 41/300
1000/1000 [==============================] – 5s 5ms/step – loss: 0.2506 – mae: 0.4444
Epoch 42/300
1000/1000 [==============================] – 5s 5ms/step – loss: 0.2506 – mae: 0.4444
Epoch 43/300
1000/1000 [==============================] – 5s 5ms/step – loss: 0.2506 – mae: 0.4444
Epoch 44/300
772/1000 [======================>…….] – ETA: 1s – loss: 0.2506 – mae: 0.4444
我不确定它为什么会这样?
感谢您的时间。
你好 Ian……你可能会发现以下资源很有帮助。
https://machinelearning.org.cn/learning-curves-for-diagnosing-machine-learning-model-performance/
很棒的帖子
我有个问题
我们可以将此代码用于二元图像分类吗?
请先生,我正在等待您的回复。
你好 kamranullah……非常欢迎!这个例子以及以下资源是一个很好的起点:
https://machinelearning.org.cn/binary-classification-tutorial-with-the-keras-deep-learning-library/
我正在使用 ImageDatagenerator 类动态生成数据集,因此我使用 validation_set.class_indices 代替 testy。
它正在抛出错误。请帮忙
感谢您的详细解释。
有些事情我不明白
在“单独堆叠模型”部分
您从5个子模型创建了一个堆叠模型,其中每个子模型对每个类别都有3个预测。
然后您通过将测试集的大小更改为[1000,15]来更新堆叠模型的数据集。
下一步您选择逻辑回归作为堆叠模型的分类器,但现在我感到困惑。
即使我们说我们可以通过“一对多”的方法将逻辑回归用于多类别分类
通过从所有二元分类中取出 argmax,如果我们有3个类别,那将很好,但现在我们有15个!
那么选择类别“i”(其中 I>3)的 argmax 是什么意思呢?
你好 DBM……以下资源提供了另一种观点,你可能会感兴趣:
https://www.analyticsvidhya.com/blog/2021/08/ensemble-stacking-for-machine-learning-and-deep-learning/
如果每个基础模型的预测类别概率都是正确的且等于1.0000,但在集成模型中,正确类别的预测类别概率下降,例如降至0.9821,并且其他类别也有概率分配,例如0.179。我得到这个结果有什么具体原因吗?
你好 Firly……关于何时使用集成学习的最佳实践,以下资源可能会引起你的兴趣。它不一定总是有优势。
https://machinelearning.org.cn/why-use-ensemble-learning/
谢谢你回答了我的问题,我能问你一个我正在做的关于集成堆叠的案例研究的手动计算问题吗,我想知道从2个模型连接到隐藏层之后的过程是如何进行的?它是由一个二维矩阵组成的吗?不是隐藏层只应该输入一维矩阵吗?
我正在使用上面提到的确切过程,但在执行时遇到错误
def stacked_model(models)
for model in models
model.trainable = False
# 定义输入
inputs = [model.input for model in models]
# 定义输出
ensemble_outputs = [model.output for model in models]
merge = layers.Concatenate()(ensemble_outputs)
outputs = layers.Dense(4,activation=’softmax’)(merge)
model = tf.keras.Model(inputs=inputs,outputs=outputs)
return model
models = [densenet,vgg19,xception,effnet]
stacked_model = stacked_model(models)
—————————————————————————
AttributeError Traceback (最近一次调用)
in ()
1 models = [densenet,vgg19,xception,effnet]
—-> 2 stacked_model = stacked_model(models)
2 帧
/usr/local/lib/python3.10/dist-packages/keras/src/layers/merging/base_merge.py in (.0)
248
249 def compute_output_spec(self, inputs)
–> 250 output_shape = self.compute_output_shape([x.shape for x in inputs])
251 output_sparse = all(x.sparse for x in inputs)
252 return KerasTensor(
AttributeError: Exception encountered when calling Concatenate.call().
‘list’ object has no attribute ‘shape’
Concatenate.call()接收到的参数
• args=([[”], [”], [”], [”]],)
• kwargs=
有人能帮我吗
你好 Rishik……看起来你遇到的错误是由于你将输入传递给 `Concatenate` 层的方式造成的。具体来说,错误消息表明你正在传递一个列表的列表,而 `Concatenate` 期望的是一个张量列表(而不是一个列表的列表)。你可以这样修复它:
### 问题
问题可能在于输入张量的嵌套不正确。当你为堆叠模型定义输入和输出时,你需要确保将张量对象(来自你的模型)传递给 `Concatenate` 层。
### 解决方案
你可以修改你的代码以确保正确处理输入和输出:
1. 确保每个模型的输入和输出都是张量。
2. 在 Python 中为 `for` 循环使用正确的缩进。
3. 使用 TensorFlow 的
layers.Concatenate()
正确地连接模型的输出。以下是您的代码的修正版本:
python
import tensorflow as tf
from tensorflow.keras import layers
def stacked_model(models)
# 冻结基础模型的层以防止训练
for model in models
model.trainable = False
# 定义每个模型的输入
inputs = [model.input for model in models]
# 获取每个模型的输出
ensemble_outputs = [model.output for model in models]
# 连接模型的输出
merge = layers.Concatenate()(ensemble_outputs)
# 定义最终输出层
outputs = layers.Dense(4, activation='softmax')(merge)
# 创建新模型
model = tf.keras.Model(inputs=inputs, outputs=outputs)
return model
# 定义您的模型
models = [densenet, vgg19, xception, effnet]
# 创建堆叠模型
stacked_model = stacked_model(models)
### 解释
1. **
inputs = [model.input for model in models]
**:此行从列表中的所有模型收集输入张量。2. **
ensemble_outputs = [model.output for model in models]
**:类似地,此行从所有模型收集输出张量。3. **
layers.Concatenate()(ensemble_outputs)
**:这将每个模型的输出连接成一个单一的张量。### 错误发生的原因
错误
'list' object has no attribute 'shape'
是将列表的列表(或非张量对象)传递给Concatenate
层造成的。修正后的代码确保您传递的是实际的输入和输出张量。如果这解决了您的问题,请告诉我!