用Keras中的權值約束緩解過擬合

张倩發表於2018-12-10

權值約束為緩解深度學習神經網路模型對訓練資料的過擬合、提高模型在新資料上的效能提供了一種方法。目前有多種型別的權值約束方式,比如最大向量範數和單位向量範數,其中有些方法要求使用者必須配置引數。在本教程中,作者介紹了向深度學習神經網路模型加入權值約束以緩解過擬合的 Keras API。

本教程將幫你掌握:

  • 如何使用 Keras API 建立向量範數約束。

  • 如何使用 Keras API 向多層感知機(MLP)、卷積神經網路(CNN)以及迴圈神經網路(RNN)層加入權值約束。

  • 如何透過向一個現有的模型新增權值約束來緩解過擬合

用Keras中的權值約束緩解過擬合

如何使用 Keras 中的權值約束緩解深度神經網路中的過擬合現象(圖源:https://www.flickr.com/photos/31246066@N04/5907974408/)

教程大綱

本教程分為三個部分:

1. Keras 中的權值約束

2. 神經網路層上的權值約束

3. 權值約束的案例分析

Keras 中的權值約束

Keras API 支援權值約束技術。這樣的權值約束是逐層指定的,但是需要在層中的每一個節點應用並執行。使用權值約束的方法通常包括在層上為輸入權值設定「kernel_constraint」引數,以及為偏置的權值設定「bias_constraint」。一般來說,權值約束不會用於偏置的權重。我們可以使用一組不同的向量範數作為權值約束,Keras 在「keras.constraints module」中給出了這些方法:

  • 最大範數(max_norm),限制權值的大小不超過某個給定的極限。

  • 非負範數(non_neg),限制權值為正。

  • 單位範數(unit_form),限制權值大小為 1.0。

  • 最小最大範數(min_max_norm),限制權值大小在某個範圍內。

例如,一個權值約束可以透過下面的方式被引入並例項化

# import norm
from keras.constraints import max_norm
# instantiate norm
norm = max_norm(3.0)
Weight Constraints on Layers

神經網路層上的權值約束

在 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)))
...

相信讀到這裡,讀者已經知道如何使用權值約束 API 了。下面將為讀者展示一個有效的案例。

權值約束案例分析

在本章中,我們將展示如何在一個簡單的二分類問題上使用權值約束緩解一個多層感知機的過擬合現象。

下面的例子給出了一個將權值約束應用到用於分類和迴歸問題的神經網路的模板。

  • 分類問題

本文使用了一個標準的二分類問題,它定義了兩個半圓的觀測資料,每一個半圓對應一個類。每個觀測資料都有兩個相同規模的輸入變數和一個 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()

執行該示例程式碼將建立一個散點圖,該圖會顯示出每個類的半圓或月牙形的觀測資料。我們可以看到這些散開的噪聲使得月牙形顯得沒有那麼明顯了。

用Keras中的權值約束緩解過擬合

月牙形資料集的散點圖,圖中不同的顏色表示每個樣本的類值

這是一個很好的測試問題,因為這樣的類不能透過一條直線來分割,即線性不可分的情況,我們需要透過諸如神經網路這樣的非線性方法來解決這個問題。

我們僅僅生成了 100 個樣本,這樣的樣本量對於一個神經網路來說是很小的,但這恰好為在訓練資料集上發生過擬合提供了機會,會在測試資料集上出現更高的誤差:

這是一個使用正則化技術的絕佳場合。此外,樣本中帶有噪聲,這讓該模型有機會學習到它不能夠泛化到的樣本的一些特徵。

  • 過擬合的多層感知機

我們可以開發一個多層感知機模型來解決這個二分類問題。該模型將含有一個隱藏層,其中包含的節點比解決這個問題本身所需的節點要多一些,從而為過擬合提供了機會。我們還將對模型進行較長時間的訓練,以確保模型過擬合。在定義模型之前,我們將把資料集分為訓練集和測試集,使用 30 個示例訓練模型,70 個示例評估擬合模型的效能。

# 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'])

接著,上面定義的模型將在訓練資料上進行 4000 個 epoch 的擬合,預設批大小為 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))

最後,我們將繪製模型在每個 epoch 中在訓練集和測試集上的效能曲線。如果模型確實過擬合了訓練資料集,隨著模型在訓練資料集中學習到統計噪聲,我們希望訓練集的準確率直線圖會持續遞增,而測試集的準確率曲線則會先上升,然後再次下降。

# 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

在訓練和測試集上建立的顯示模型準確率的折線圖。

我們可以看到預期的過擬合模型的形狀,它的準確率會增加到一個點,然後又開始下降。

用Keras中的權值約束緩解過擬合

  • 帶權值約束的過擬合多層感知機

我們可以進一步更新使用權值約束的示例。有幾種不同的權值約束方式可供選擇。對於該模型來說,一個很好的簡單約束方式就是直接歸一化權值,使範數等於 1.0。這個約束的作用是迫使所有傳入的權值都很小。我們可以透過使用 Keras 中的「unit_norm」來實現這一點。該約束可以透過下面的方式新增到第一個隱藏層中:

model.add(Dense(500, input_dim=2, activation='relu', kernel_constraint=unit_norm()))

我們還可以透過使用「min_max_norm」並將最小值和最大值都設為 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

仔細觀察訓練和測試的準確率的折線圖,我們可以看到,模型不再出現過擬合訓練資料集的情況。

在訓練集和測試集上的模型的準確率繼續上升到一個穩定的水平。

用Keras中的權值約束緩解過擬合

擴充套件

本章列舉出了一些擴充套件內容:

  • 顯示出權值範數。更新示例以計算所處網路權值的大小,並說明權值約束確實能讓權值更小。

  • 約束輸出層。更新示例,向模型的輸出層新增約束並比較結果。

  • 約束偏置。更新示例,從而向偏差權值新增約束並比較結果。

  • 多次評價。更新示例,從而對模型進行多次擬合和評價,並顯示出模型效能的均值和標準差。

API

  • Keras Constraints API:https://keras.io/constraints/

  • Keras constraints.py:https://github.com/keras-team/keras/blob/master/keras/constraints.py

  • Keras Core Layers API:https://keras.io/layers/core/

  • Keras Convolutional Layers API:https://keras.io/layers/convolutional/

  • Keras Recurrent Layers API:https://keras.io/layers/recurrent/

  • sklearn.datasets.make_moons API:http://scikit-learn.org/stable/modules/generated/sklearn.datasets.make_moons.html

原文連結:https://machinelearningmastery.com/how-to-reduce-overfitting-in-deep-neural-networks-with-weight-constraints-in-keras/

相關文章