【调参19】如何使用梯度裁剪(Gradient Clipping)避免梯度爆炸
文章目錄
- 1. 梯度爆炸和裁剪
- 2. TensorFlow.Keras 實現(xiàn)
- 2.1 梯度范數(shù)縮放(Gradient Norm Scaling)
- 2.2 梯度值裁剪(Gradient Value Clipping)
- 3. 實例
- 3.1 梯度爆炸 MLP
- 3.2 梯度范數(shù)縮放 MLP
- 3.3 梯度值裁剪 MLP
給定誤差函數(shù),學(xué)習(xí)率,甚至目標(biāo)變量的大小,訓(xùn)練神經(jīng)網(wǎng)絡(luò)可能變得不穩(wěn)定。訓(xùn)練期間權(quán)重的較大更新會導(dǎo)致數(shù)值上溢或下溢,通常稱為梯度爆炸(gradients exploding)。
梯度爆炸在遞歸神經(jīng)網(wǎng)絡(luò)中更為常見,例如LSTM,因為梯度的累積在數(shù)百個輸入時間步長上展開。
梯度爆炸的一種常見且相對容易的解決方案是:在通過網(wǎng)絡(luò)向后傳播誤差并使用其更新權(quán)重之前,更改誤差的導(dǎo)數(shù)。兩種方法包括:給定選定的向量范數(shù)( vector norm)來重新縮放梯度;以及裁剪超出預(yù)設(shè)范圍的梯度值。這些方法一起被稱為梯度裁剪(gradient clipping)。
1. 梯度爆炸和裁剪
使用隨機梯度下降優(yōu)化算法訓(xùn)練神經(jīng)網(wǎng)絡(luò)。這首先需要在一個或多個訓(xùn)練樣本上估算損失,然后計算損失的導(dǎo)數(shù),該導(dǎo)數(shù)通過網(wǎng)絡(luò)反向傳播,以更新權(quán)重。使用學(xué)習(xí)率控制的反向傳播誤差的一小部分來更新權(quán)重。
權(quán)重的更新可能會很大,以至于權(quán)重的數(shù)值精度超出或低于該數(shù)值精度。權(quán)重在上溢或下溢時可以取“NaN”或“Inf”值,但網(wǎng)絡(luò)將毫無用處,因為信號流過無效權(quán)重時永遠(yuǎn)預(yù)測NaN值。權(quán)重的上溢或下溢是指網(wǎng)絡(luò)訓(xùn)練過程的不穩(wěn)定性,并且由于不穩(wěn)定的訓(xùn)練過程導(dǎo)致網(wǎng)絡(luò)無法進行訓(xùn)練,從而導(dǎo)致模型實質(zhì)上是無用的,因此被稱為梯度爆炸。
在給定的神經(jīng)網(wǎng)絡(luò)(例如卷積神經(jīng)網(wǎng)絡(luò)或多層感知器)中,可能由于配置選擇不當(dāng)而發(fā)生梯度爆炸:
- 學(xué)習(xí)率選擇不當(dāng)會導(dǎo)致較大的權(quán)重更新。
- 準(zhǔn)備的數(shù)據(jù)有很多噪聲,導(dǎo)致目標(biāo)變量差異很大。
- 損失函數(shù)選擇不當(dāng),導(dǎo)致計算出較大的誤差值。
在遞歸神經(jīng)網(wǎng)絡(luò)(例如長短期記憶網(wǎng)絡(luò))中容易出現(xiàn)梯度爆炸。通常,可以通過精心配置網(wǎng)絡(luò)模型來避免爆炸梯度,例如,選擇較小的學(xué)習(xí)速率,按比例縮放目標(biāo)變量和標(biāo)準(zhǔn)損失函數(shù)。盡管如此,對于具有大量輸入時間步長的遞歸網(wǎng)絡(luò),梯度爆炸仍然是一個需要著重考慮的問題。
梯度爆炸的一種常見解決方法是先更改誤差導(dǎo)數(shù),然后通過網(wǎng)絡(luò)反向傳播誤差導(dǎo)數(shù),然后使用它來更新權(quán)重。通過重新縮放誤差導(dǎo)數(shù),權(quán)重的更新也將被重新縮放,從而大大降低了上溢或下溢的可能性。更新誤差導(dǎo)數(shù)的主要方法有兩種:
- 梯度縮放(Gradient Scaling)
- 梯度裁剪(Gradient Clipping)
梯度縮放涉及對誤差梯度向量進行歸一化,以使向量范數(shù)大小等于定義的值,例如1.0。只要它們超過閾值,就重新縮放它們。如果漸變超出了預(yù)期范圍,則漸變裁剪會強制將漸變值(逐個元素)強制為特定的最小值或最大值。這些方法通常簡稱為梯度裁剪。
當(dāng)傳統(tǒng)的梯度下降算法建議進行一個非常大的步長時,梯度裁剪將步長減小到足夠小,以至于它不太可能走到梯度最陡峭的下降方向的區(qū)域之外。
它是一種僅解決訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)模型的數(shù)值穩(wěn)定性,而不能改進網(wǎng)絡(luò)性能的方法。
梯度向量范數(shù)或預(yù)設(shè)范圍的值可以通過反復(fù)試驗來配置,可以使用文獻中使用的常用值,也可以先通過實驗觀察通用向量范數(shù)或范圍,然后選擇一個合理的值。
對于網(wǎng)絡(luò)中的所有層,通常都使用相同的梯度裁剪配置。不過,在某些示例中,與隱藏層相比,輸出層中允許更大范圍的誤差梯度。
2. TensorFlow.Keras 實現(xiàn)
2.1 梯度范數(shù)縮放(Gradient Norm Scaling)
梯度范數(shù)縮放:在梯度向量的L2向量范數(shù)(平方和)超過閾值時,將損失函數(shù)的導(dǎo)數(shù)更改為具有給定的向量范數(shù)。
例如,可以將范數(shù)指定為1.0,這意味著,如果梯度的向量范數(shù)超過1.0,則向量中的值將重新縮放,以使向量范數(shù)等于1.0。在Keras中通過在優(yōu)化器上指定 clipnorm 參數(shù)實現(xiàn):
.... opt = SGD(lr=0.01, momentum=0.9, clipnorm=1.0)2.2 梯度值裁剪(Gradient Value Clipping)
如果梯度值小于負(fù)閾值或大于正閾值,則梯度值剪切將損失函數(shù)的導(dǎo)數(shù)剪切為給定值。例如,可以將范數(shù)指定為0.5,這意味著如果梯度值小于-0.5,則將其設(shè)置為-0.5,如果梯度值大于0.5,則將其設(shè)置為0.5。通過在優(yōu)化器上指定 clipvalue 參數(shù)實現(xiàn):
... opt = SGD(lr=0.01, momentum=0.9, clipvalue=0.5)3. 實例
通過一個簡單的MLP回歸問題來說明梯度裁剪的作用。
3.1 梯度爆炸 MLP
from sklearn.datasets import make_regression from keras.layers import Dense from keras.models import Sequential from keras.optimizers import SGD import matplotlib.pyplot as plt plt.rcParams['figure.dpi'] = 150# 構(gòu)造回歸問題數(shù)據(jù)集 X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)# 劃分訓(xùn)練集和驗證集 n_train = 500 trainX, testX = X[:n_train, :], X[n_train:, :] trainy, testy = y[:n_train], y[n_train:]# 定義模型 model = Sequential() model.add(Dense(25, input_dim=20, activation='relu', kernel_initializer='he_uniform')) model.add(Dense(1, activation='linear'))# 編譯模型 model.compile(loss='mean_squared_error', optimizer=SGD(lr=0.01, momentum=0.9))# 訓(xùn)練模型 history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=100, verbose=0)# 評估模型 train_mse = model.evaluate(trainX, trainy, verbose=0) test_mse = model.evaluate(testX, testy, verbose=0) print('Train: %.3f, Test: %.3f' % (train_mse, test_mse))# 繪制損失曲線 plt.title('Mean Squared Error') plt.plot(history.history['loss'], label='train') plt.plot(history.history['val_loss'], label='test') plt.legend() plt.show()在這種情況下,該模型無法學(xué)習(xí),從而導(dǎo)致對NaN值的預(yù)測。給定非常大的誤差,然后在訓(xùn)練中針對權(quán)重更新計算出的誤差梯度,模型權(quán)重會爆炸。傳統(tǒng)的解決方案是使用標(biāo)準(zhǔn)化或歸一化來重新調(diào)整目標(biāo)變量。不過,本文使用替代方法–梯度修剪。
3.2 梯度范數(shù)縮放 MLP
from sklearn.datasets import make_regression from keras.layers import Dense from keras.models import Sequential from keras.optimizers import SGD import matplotlib.pyplot as plt plt.rcParams['figure.dpi'] = 150# 構(gòu)造回歸問題數(shù)據(jù)集 X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)# 劃分訓(xùn)練集和驗證集 n_train = 500 trainX, testX = X[:n_train, :], X[n_train:, :] trainy, testy = y[:n_train], y[n_train:]# 定義模型 model = Sequential() model.add(Dense(25, input_dim=20, activation='relu', kernel_initializer='he_uniform')) model.add(Dense(1, activation='linear'))# 編譯模型 opt = SGD(lr=0.01, momentum=0.9, clipnorm=1.0) model.compile(loss='mean_squared_error', optimizer=opt)# 訓(xùn)練模型 history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=100, verbose=0)# 評估模型 train_mse = model.evaluate(trainX, trainy, verbose=0) test_mse = model.evaluate(testX, testy, verbose=0) print('Train: %.3f, Test: %.3f' % (train_mse, test_mse))# 繪制損失曲線 plt.title('Mean Squared Error') plt.plot(history.history['loss'], label='train') plt.plot(history.history['val_loss'], label='test') plt.legend() plt.show()
該圖顯示了損失在20個epoch內(nèi)從20000以上的大數(shù)值迅速下降到100以下的小數(shù)值。
3.3 梯度值裁剪 MLP
from sklearn.datasets import make_regression from keras.layers import Dense from keras.models import Sequential from keras.optimizers import SGD import matplotlib.pyplot as plt plt.rcParams['figure.dpi'] = 150# 構(gòu)造回歸問題數(shù)據(jù)集 X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)# 劃分訓(xùn)練集和驗證集 n_train = 500 trainX, testX = X[:n_train, :], X[n_train:, :] trainy, testy = y[:n_train], y[n_train:]# 定義模型 model = Sequential() model.add(Dense(25, input_dim=20, activation='relu', kernel_initializer='he_uniform')) model.add(Dense(1, activation='linear'))# 編譯模型 opt = SGD(lr=0.01, momentum=0.9, clipvalue=5.0) model.compile(loss='mean_squared_error', optimizer=opt)# 訓(xùn)練模型 history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=100, verbose=0)# 評估模型 train_mse = model.evaluate(trainX, trainy, verbose=0) test_mse = model.evaluate(testX, testy, verbose=0) print('Train: %.3f, Test: %.3f' % (train_mse, test_mse))# 繪制損失曲線 plt.title('Mean Squared Error') plt.plot(history.history['loss'], label='train') plt.plot(history.history['val_loss'], label='test') plt.legend() plt.show()
該圖表明,該模型可以快速學(xué)習(xí)問題,僅在幾個訓(xùn)練周期內(nèi)就損失了不到100的MSE。
參考:
https://machinelearningmastery.com/how-to-avoid-exploding-gradients-in-neural-networks-with-gradient-clipping/
總結(jié)
以上是生活随笔為你收集整理的【调参19】如何使用梯度裁剪(Gradient Clipping)避免梯度爆炸的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WSL2连接调用USB设备
- 下一篇: 言论(《读者》2007年第1-4