【Keras】減少過擬合的祕訣——Dropout正則化

摘要: Dropout正則化是最簡單的神經網絡正則化方法。閱讀完本文,你就學會了在Keras框架中,如何將深度學習神經網絡Dropout正則化添加到深度學習神經網絡模型裏。

 Dropout正則化是最簡單的神經網絡正則化方法。其原理非常簡單粗暴:任意丟棄神經網絡層中的輸入,該層可以是數據樣本中的輸入變量或來自先前層的激活。它能夠模擬具有大量不同網絡結構的神經網絡,並且反過來使網絡中的節點更具有魯棒性。

閱讀完本文,你就學會了在Keras框架中,如何將深度學習神經網絡Dropout正則化添加到深度學習神經網絡模型裏,具體內容如下:如何使用Keras API創建Dropout層;如何使用Keras API將Dropout正則化添加到MLP、CNN和RNN層;在現有模型中,如何使用Dropout正則化減少過擬合。

Keras中的Dopout正則化

在Keras深度學習框架中,我們可以使用Dopout正則化,其最簡單的Dopout形式是Dropout核心層。

在創建Dopout正則化時,可以將 dropout rate的設爲某一固定值,當dropout rate=0.8時,實際上,保留概率爲0.2。下面的例子中,dropout rate=0.5。

layer = Dropout(0.5)

Dropout層

將Dropout層添加到模型的現有層和之前的輸出層之間,神經網絡將這些輸出反饋到後續層中。用dense()方法指定兩個全連接網絡層:

...
model.append(Dense(32))
model.append(Dense(32))
...

在這兩層中間插入一個dropout層,這樣一來,第一層的輸出將對第二層實現Dropout正則化,後續層與此類似。現在,我們對第二層實現了Dropout正則化。

...
model.append(Dense(32))
model.append(Dropout(0.5))
model.append(Dense(32))
...

Dropout也可用於可見層,如神經網絡的輸入。在這種情況下,就要把Dropout層作爲網絡的第一層,並將input_shape參數添加到層中,來制定預期輸入。

...
model.add(Dropout(0.5, input_shape=(2,)))
...

下面,我們來看看Dropout正則化如何與常見的網絡類型一起使用。

MLP Dropout正則化

在兩個全連接層之間添加Dropout正則化,代碼如下所示:

# example of dropout between fully connected layers
from keras.layers import Dense
from keras.layers import Dropout
...
model.add(Dense(32))
model.add(Dropout(0.5))
model.add(Dense(1))
...

CNN Dropout正則化

我們可以在卷積層和池化層後使用Dropout正則化。一般來說,Dropout僅在池化層後使用。

# example of dropout for a CNN
from keras.layers import Dense
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import Dropout
...
model.add(Conv2D(32, (3,3)))
model.add(Conv2D(32, (3,3)))
model.add(MaxPooling2D())
model.add(Dropout(0.5))
model.add(Dense(1))
...

在這種情況下,我們要將Dropout應用於特徵圖的每個單元中。

在卷積神經網絡中使用Dropout正則化的另一個方法是,將卷積層中的整個特徵圖都丟棄,然後在池化期間也不再使用。這種方法稱爲空間丟棄,即Spatial Dropout。

“我們創建了一個新的Dropout正則化方法,我們將其稱爲Spatial Dropout。在這個方法中,我們將Dropout值擴展到整個特徵映射中。”

——《使用卷積神經網絡有效的進行對象本地化,2015》

在Keras中,通過SpatialDropout2D層提供Spatial Dropout正則化。

# example of spatial dropout for a CNN
from keras.layers import Dense
from keras.layers import Conv2D
from keras.layers import MaxPooling2D
from keras.layers import SpatialDropout2D
...
model.add(Conv2D(32, (3,3)))
model.add(Conv2D(32, (3,3)))
model.add(SpatialDropout2D(0.5))
model.add(MaxPooling2D())
model.add(Dense(1))
...

RNN Dropout正則化

我們在LSTM循環層和全連接層之間使用Dropout正則化,代碼如下所示:

# example of dropout between LSTM and fully connected layers
from keras.layers import Dense
from keras.layers import LSTM
from keras.layers import Dropout
...
model.add(LSTM(32))
model.add(Dropout(0.5))
model.add(Dense(1))
...

在這裏,將Dropout應用於LSTM層的32個輸出中,這樣,LSTM層就作爲全連接層的輸入。

