如何利用Keras在深度神经网络中进行堆栈集成(Stacking Ensemble)

    译自Machine Learning Mastery~

    模型平均是一种集成技术,其中多个子模型对组合预测的贡献相等。

    利用子模型的预期性能,加权各子模型对组合预测的贡献,可以改善模型平均。通过培训一个全新的模型来学习如何最好地组合来自每个子模型的贡献,可以进一步扩展这一点。这种方法被称为Stacked Generalization(堆栈泛化),或简称Stacking,可以产生比任何单个贡献模型更好的预测性能。

    在本教程中,您将了解如何为深度学习神经网络开发一个Stacked Generalization Ensemble(集成堆栈泛化)。

    完成本教程后,您将知道:

  • Stacked Generalization是一种集成方法,新模型学习如何最好地结合多个现有模型的预测。
  • 如何以神经网络为子模型,以scikit-learn分类器为元学习机,建立Stacking模型。
  • 如何将神经网络的子模型嵌入到更大的叠加集成模型中进行训练和预测。

    让我们开始吧。

教程概述

    本教程分为六个部分;它们是:

  1. Stacked Generalization Ensemble(集成堆栈集成)
  2. 多分类问题
  3. 多层感知器模型
  4. 训练和保存子模型
  5. 单独的Stacking模型
  6. 综合Stacking模型

 Stacked Generalization Ensemble(堆叠泛化集成)

    一个平均集成模型结合了来自多个训练模型的预测。

    这种方法的一个限制是,无论模型执行得如何,每个模型对集成预测的贡献都是相同的。这种方法的一种变体称为加权平均集成,它通过模型对预留集(holdout dataset)的信任或预期性能来确定每个集成成员的权重。这允许性能良好的模型贡献更多,而性能较差的模型贡献更少。加权平均集成比模型平均集成提供了一种改进。

    该方法的另一个推广是用其他算法来替代线性加权和(例如线性回归)模型。这种方法称为堆栈泛化(Stacked Generlization),简称Stacking。

    在Stacking中,算法将子模型的输出作为输入,并尝试学习如何将输入预测最好地组合起来,从而做出更好的输出预测。

    我们科研将Stacking过程看作有两个级别:级别0和级别1。

  • 0级:0级数据是训练数据集的输入,0级模型学习如何从这些数据中进行预测。
  • 1级:第1级数据以第0级模型的输出作为输入,第1级模型(元学习机)学习从该0级数据进行预测。

    Stacking Generalization的工作原理是根据给定的学习集推导出泛化器的偏差。这个推论通过在第二个空间中进行泛化来进行,第二个空间的输入是原始泛化器在部分学习集上学习的结果,并尝试猜测其余部分,其输出(例如)是正确的猜测。

                                                                                                                                          — Stacked generalization, 1992.

    与加权平均集成不同,Stacked Generalization Ensemble(堆栈泛化集成)可以将预测集用作上下文,并有条件地决定以不同的方式赋以输入预测权重,从而可能获得更好的性能。

    有趣的是,尽管Stacking被描述为具有两个或多个0级模型的集成学习方法,但它可以用于只有一个0级模型的情况。在这种情况下,1级或元学习机模型学习从0级模型纠正预测。

    ......虽然它也可以用于只有一个泛化器的情况下,作为一种改进该泛化器的技术。

                                                                                                                                         — Stacked generalization, 1992.

    重要的是,在一个单独的数据集上对元学习机进行训练,以获得用于训练0级模型的示例,以避免过度拟合。

    可以用一个简单的方法,把训练数据集分成训练集和验证集。0级模型在训练集训练。1级模型使用验证训练集,原始输入喂进0级模型预测,并用作输入到1级模型。

   预留集(hold-out validation set)验证训练Stacking模型的一个限制是0级和1级模型没有在完整的数据集上训练。

    一种更为复杂的方法是使用k-fold交叉验证来开发元学习机模型的训练数据集。每个0级模型都使用k-fold交叉验证(甚至为了达到最大效果使用留一法交叉验证)进行训练;然后模型被丢弃,但是预测被保留。这意味着对于每个模型,都有一个模型版本所做的预测,而这个版本的模型并没有针对这些例子进行训练,例如,有一些在预留的例子,但是在这个例子中,是针对整个训练数据集的。

    这里,引用一下详解stacking过程里面的一张图,非常经典地阐述了Stacking的过程。同时也非常推荐大家看一下这篇博客,讲得比较清楚。关于Stacking和其他集成方法的总结以及在scikit-learn中使用Stacking的代码可参考集成学习总结 & Stacking方法详解。

