利用Keras中的權重約束減少深度神經網絡中的過擬合
權重約束提供了一種方法,用于減少深度學習神經網絡模型對訓練數據的過度擬合,并改善模型對新數據(例如測試集)的性能。有多種類型的權重約束,例如最大和單位向量規范,有些需要必須配置的超參數。
在本教程中,您將發現Keras API,用于向深度學習神經網絡模型添加權重約束以減少過度擬合。
完成本教程后,您將了解:
- 如何使用Keras API創建向量范數約束。
- 如何使用Keras API為MLP,CNN和RNN層添加權重約束。
- 如何通過向現有模型添加權重約束來減少過度擬合。
教程概述
本教程分為三個部分,他們是:
- Keras的重量約束
- 圖層上的權重約束
- 體重約束案例研究
Keras的重量約束
Keras API支持權重限制。約束是按層指定的,但是在層中應用和強制執行每個節點。
使用約束通常涉及在圖層上為輸入權重設置kernel_constraint參數,并為偏差權重設置bias_constraint。
通常,權重約束不用于偏差權重。一組不同的向量規范可以用作約束,作為keras.constraints模塊中的類提供。他們是:
- 最大范數(max_norm),用于強制權重等于或低于給定限制。
- 非負規范(non_neg),強制權重具有正數。
- 單位范數(unit_norm),強制權重為1.0。
- Min-Max范數(min_max_norm),用于強制權重在一個范圍之間。
例如,一個簡單的約束可以這樣被引入和實例化:
- # import norm
- from keras.constraints import max_norm
- # instantiate norm
- norm = max_norm(3.0)
- # import norm
- from keras.constraints import max_norm
- # instantiate norm
- norm = max_norm(3.0)
圖層上的權重約束
權重規范可用于Keras的大多數層。在本節中,我們將看一些常見的例子。
MLP加權約束
以下示例在密集完全連接層上設置最大范數權重約束。
- # 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 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)))
- ...
CNN加權約束
下面的示例在卷積層上設置最大范數權重約束。
- # 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)))
- ...
RNN權重約束
與其他圖層類型不同,遞歸神經網絡允許您對輸入權重和偏差以及循環輸入權重設置權重約束。通過圖層的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)))
- ...
- # 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)))
- ...
現在我們知道如何使用權重約束API,讓我們看一個有效的例子。
加權約束案例研究
在本節中,我們將演示如何使用權重約束來減少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(axax=ax, kind='scatter', x='x', y='y', label=key, color=colors[key])
- pyplot.show()
- # 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(axax=ax, kind='scatter', x='x', y='y', label=key, color=colors[key])
- pyplot.show()
運行該示例會創建一個散點圖,顯示每個類中觀察的半圓形或月亮形狀。我們可以看到點的分散中的噪音使得衛星不太明顯。
這是一個很好的測試問題,因為類不能用一行來分隔,例如不是線性可分的,需要非線性方法,如神經網絡來解決。我們只生成了100個樣本,這對于神經網絡而言很小,提供了過度擬合訓練數據集的機會,并且在測試數據集上具有更高的誤差:使用正則化的一個好例子。此外,樣本具有噪聲,使模型有機會學習不一致的樣本的各個方面。
過度多層感知器
我們可以開發一個MLP模型來解決這個二進制分類問題。該模型將具有一個隱藏層,其具有比解決該問題所需的節點更多的節點,從而提供過度擬合的機會。我們還將訓練模型的時間超過確保模型過度所需的時間。在我們定義模型之前,我們將數據集拆分為訓練集和測試集,使用30個示例來訓練模型,使用70個示例來評估擬合模型的性能。
- 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個節點和整流的線性激活函數。在輸出層中使用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'])
然后,定義的模型擬合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
創建一個圖,顯示訓練集和測試集上模型精度的曲線圖。我們可以看到過度擬合模型的預期形狀,其中測試精度增加到一個點然后再次開始減小。
具有加權約束的Overfit MLP
我們可以更新示例以使用權重約束。有一些不同的加權限制可供選擇。這個模型的一個很好的簡單約束是簡單地標準化加權,使得范數等于1.0。此約束具有強制所有傳入權重較小的效果。我們可以通過在Keras中使用unit_norm來實現??梢詫⒋思s束添加到第一個隱藏層,如下所示:
- 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
回顧訓練集的曲線和測試精度,我們可以看到模型已經過度擬合訓練數據集了。訓練集和測試集的模型精度繼續提高到穩定水平。
擴展
本節列出了一些擴展您可能希望探索的教程的想法。
- 報告加權標準。更新示例以計算網絡權重的大小,并證明約束確實使得幅度更小。
- 約束輸出層。更新示例以將約束添加到模型的輸出層并比較結果。
- 約束偏差。更新示例以向偏差權重添加約束并比較結果。
- 反復評估。更新示例以多次擬合和評估模型,并報告模型性能的均值和標準差。