本文介紹了爲什麼要使用正則化以及如何在Keras中使用正則化來降低模型過擬合風險。
文章目錄
代碼環境:
python -3.7.6
tensorflow -2.1.0
神經網絡學習一組權重,以最佳地將輸入映射到輸出。具有較大網絡權重的網絡可能表示網絡不穩定,在該網絡中,輸入的較小變化可能導致輸出的較大變化。這可能表明網絡過度適合訓練數據集,並且在對新數據進行預測時可能表現不佳。
該問題的解決方案是更新學習算法,使網絡保持較小的權重,即權重正則化(weight regularization),該方法可以減少訓練數據集的過擬合風險並提高模型泛化能力。
1. 權重過大導致的問題
訓練神經網絡一般使用隨機梯度下降(SGD)算法,“訓練”即學習權重向量和偏置的過程。
訓練週期越長,模型越容易過度適應訓練集的特徵分佈,從而導致過擬合。爲了從訓練集中提取特徵,權重會增大,較大的權重會導致網絡不穩定,即使輸入數據發生較小的波動或引入隨機噪聲,也會導致輸出有很大差異。也就就是說,模型在訓練集上表現很好,但是在新數據集上表現很差,即泛化能力很差。
通常認爲這樣的模型具有較大的方差和較小的偏差。即模型對訓練數據集中的某些樣本敏感,比如收集數據過程中引入的噪聲。
權重較大的模型比權重較小的模型更爲複雜,雖然擬合能力很強,但很容易導致過擬合。根據奧卡剃刀定律:“如無必要,勿增實體”,在應用過程中,一般從較簡單的模型開始建模。
另一個可能的問題是,可能有許多輸入變量,每一個都與輸出變量有不同程度的相關性。有時可以使用方法來幫助選擇輸入變量,但通常變量之間的相互關係並不明顯。
對於不太相關或不相關的模型輸入,使用較小的權重甚至零權重可以使得模型專注於學習。這也可以減少模型容量,提高泛化能力,避免陷入局部最優。
2. 正則化
正則化又稱爲懲罰,即通過選擇解決學習問題的最小向量來抑制權重向量的任何不相關分量。常用的正則化有L1正則化,又稱爲稀疏正則化;L2正則化,又稱爲權重衰減正則化。L1和L2分別表示L1範數和L2範數。其計算公式如下:
範數表示向量的各個元素的絕對值之和。其公式爲:
範數表示向量的各個元素的平方和再開平方。
其中, 表示一個 維向量。範數(Norm) 是一個表示向量“長度”的函數,爲向量空間內的所有向量賦予非零的正長度或大小。
加入正則化後,最優化目標函數變爲:
其中 爲損失函數, 爲訓練樣本的數量, 爲待學習的神經網絡, 爲其參數, 表示範數,通常爲 範數和 範數,λ表示正則化係數。
L1 正則化鼓勵模型將權重設置爲0或1,從而使模型稀疏。L2 正則化又稱爲 嶺迴歸(ridge regression) 或 Tikhonov 正則化。L2 是更常用的正則化方法。
通過之前的機器學習基石課程中知道,L2正則化中包含一個拉格朗日乘子係數 ,稱爲懲罰項或正則化項,控制對模型權重懲罰的力度,其值介於0和1之間。如果 選擇的大小合適,權重衰減會抑制靜態噪聲對目標的某些影響。越大的懲罰項表示對模型的擬合能力限制越大。
權重的向量範數通常是逐層計算的,而不是整個網絡中的。儘管默認情況下通常在每個層上使用相同的 值,但這在選擇所使用的正則化類型(例如,輸入層使用L1,其它層使用L2)的類型方面提供了更大的靈活性,並且在 值方面具有靈活性。
在神經網絡的情況下,有時需要爲網絡的每一層使用係數不同的懲罰項。因爲搜索多個合適的超參數很耗費計算資源,所以在所有層上使用相同的權重衰減只是爲了減小搜索空間的大小,但仍然是合理的。
3. 正則化使用技巧
3.1 在所有模型上使用
權重正則化是一種通用方法。它可以與大多數的神經網絡模型一起使用,尤其是多層感知器,卷積神經網絡和長短期記憶遞歸神經網絡等最常見的網絡類型。對於LSTM,可能需要對輸入層和LSTM層使用不同的懲罰項。
3.2 標準化輸入數據 🐳
當輸入變量具有不同的比例時,網絡權重的比例將相應地變化。這在使用權重正則化時引入了一個問題,必須添加權重的絕對值或平方值以用於懲罰。可以通過標準化或標準化輸入變量來解決此問題。
常用的做法是將原始數據減去均值,再除以標準差。
3.3 訓練深層網絡時使用
對於較大的網絡(更多的層或更多的節點),更容易導致過擬合。因此使用正則化來降低過擬合風險。
3.4 結合使用 L1和L2正則化
因爲這兩個正則化方法各有其特點,所以結合使用不失爲一種提高模型性能的方法。
3.5 在訓練好的網絡上使用正則化 🐬
例如,可以先不使用正則化訓練模型,然後再使用正則化更新模型,以減小已經表現良好的模型的權重大小。
3.6 其它正則化方法 🐋
權重懲罰的另一種可能類型是:
它雖然不像L1那樣強烈,卻鼓勵零權重,同時也像L2一樣強力懲罰較大的權重(但更強)。
4. Keras 實現
4.1 Keras API
tensorflow.keras
提供了三個正則化關鍵字參數,分別是:
kernel_regularizer
:正則化器在層的內核上添加懲罰項;bias_regularizer
:正則化器對層的偏差添加懲罰項;activity_regularizer
:正則化器對層的輸出添加懲罰項。
簡單示例:
from tensorflow.keras import layers
from tensorflow.keras import regularizers
layer = layers.Dense(
units=64,
kernel_regularizer=regularizers.l1_l2(l1=1e-5, l2=1e-4),
bias_regularizer=regularizers.l2(1e-4),
activity_regularizer=regularizers.l2(1e-5)
)
4.2 L1 正則化
tf.keras.regularizers.l1(l=0.01)
【The L1 regularization penalty is computed as: loss = l * reduce_sum(abs(x))
】
參數說明:
l
: Float; L1 regularization factor.
4.3 L2 正則化
tf.keras.regularizers.l2(l=0.01)
【The L2 regularization penalty is computed as: loss = l * reduce_sum(square(x))
】
參數說明:
l
: Float; L2 regularization factor.
4.3 L1L2 正則化
tf.keras.regularizers.l1_l2(l1=0.01, l2=0.01)
4.4 自定義正則化函數 🐟
1.簡單方法
def my_regularizer(x):
return 1e-3 * tf.reduce_sum(tf.square(x))
2.Regularizer 子類
如果需要通過各種參數(例如l1和中的l2參數l1_l2)配置正則化,則應實現爲tf.keras.regularizers.Regularizer
的子類。
class MyRegularizer(regularizers.Regularizer):
def __init__(self, strength):
self.strength = strength
def __call__(self, x):
return self.strength * tf.reduce_sum(tf.square(x))
5. 網格搜索尋找最佳的正則化參數
在掉頭髮調參的時候,如果確定正則化可以提高模型性能,那麼就需要通過網格搜索來確定最佳的正則化參數。
一般的做法的是,首先在0.0到0.1之間的各個數量級上進行網格搜索,然後在找到某個級別後,再對該級別進行網格搜索。
from sklearn.datasets import make_moons
from tensorflow.keras.layers import Dense
from tensorflow.keras.models import Sequential
from tensorflow.keras.regularizers import l2
import matplotlib.pyplot as plt
plt.rcParams['figure.dpi'] = 150
# 生成虛擬分類樣本
X, y = make_moons(n_samples=100, noise=0.2, random_state=1)
# 劃分訓練集和測試集
n_train = 30
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# 網格搜索參數配置
values = [1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6]
all_train, all_test = list(), list()
for param in values:
# 定義模型
model = Sequential()
model.add(Dense(500, input_dim=2, activation='relu', kernel_regularizer=l2(param)))
model.add(Dense(1, activation='sigmoid'))
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
# 訓練模型
model.fit(trainX, trainy, epochs=4000, verbose=0)
# 評估模型
_, train_acc = model.evaluate(trainX, trainy, verbose=0)
_, test_acc = model.evaluate(testX, testy, verbose=0)
print('Param: %f, Train: %.3f, Test: %.3f' % (param, train_acc, test_acc))
all_train.append(train_acc)
all_test.append(test_acc)
plt.semilogx(values, all_train, label='train', marker='o') # 轉換爲10的冪顯示
plt.semilogx(values, all_test, label='test', marker='o')
plt.legend()
plt.show()
輸出:
Param: 0.100000, Train: 0.967, Test: 0.829
Param: 0.010000, Train: 1.000, Test: 0.943
Param: 0.001000, Train: 1.000, Test: 0.943
Param: 0.000100, Train: 1.000, Test: 0.929
Param: 0.000010, Train: 1.000, Test: 0.914
Param: 0.000001, Train: 1.000, Test: 0.914
可以看出,0.1的正則化參數模型表現比較差;0.01是比較好的正則化參數。
參考:
https://machinelearningmastery.com/weight-regularization-to-reduce-overfitting-of-deep-learning-models/
https://machinelearningmastery.com/how-to-reduce-overfitting-in-deep-learning-with-weight-regularization/
https://keras.io/getting_started/
https://keras.io/api/layers/regularizers/