如何利用Keras在深度神经网络中进行堆栈集成(Stacking Ensemble)_第1张图片

    然后将预测作为训练元学习机的输入。然后对整个训练数据集进行0级模型的训练,并与元学习机一起使用Stacking模型对新数据进行预测。

    Tips(使用时的注意事项):在实践中,通常使用不同的算法来准备每个level 0模型,以提供不同的预测集。

    ....Stacking通常不用于组合同类型的模型[…],它适用于不同学习算法构建的模型。

                                                                      — Practical Machine Learning Tools and Techniques, Second Edition, 2005.

    使用简单的线性模型组合预测结果也很常见。由于线性模型的使用非常普遍,Stacking在最近被称为“model blending”或简称为“blengding”,尤其是在机器学习竞赛中。

    ......采用多响应最小二乘线性回归技术(multi-response least squares linear regression technique )作为高级推广器。该技术提供了一种组合0级模型置信度的方法。

                                                                                                                            — Issues in Stacked Generalization, 1999.

    Stacking Generalization Ensemble可以被用作回归和分类问题。在分类问题中,使用类概率预测而不是类标签作为元学习机的输入,效果更好。

    ......我们应该使用类概率,而不是单一的预测类作为更高层次学习的输入属性。类概率作为预测的置信度。

                                                                                                                              — Issues in Stacked Generalization, 1999.

    现在我们已经熟悉了Stacking Generalization,我们可以通过一个案例研究来开发一个堆栈深度学习模型。

多分类问题

    我们将使用一个小的多类分类问题作为基础来演示Stacking。

    scikit-learn类提供了make_blobs()函数,该函数可用于创建具有指定样本数量、输入变量、类和类内样本方差的多类分类问题。该问题有两个输入变量(表示点的x和y坐标),每组点的标准差为2.0。我们将使用相同的随机状态(伪随机数生成器的种子)来确保总是得到相同的数据点。

# generate 2d classification dataset
X, y = make_blobs(n_samples=1000, centers=3, n_features=2, cluster_std=2, random_state=2)

    结果是我们可以建模的数据集的输入和输出元素。为了了解问题的复杂性,我们可以在二维散点图上绘制每个点,并按类值对每个点进行着色。下面列出了完整的示例。

# scatter plot of blobs dataset
from sklearn.datasets.samples_generator import make_blobs
from matplotlib import pyplot
from pandas import DataFrame
# generate 2d classification dataset
X, y = make_blobs(n_samples=1000, centers=3, n_features=2, cluster_std=2, random_state=2)
# scatter plot, dots colored by class value
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的标准偏差意味着类不是线性可分的(由直线可分),这导致了许多不明确的点。这是可取的,因为这意味着问题不是微不足道的,并将允许神经网络模型找到许多不同的“足够好”的候选解决方案,从而导致高方差。

如何利用Keras在深度神经网络中进行堆栈集成(Stacking Ensemble)_第2张图片

 多层感知器模型

    在定义模型之前,我们需要设计一个适合于Stacking的问题。

    在我们的问题中,训练数据集相对较小。具体地说,在训练数据集中,训练集和预留集(holdout dataset)的比例为10:1。这模拟了一种情况,在这种情况下,我们可能有大量未标记的示例和少量标记的示例来训练模型。

    我们将从blobs问题中创建1100个数据点。模型将在前100个点上进行训练,剩下的1000个点将保存在测试数据集中,模型无法使用。

    该问题是一个多类分类问题,我们将在输出层使用softmax激活函数对其建模。这意味着该模型将预测一个包含三个元素的向量,每个元素为样本属于这三个类中的每一类的概率。因此,在我们将行分割到训练集和测试数据集之前,我们必须对类值进行独热编码。我们可以使用Keras to_categorical()函数来实现这一点。

# generate 2d classification dataset
X, y = make_blobs(n_samples=1100, centers=3, n_features=2, cluster_std=2, random_state=2)
# one hot encode output variable
y = to_categorical(y)
# split into train and test
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个节点的隐藏层和一个修正的线性激活函数(Relu),然后有一个包含3个节点的输出层来预测这3个类的概率和一个softmax激活函数。

    由于问题是多类的,我们将使用分类交叉熵损失函数和Adam来优化模型。

# define model
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.summary()

    这里我们在原博客的基础上加了一句model.summary(),可以直观地看到网络的结构。有关于详细的参数数量计算可参考老鼠屎从前的文章Keras实战:基于LSTM的股价预测方法。

如何利用Keras在深度神经网络中进行堆栈集成(Stacking Ensemble)_第3张图片

    我们使用500个epochs,并在测试集上对每个训练期进行评估,将测试集作为验证集。

# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, verbose=0)

    这里,verbose控制是否显示训练的过程。verbose=1为显示。由于训练过程很快,这里我们就使verbose=1。

    在运行结束时,我们将在训练和测试集上评估模型的性能。

# evaluate the model
_, 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))

    最后,我们将在训练数据集和验证数据集上绘制模型在每个训练时期的精度学习曲线。

# learning curves of model accuracy
pyplot.plot(history.history['acc'], label='train')
pyplot.plot(history.history['val_acc'], label='test')
pyplot.legend()
pyplot.show()

    将所有这些结合在一起,下面列出了完整的示例。

# develop an mlp for blobs dataset
from sklearn.datasets.samples_generator import make_blobs
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense
from matplotlib import pyplot
# generate 2d classification dataset
X, y = make_blobs(n_samples=1100, centers=3, n_features=2, cluster_std=2, random_state=2)
# one hot encode output variable
y = to_categorical(y)
# split into train and test
n_train = 100
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
print(trainX.shape, testX.shape)
# define model
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'])
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, verbose=0)
# evaluate the model
_, 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))
# learning curves of model accuracy
pyplot.plot(history.history['acc'], label='train')
pyplot.plot(history.history['val_acc'], label='test')
pyplot.legend()
pyplot.show()

    运行该示例首先打印每个数据集的形状以进行确认,然后在训练集和测试数据集上打印最终模型的性能。由于模型的高方差特性,您的特定结果将会(根据设计)发生变化。

    在这种情况下,我们可以看到模型在训练数据集上的准确率达到了85%左右,我们知道这是乐观的,在测试数据集上准确率达到了80%左右,我们希望这个模型更加真实。

    此外,还创建了一个线形图,显示了在每个训练时间内训练集和测试集的模型精度的学习曲线。我们可以看到,训练的准确性在大多数情况下更乐观,我们也注意到最后的分数。

如何利用Keras在深度神经网络中进行堆栈集成(Stacking Ensemble)_第4张图片

    现在,我们可以将该模型的实例用Stacking的一部分。

训练和保存子模型

    为了保持这个示例的简单性,我们将在Stacking中使用与级别0或子模型相同的模型的多个实例。我们还将使用一个预留集(holdout set)来训练集成中的一级或元学习者。一个更高级的例子可以使用不同类型的MLP模型(更深、更广等)作为子模型,并使用k-fold交叉验证来训练元学习者。

    在本节中,我们将训练多个子模型,并将它们保存到文件中,以供以后在Stacking使用。

    第一步是创建一个函数,该函数将在训练数据集上定义并适合MLP模型。

# fit model on dataset
def fit_model(trainX, trainy):
	# define model
	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'])
	# fit model
	model.fit(trainX, trainy, epochs=500, verbose=0)
	return model

    接下来,我们可以创建一个子目录来存储模型。注意,如果目录已经存在,在重新运行此代码时可能必须删除它。

# create directory for models
makedirs('models')

    最后,我们可以创建多个MLP实例,并将每个实例保存到具有唯一文件名的“models/”子目录中。在这种情况下,我们将创建5个子模型,但是您可以使用不同数量的模型进行试验,看看它如何影响模型性能。

# fit and save models
n_members = 5
for i in range(n_members):
	# fit model
	model = fit_model(trainX, trainy)
	# save model
	filename = 'models/model_' + str(i + 1) + '.h5'
	model.save(filename)
	print('>Saved %s' % filename)

    我们可以把所有这些元素联系起来;下面列出了训练子模型并将其保存到文件中的完整示例。

# example of saving sub-models for later use in a stacking ensemble
from sklearn.datasets.samples_generator 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

# fit model on dataset
def fit_model(trainX, trainy):
	# define model
	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'])
	# fit model
	model.fit(trainX, trainy, epochs=500, verbose=0)
	return model

# generate 2d classification dataset
X, y = make_blobs(n_samples=1100, centers=3, n_features=2, cluster_std=2, random_state=2)
# one hot encode output variable
y = to_categorical(y)
# split into train and test
n_train = 100
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
print(trainX.shape, testX.shape)
# create directory for models
makedirs('models')
# fit and save models
n_members = 5
for i in range(n_members):
	# fit model
	model = fit_model(trainX, trainy)
	# save model
	filename = 'models/model_' + str(i + 1) + '.h5'
	model.save(filename)
	print('>Saved %s' % filename)

    接下来,我们可以看看如何训练元学习机更好地利用这些子模型进行预测。

单独的Stacking模型

    我们现在可以训练一个元学习机,使他能够最好地结合子模型的预测,并且在理想情况下比任何单个子模型表现得更好。

    第一步是加载保存的模型。我们可以使用load_model() Keras函数并创建一个已加载模型的Python列表。

# load models from file
def load_all_models(n_models):
	all_models = list()
	for i in range(n_models):
		# define filename for this ensemble
		filename = 'models/model_' + str(i + 1) + '.h5'
		# load model from file
		model = load_model(filename)
		# add to list of members
		all_models.append(model)
		print('>loaded %s' % filename)
	return all_models

    我们可以调用这个函数来从“models/”子目录加载我们保存的五个模型。

# load all models
n_members = 5
members = load_all_models(n_members)
print('Loaded %d models' % len(members))

    了解单个模型在测试数据集上的性能有多好是很有用的,因为我们期望堆栈模型的性能更好。我们可以很容易地在训练数据集上评估每个单独的模型,并建立性能基线。

# evaluate standalone models on test dataset
for model in members:
	testy_enc = to_categorical(testy)
	_, acc = model.evaluate(testX, testy_enc, verbose=0)
	print('Model Accuracy: %.3f' % acc)

    接下来,我们可以训练元学习机。这需要两个步骤:

  • 为元学习者准备一个训练数据集。
  • 使用准备好的训练数据集来适应元学习机模型。

    我们将为元学习机准备一个训练数据集,提供从测试集到每个子模型的例子,并收集预测。在这种情况下,每个模型将为每个示例输出三个预测,预测给定示例属于这三个类中的每个类的概率。因此,测试集中的1000个示例将生成5个形状为[1000,3]的数组。

我们可以使用dstack() NumPy函数将这些数组组合成形状为[1000,5,3]的三维数组,该函数将堆叠每一组新的预测。

    作为新模型的输入,我们将需要具有一些特性的1,000个示例。假设我们有5个模型,每个模型对每个示例进行3次预测,那么我们将为提供给子模型的每个示例提供15 (3 x 5)个特性。我们可以将[1000,5,3]形状的预测从子模型转换为[1000,15]形状的数组,用于使用重塑()NumPy函数训练元学习者,并将最后两个维度扁平化。stacked_dataset() 函数的作用是实现这个步骤。

# create stacked model input dataset as outputs from the ensemble
def stacked_dataset(members, inputX):
	stackX = None
	for model in members:
		# make prediction
		yhat = model.predict(inputX, verbose=0)
		# stack predictions into [rows, members, probabilities]
		if stackX is None:
			stackX = yhat
		else:
			stackX = dstack((stackX, yhat))
	# flatten predictions to [rows, members x probabilities]
	stackX = stackX.reshape((stackX.shape[0], stackX.shape[1]*stackX.shape[2]))
	return stackX

    一旦准备好,我们就可以使用这个输入数据集以及测试集的输出(即y部分)来训练新的元学习者。在这种情况下,我们将从scikit-learn库中训练一个简单的逻辑回归算法。

    Logistic回归只支持二进制分类,尽管在LogisticRegression类中的scikit-learn中实现的Logistic回归支持使用one-vs-rest模式进行多类分类(两个以上的类)。下面的函数fit_stacked_model()将通过调用stacked_dataset()函数为元学习机准备训练数据集,然后拟合一个逻辑回归模型,然后返回该模型。

# fit a model based on the outputs from the ensemble members
def fit_stacked_model(members, inputX, inputy):
	# create dataset using ensemble
	stackedX = stacked_dataset(members, inputX)
	# fit standalone model
	model = LogisticRegression()
	model.fit(stackedX, inputy)
	return model

    我们可以调用这个函数并传入加载的模型列表和训练数据集。

# fit stacked model using the ensemble
model = fit_stacked_model(members, testX, testy)

    一旦加载,我们可以使用Stacking模型,包括成员和元学习者,对新数据进行预测。

    这可以通过首先使用子模型为元学习机生成输入数据集来实现,例如调用stacked_dataset()函数,然后使用元学习机进行预测。下面的stacked_prediction()函数实现了这一点。

# make a prediction with the stacked model
def stacked_prediction(members, model, inputX):
	# create dataset using ensemble
	stackedX = stacked_dataset(members, inputX)
	# make a prediction
	yhat = model.predict(stackedX)
	return yhat

    我们可以利用这个函数对新数据进行预测;在这种情况下,我们可以通过对测试集进行预测来证明它。

# evaluate model on test set
yhat = stacked_prediction(members, model, testX)
acc = accuracy_score(testy, yhat)
print('Stacked Test Accuracy: %.3f' % acc)

    将所有这些元素组合在一起,下面列出了为MLP子模型的Stacking拟合线性元学习者的完整示例。

# stacked generalization with linear meta model on blobs dataset
from sklearn.datasets.samples_generator 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

# load models from file
def load_all_models(n_models):
	all_models = list()
	for i in range(n_models):
		# define filename for this ensemble
		filename = 'models/model_' + str(i + 1) + '.h5'
		# load model from file
		model = load_model(filename)
		# add to list of members
		all_models.append(model)
		print('>loaded %s' % filename)
	return all_models

# create stacked model input dataset as outputs from the ensemble
def stacked_dataset(members, inputX):
	stackX = None
	for model in members:
		# make prediction
		yhat = model.predict(inputX, verbose=0)
		# stack predictions into [rows, members, probabilities]
		if stackX is None:
			stackX = yhat
		else:
			stackX = dstack((stackX, yhat))
	# flatten predictions to [rows, members x probabilities]
	stackX = stackX.reshape((stackX.shape[0], stackX.shape[1]*stackX.shape[2]))
	return stackX

# fit a model based on the outputs from the ensemble members
def fit_stacked_model(members, inputX, inputy):
	# create dataset using ensemble
	stackedX = stacked_dataset(members, inputX)
	# fit standalone model
	model = LogisticRegression()
	model.fit(stackedX, inputy)
	return model

# make a prediction with the stacked model
def stacked_prediction(members, model, inputX):
	# create dataset using ensemble
	stackedX = stacked_dataset(members, inputX)
	# make a prediction
	yhat = model.predict(stackedX)
	return yhat

# generate 2d classification dataset
X, y = make_blobs(n_samples=1100, centers=3, n_features=2, cluster_std=2, random_state=2)
# split into train and test
n_train = 100
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
print(trainX.shape, testX.shape)
# load all models
n_members = 5
members = load_all_models(n_members)
print('Loaded %d models' % len(members))
# evaluate standalone models on test dataset
for model in members:
	testy_enc = to_categorical(testy)
	_, acc = model.evaluate(testX, testy_enc, verbose=0)
	print('Model Accuracy: %.3f' % acc)
# fit stacked model using the ensemble
model = fit_stacked_model(members, testX, testy)
# evaluate model on test set
yhat = stacked_prediction(members, model, testX)
acc = accuracy_score(testy, yhat)
print('Stacked Test Accuracy: %.3f' % acc)

    运行该示例首先将子模型加载到一个列表中,并评估每个子模型的性能。我们可以看到,表现最好的模型是最终模型,准确率约为81.3%。由于神经网络学习算法的随机性,具体结果可能会有所不同。

如何利用Keras在深度神经网络中进行堆栈集成(Stacking Ensemble)_第5张图片

    然后,对测试集上每个子模型的预测概率进行logistic回归元学习机训练,然后在测试集上对整个叠加模型进行评估。我们可以看到,在这种情况下,元学习者的表现优于测试集中的每个子模型,达到了大约82.4%的准确率。

综合Stacking模型

    当使用神经网络作为子模型时,最好使用神经网络作为元学习机。

    具体地说,这些子网络可以嵌入到一个更大的多头神经网络中,然后学习如何最好地组合来自每个输入子模型的预测。它允许将堆栈集成视为单个大型模型。

    这种方法的好处是,子模型的输出直接提供给元学习机。此外,如果需要的话,还可以结合元学习模型更新子模型的权重。

    这可以通过使用Keras函数接口来开发模型来实现。将模型作为列表加载后,可以定义一个更大的堆栈集成模型,其中每个加载的模型都用作模型的单独输入头。这要求将每个已加载模型中的所有层都标记为不可训练,这样在训练新的较大模型时就无法更新权重。Keras还要求每个层都有唯一的名称,因此必须更新每个已加载模型中每个层的名称,以指示它们属于哪个集成成员。

# update all layers in all models to not be trainable
for i in range(len(members)):
	model = members[i]
	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
    model.summary()

    类似地,我们还是使用model.summary()来可视化内部的网络结构。这里由于篇幅原因,只截了两个模型结构。

如何利用Keras在深度神经网络中进行堆栈集成(Stacking Ensemble)_第6张图片

     一旦准备好子模型,我们就可以定义Stacking模型。每个子模型的输入层将用作这个新模型的单独输入头。这意味着任何输入数据的k个副本都必须提供给模型,其中k是输入模型的数量,在本例中是5。

    然后可以合并每个模型的输出。在这种情况下,我们将使用一个简单的串联合并,其中一个15个元素的向量将由5个模型各自预测的3个类概率创建。

    然后我们将定义一个隐藏层来解释这个“输入”给元学习者,以及一个输出层来做出它自己的概率预测。下面的define_stacked_model()函数实现了这一点,并将在给定一组训练过的子模型的情况下返回一个Stacking的泛化神经网络模型。

# define stacked model from multiple member input models
def define_stacked_model(members):
	# update all layers in all models to not be trainable
	for i in range(len(members)):
		model = members[i]
		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
	# define multi-headed input
	ensemble_visible = [model.input for model in members]
	# concatenate merge output from each model
	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 graph of ensemble
	plot_model(model, show_shapes=True, to_file='model_graph.png')
	# compile
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model

    当调用此函数时,将创建网络图的绘图,以了解集成模型如何配合在一起。

# define ensemble model
stacked_model = define_stacked_model(members)

    创建绘图需要安装pygraphviz。如果这在您的工作站上是一个挑战,您可以注释掉对plot_model()函数的调用。

如何利用Keras在深度神经网络中进行堆栈集成(Stacking Ensemble)_第7张图片

    一旦定义了模型,就可以进行拟合。我们可以将其直接安装在测试数据集上。

    由于子模型是不可训练的,在训练过程中不会更新它们的权值,只更新新的隐含层和输出层的权值。下面的fit_stacked_model()函数将适用于300个epochs的stacking神经网络模型。

# fit a stacked model
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)

    我们可以调用这个函数,提供已定义的stacking模型和测试数据集。

# fit stacked model on test dataset
fit_stacked_model(stacked_model, testX, testy)

    一旦拟合,我们可以使用新的stacking模型对新的数据进行预测。这与在模型上调用predict()函数一样简单。一个较小的变化是,我们需要为每个k个子模型向模型提供列表中输入数据的k个副本。下面的predict_stacked_model()函数简化了使用stacking模型进行预测的过程。

# make a prediction with a stacked model
def predict_stacked_model(model, inputX):
	# prepare input data
	X = [inputX for _ in range(len(model.input))]
	# make prediction
	return model.predict(X, verbose=0)

    我们可以调用这个函数对测试数据集进行预测并报告其准确性。我们期望神经网络学习者的性能比任何单独的子模型都好,并且可能与上一节使用的线性元学习机相竞争。

# make predictions and evaluate
yhat = predict_stacked_model(stacked_model, testX)
yhat = argmax(yhat, axis=1)
acc = accuracy_score(testy, yhat)
print('Stacked Test Accuracy: %.3f' % acc)

    将所有这些元素组合在一起,下面列出了完整的示例。

