摘要: 深度學習小技巧,約束權重以下降模型過擬合的可能,附keras實現代碼。html
在深度學習中,批量歸一化(batch normalization)以及對損失函數加一些正則項這兩類方法,通常能夠提高模型的性能。這兩類方法基本上都屬於權重約束,用於減小深度學習神經網絡模型對訓練數據的過擬合,並改善模型對新數據的性能。
目前,存在多種類型的權重約束方法,例如最大化或單位向量歸一化,有些方法也必須須要配置超參數。
在本教程中,使用Keras API,用於向深度學習神經網絡模型添加權重約束以減小過擬合。
完成本教程後,您將瞭解:git
下面,讓咱們開始吧。github
本教程分爲三個部分:算法
Keras API支持權重約束,且約束能夠按每層指定。
使用約束一般涉及在圖層上爲輸入權重設置kernel_constraint
參數,誤差權重設置爲bias_constraint
。一般,權重約束方法不涉及誤差權重。
一組不一樣的向量規範在keras.constraints
模塊能夠用做約束:網絡
例如,能夠導入和實例化約束:dom
# import norm from keras.constraints import max_norm # instantiate norm norm = max_norm(3.0)
權重規範可用於Keras的大多數層,下面介紹一些常見的例子:機器學習
下面的示例是在全鏈接層上設置最大範數權重約束:函數
# example of max norm on a dense layer from keras.layers import Dense from keras.constraints import max_norm ... model.add(Dense(32, kernel_constraint=max_norm(3), bias_constraint==max_norm(3))) ...
下面的示例是在卷積層上設置最大範數權重約束:性能
# example of max norm on a cnn layer from keras.layers import Conv2D from keras.constraints import max_norm ... model.add(Conv2D(32, (3,3), kernel_constraint=max_norm(3), bias_constraint==max_norm(3))) ...
與其餘圖層類型不一樣,遞歸神經網絡容許咱們對輸入權重和誤差以及循環輸入權重設置權重約束。經過圖層的recurrent_constraint
參數設置遞歸權重的約束。
下面的示例是在LSTM圖層上設置最大範數權重約束:學習
# example of max norm on an lstm layer from keras.layers import LSTM from keras.constraints import max_norm ... model.add(LSTM(32, kernel_constraint=max_norm(3), recurrent_constraint=max_norm(3), bias_constraint==max_norm(3))) ...
基於以上的基本知識,下面進行實例實踐。
在本節中,將演示如何使用權重約束來減小MLP對簡單二元分類問題的過擬合問題。
此示例只是提供了一個模板,讀者能夠觸類旁通,將權重約束應用於本身的神經網絡以進行分類和迴歸問題。
使用標準二進制分類問題來定義兩個半圓觀察,每一個類一個半圓。其中,每一個觀測值都有兩個輸入變量,它們具備相同的比例,輸出值分別爲0或1,該數據集也被稱爲「 月亮」數據集,這是因爲繪製時,每一個類中出現組成的形狀相似於月亮。
可使用make_moons()
函數生成觀察結果,設置參數爲添加噪聲、隨機關閉,以便每次運行代碼時生成相同的樣本。
# generate 2d classification dataset X, y = make_moons(n_samples=100, noise=0.2, random_state=1)
能夠在圖表上繪製兩個變量x和y座標,並將數據點所屬的類別的顏色做爲觀察的顏色。
下面列出生成數據集並繪製數據集的完整示例:
# generate two moons dataset from sklearn.datasets import make_moons from matplotlib import pyplot from pandas import DataFrame # generate 2d classification dataset X, y = make_moons(n_samples=100, noise=0.2, 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模型能夠解決這類二進制分類問題。
MLP模型只具備一個隱藏層,但具備比解決該問題所需的節點更多的節點,從而提供過擬合的可能。
在定義模型以前,須要將數據集拆分爲訓練集和測試集,按照3:7的比例將數據集劃分爲訓練集和測試集。
# generate 2d classification dataset X, y = make_moons(n_samples=100, noise=0.2, 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、激活函數爲RELU,但在輸出層中使用Sigmoid激活函數以預測輸出類別爲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'])
而後,設置迭代次數爲4,000次,默認批量訓練樣本數量爲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 moons dataset from sklearn.datasets import make_moons from keras.layers import Dense from keras.models import Sequential from matplotlib import pyplot # generate 2d classification dataset X, y = make_moons(n_samples=100, noise=0.2, 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.914
建立一個圖,顯示訓練和測試集上模型精度的線圖。從圖中能夠看到模型過擬合時的預期形狀,其中測試精度達到一個臨界點後再次開始減少。
爲了和上面作對比,如今對MLP使用權重約束。目前,有一些不一樣的權重約束方法可供選擇。本文選用一個簡單且好用的約束——簡單地標準化權重,使得其範數等於1.0,此約束具備強制全部傳入權重變小的效果。
在Keras中能夠經過使用unit_norm
來實現,而且將此約束添加到第一個隱藏層,以下所示:
model.add(Dense(500, input_dim=2, activation='relu', kernel_constraint=unit_norm()))
此外,也能夠經過使用min_max_norm
並將min
和maximum
設置爲1.0 來實現相同的結果,例如:
model.add(Dense(500, input_dim=2, activation='relu', kernel_constraint=min_max_norm(min_value=1.0, max_value=1.0)))
可是沒法經過最大範數約束得到相同的結果,由於它容許規範等於或低於指定的限制; 例如:
model.add(Dense(500, input_dim=2, activation='relu', kernel_constraint=max_norm(1.0)))
下面列出具備單位規範約束的完整代碼:
# mlp overfit on the moons dataset with a unit norm constraint from sklearn.datasets import make_moons from keras.layers import Dense from keras.models import Sequential from keras.constraints import unit_norm from matplotlib import pyplot # generate 2d classification dataset X, y = make_moons(n_samples=100, noise=0.2, 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', kernel_constraint=unit_norm())) 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.943
從訓練和測試精度曲線圖來看,模型已經在訓練數據集上再也不過擬合了,且模型在訓練和測試數據集的精度保持在一個穩定的水平。
本節列出了一些讀者可能但願探索擴展的教程:
若是想進一步深刻了解,下面提供一些有關該主題的其它資源: