如何利用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方法詳解

    然後將預測作爲訓練元學習機的輸入。然後對整個訓練數據集進行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的標準偏差意味着類不是線性可分的(由直線可分),這導致了許多不明確的點。這是可取的,因爲這意味着問題不是微不足道的,並將允許神經網絡模型找到許多不同的“足夠好”的候選解決方案,從而導致高方差。

 多層感知器模型

    在定義模型之前,我們需要設計一個適合於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的股價預測方法

    我們使用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%左右,我們希望這個模型更加真實。

    此外,還創建了一個線形圖,顯示了在每個訓練時間內訓練集和測試集的模型精度的學習曲線。我們可以看到,訓練的準確性在大多數情況下更樂觀,我們也注意到最後的分數。

    現在,我們可以將該模型的實例用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%。由於神經網絡學習算法的隨機性,具體結果可能會有所不同。

    然後,對測試集上每個子模型的預測概率進行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()來可視化內部的網絡結構。這裏由於篇幅原因,只截了兩個模型結構。

     一旦準備好子模型,我們就可以定義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()函數的調用。

    一旦定義了模型,就可以進行擬合。我們可以將其直接安裝在測試數據集上。

    由於子模型是不可訓練的,在訓練過程中不會更新它們的權值,只更新新的隱含層和輸出層的權值。下面的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%左右,優於上一節的線性模型。

拓展

    本節列出了一些擴展教程的想法,您可能希望對其進行研究。

  • 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

Papers

API

Articles

Posts

總結

    在本教程中,您瞭解瞭如何爲深度學習神經網絡開發一個Stacking。具體來說,你學會了:

  • Stacking是一種集成方法,新模型學習如何最好地結合多個現有模型的預測。
  • 如何以神經網絡爲子模型,以scikit-learn分類器爲元學習機,建立Stacking模型。
  • 如何將神經網絡的子模型嵌入到更大的Stacking模型中進行訓練和預測。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章