還有一種方法可以將Dropout與LSTM之類的循環層一起使用。LSTM可以將相同的Dropout掩碼用於所有的輸入中。這個方法也可用於跨樣本時間步長的循環輸入連接。這種使用遞歸模型進行Dropout正則化則稱爲變分循環神經網絡(Variational RNN)。

“變分循環神經網絡在每個時間步長使用相同的Dropout掩碼,包括循環層。這與在RNN中實現Dropout正則化一樣,在每個時間步長丟棄相同的神經網絡單元,並且隨意的丟棄輸入、輸出和循環連接。這和現有的技術形成對比,在現有的技術中,不同的神經網絡單元將在不同的時間步長被丟棄,並且不會對全連接層進行丟棄。”

——《循環神經網絡中Dropout的基礎應用,2016》

Keras通過循環層上的兩個參數來支持變分神經網絡(輸入和循環輸入樣本時間步長的一致性丟棄),這稱爲 輸入“Dropout”和循環輸入的“recurrent_dropout”。

# example of dropout between LSTM and fully connected layers
from keras.layers import Dense
from keras.layers import LSTM
from keras.layers import Dropout
...
model.add(LSTM(32))
model.add(Dropout(0.5))
model.add(Dense(1))
...

Dropout正則化案例

在本節中,我們將演示如何使用Dropout正則化來減少MLP在簡單二元分類問題上的過擬合。在這裏,我們提供了一個在神經網絡上應用Dropout正則化的模板,你也可以將其用於分類和迴歸問題。

二元分類問題

在這裏,我們使用一個標準的二元分類問題,即定義兩個二維同心圓,每個類爲一個圓。

每個觀測值都有兩個輸入變量,它們具有相同的比例,類輸出值爲0或1。這個數據集就是 “圓”數據集。

我們可以使用make_circles()方法生成觀測結果。我們爲數據添加噪聲和隨機數生成器,以防每次運行代碼時使用相同的樣本。

# generate 2d classification dataset
X, y = make_circles(n_samples=100, noise=0.1, random_state=1)

我們可以用x和y座標繪製一個數據集,並將觀察到的顏色定義爲類值。生成和繪製數據集的代碼如下:

# generate two circles dataset
from sklearn.datasets import make_circles
from matplotlib import pyplot
from pandas import DataFrame
# generate 2d classification dataset
X, y = make_circles(n_samples=100, noise=0.1, random_state=1)
# scatter plot, dots colored by class value
df = DataFrame(dict(x=X[:,0], y=X[:,1], label=y))
colors = {0:'red', 1:'blue'}
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()

運行以上代碼,會創建一個散點圖,散點圖展示每個類中觀察到的同心圓形狀。我們可以看到,因爲噪聲,圓圈並不明顯。

這是一個特別好的測試問題,因爲類不可能用一條直線表示,比如它不是線性可微分的,在這種情況下,就需要使用非線性方法來解決,比如神經網絡。

在這裏,我們只生成了100個樣本,這對於神經網絡來說,樣本是相當少了。但是它提供了訓練數據集的過擬合現象,並且在測試數據及上的誤差更大:這是使用正則化的一個特別好的例子。除此之外,這個樣本集中有噪聲,這就使神經網絡模型有機會學習不一致樣本的各個方面。

多層感知器的過擬合

我們可以創建一個MLP模型來解決這個二元分類問題。

該模型將具有一個隱藏層,它的節點比解決該問題所需節點要多得多,從而產生過擬合。另外,我們訓練模型的時間也大大超過正常訓練模型所需要的時間。

在定義模型之前,我們將數據集拆分爲訓練集和測試集:30個訓練數據來訓練模型和70個測試數據來評估擬合模型性能。

# generate 2d classification dataset
X, y = make_circles(n_samples=100, noise=0.1, random_state=1)
# split into train and test
n_train = 30
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]

接下來,我們可以定義模型。

在隱藏層中使用500個節點和矯正過得線性激活函數;在輸出層中使用S型激活函數預測類的值(0或1)。

該模型使用二元交叉熵損失函數進行優化,這個函數適用於二元分類問題和梯度下降到有效Adam問題。