# stacked generalization with neural net meta model on blobs dataset
from sklearn.datasets.samples_generator 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

# load models from file
def load_all_models(n_models):
	all_models = list()
	for i in range(n_models):
		# define filename for this ensemble
		filename = 'models/model_' + str(i + 1) + '.h5'
		# load model from file
		model = load_model(filename)
		# add to list of members
		all_models.append(model)
		print('>loaded %s' % filename)
	return all_models

# define stacked model from multiple member input models
def define_stacked_model(members):
	# update all layers in all models to not be trainable
	for i in range(len(members)):
		model = members[i]
		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
	# define multi-headed input
	ensemble_visible = [model.input for model in members]
	# concatenate merge output from each model
	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 graph of ensemble
	plot_model(model, show_shapes=True, to_file='model_graph.png')
	# compile
	model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])
	return model

# fit a stacked model
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)

# make a prediction with a stacked model
def predict_stacked_model(model, inputX):
	# prepare input data
	X = [inputX for _ in range(len(model.input))]
	# make prediction
	return model.predict(X, verbose=0)

# generate 2d classification dataset
X, y = make_blobs(n_samples=1100, centers=3, n_features=2, cluster_std=2, random_state=2)
# split into train and test
n_train = 100
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
print(trainX.shape, testX.shape)
# load all models
n_members = 5
members = load_all_models(n_members)
print('Loaded %d models' % len(members))
# define ensemble model
stacked_model = define_stacked_model(members)
# fit stacked model on test dataset
fit_stacked_model(stacked_model, testX, testy)
# make predictions and evaluate
yhat = predict_stacked_model(stacked_model, testX)
yhat = argmax(yhat, axis=1)
acc = accuracy_score(testy, yhat)
print('Stacked Test Accuracy: %.3f' % acc)

    运行该示例首先加载5个子模型。在试验数据集上定义了一个更大的叠加集成神经网络,并对其进行拟合,利用新模型对试验数据集进行预测。可以看出,在本例中,模型的准确率达到了83.3%左右,优于上一节的线性模型。

如何利用Keras在深度神经网络中进行堆栈集成(Stacking Ensemble)_第8张图片

拓展

    本节列出了一些扩展教程的想法,您可能希望对其进行研究。

  • Alternate Meta-Learner:更新示例以使用替代的元学习者分类器模型到逻辑回归模型。
  • Single Level 0 Model:更新示例以使用单个level-0模型并比较结果。
  • Vary Level 0 Models:开展一项研究,证明测试分类精度与堆叠集成中使用的子模型数量之间的关系。
  • Cross-Validation Stacking Ensemble.:更新示例以使用k-fold交叉验证为元学习者模型准备训练数据集。
  • Use Raw Input in Meta-Learner:更新示例,以便元学习算法获取示例的原始输入数据以及子模型的输出,并比较性能。

进一步的阅读

    如果您想深入了解这个主题,本节将提供更多的资源。

Books

  • Section 8.8 Model Averaging and Stacking, The Elements of Statistical Learning: Data Mining, Inference, and Prediction, Second Edition, 2016.
  • Section 7.5 Combining multiple models, Data Mining: Practical Machine Learning Tools and Techniques, Second Edition, 2005.
  • Section 9.8.2 Stacked Generalization, Neural Networks for Pattern Recognition, 1995.

Papers

  • Stacked generalization, 1992.
  • Issues in Stacked Generalization, 1999.

API

  • Getting started with the Keras Sequential model
  • Keras Core Layers API
  • numpy.argmax API
  • sklearn.datasets.make_blobs API
  • numpy.dstack API
  • sklearn.linear_model.LogisticRegression API

Articles

  • Stacked Generalization (Stacking) Bibliography.
  • Ensemble learning, Wikipedia.

Posts

  • How to Implement Stacked Generalization From Scratch With Python

总结

    在本教程中,您了解了如何为深度学习神经网络开发一个Stacking。具体来说,你学会了:

  • Stacking是一种集成方法,新模型学习如何最好地结合多个现有模型的预测。
  • 如何以神经网络为子模型,以scikit-learn分类器为元学习机,建立Stacking模型。
  • 如何将神经网络的子模型嵌入到更大的Stacking模型中进行训练和预测。

你可能感兴趣的:(deep,learning,深度学习入门)