【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》
博文
API
3.Keras Convolutional Layers API
5.sklearn.datasets.make_circles API
總結
閱讀完本文,你已經瞭解瞭如何將深度學習正則化新增到深度學習神經網路模型的API中。具體來說,有以下幾個內容:
1.如何使用Keras API建立Dropout層。
2.如何使用Keras API將Dropout正則化新增到MLP、CNN和RNN層。
3.如何向現有模型中新增Dropout正則化,以此減少過擬合。
本文由北郵@愛可可-愛生活 老師推薦,阿里云云棲社群組織翻譯。
文章原標題《How to Reduce Overfitting With Dropout Regularization in Keras》
譯者:Mags,審校:袁虎。
文章為簡譯,更為詳細的內容,請檢視原文