# define model
model = Sequential()
model.add(Dense(500, input_dim=2, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

將訓練數據訓練4000次,默認每次訓練次數爲32。 然後用測試數據集驗證該模型性能,代碼如下。

# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, 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))

最後,在每次訓練的時候繪製模型的性能。

如果模型在訓練數據集時的確是過擬合,那麼我們訓練集上的準確度線圖更加準確,並且準確度隨着模型學習訓練數據集中的統計噪聲而再次下降。

# plot history
pyplot.plot(history.history['acc'], label='train')
pyplot.plot(history.history['val_acc'], label='test')
pyplot.legend()
pyplot.show()

將以上所有代碼組合起來,如下所示。

# mlp overfit on the two circles dataset
from sklearn.datasets import make_circles
from keras.layers import Dense
from keras.models import Sequential
from matplotlib import pyplot
# generate 2d classification dataset
X, y = make_circles(n_samples=100, noise=0.1, random_state=1)
# split into train and test
n_train = 30
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
model.add(Dense(500, input_dim=2, activation='relu'))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, 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))
# plot history
pyplot.plot(history.history['acc'], label='train')
pyplot.plot(history.history['val_acc'], label='test')
pyplot.legend()
pyplot.show()

運行以上代碼,我們可以看到模型在訓練和測試數據集上的性能:模型在訓練數據集上的性能優於測試數據集,這是過度擬合的一個可能標誌。

鑑於神經網絡和訓練算法的隨機性,模型的測試結果可能會有所不同。由於該模型嚴重過擬合,該模型在同一數據集上運行的結果差異並不會很大。

Train: 1.000, Test: 0.757

下圖爲模型在訓練和測試集上的精度圖,我們可以看到過擬合模型的預期性能,其中測試精度增加到一定值以後,再次開始減小。

使用Dropout正則化減少MLP過擬合

我們使用Dropout正則化更新這個示例,即在隱藏層和輸出層之間插入一個新的Dropout層來實現。在這裏,指定Dropout rate=0.4。

# define model
model = Sequential()
model.add(Dense(500, input_dim=2, activation='relu'))
model.add(Dropout(0.4))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])

下面列出了隱藏層後添加了dropout層的完整更新示例。

# mlp with dropout on the two circles dataset
from sklearn.datasets import make_circles
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Dropout
from matplotlib import pyplot
# generate 2d classification dataset
X, y = make_circles(n_samples=100, noise=0.1, random_state=1)
# split into train and test
n_train = 30
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
model.add(Dense(500, input_dim=2, activation='relu'))
model.add(Dropout(0.4))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=4000, 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))
# plot history
pyplot.plot(history.history['acc'], label='train')
pyplot.plot(history.history['val_acc'], label='test')
pyplot.legend()
pyplot.show()

運行以上代碼,查看模型在訓練和測試集上的性能。你所得到的結果可能會有所不同,在這種情況下,該模型具有較高的方差。在這裏,我們可以看到,Dropout導致訓練集的準確度有所下降,從100%降至96%,而測試集的準確度從75%提高到81%。

Train: 0.967, Test: 0.814

從這裏我們可以看出,該模型已經不再適合訓練數據集了。

儘管使用Dropout正則化時會產生很多噪音,訓練數據集和測試數據集的模型精度持續增加。

在後續學習中,你可以進一步探索以下這些問題:

1.輸入Dropout。在輸入變量上使用Dropout正則化,更新示例,並比較結果。

2.權重約束。在隱藏層添加max-norm權重約束,更新示例,並比較結果。

3.反覆評估。更新示例,重複評估過擬合和Dropout模型,總結並比較平均結果。

4.網格搜索率。創建Dropout概率的網格搜索,並報告Dropout rate和測試數據集準確度二者之間的關係。

拓展閱讀

論文

1.《使用卷積神經網絡進行高效的對象本地化,2015》

2.《遞歸神經網絡中的理論Dropout應用,2016》

博文

1.基於Keras深度學習模型中的Dropout正則化

2.如何使用LSTM網絡的Dropout進行時間序列預測

API

1.Keras Regularizers API

2.Keras Core Layers API

3.Keras Convolutional Layers API

4.Keras Recurrent Layers API

5.sklearn.datasets.make_circles API

總結

閱讀完本文,你已經瞭解瞭如何將深度學習正則化添加到深度學習神經網絡模型的API中。具體來說,有以下幾個內容:

1.如何使用Keras API創建Dropout層。

2.如何使用Keras API將Dropout正則化添加到MLP、CNN和RNN層。

3.如何向現有模型中添加Dropout正則化,以此減少過擬合。



本文作者:【方向】

閱讀原文

本文爲雲棲社區原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章