【调参19】如何使用梯度裁剪(Gradient Clipping)避免梯度爆炸
文章目錄
- 1. 梯度爆炸和裁剪
- 2. TensorFlow.Keras 實現
- 2.1 梯度范數縮放(Gradient Norm Scaling)
- 2.2 梯度值裁剪(Gradient Value Clipping)
- 3. 實例
- 3.1 梯度爆炸 MLP
- 3.2 梯度范數縮放 MLP
- 3.3 梯度值裁剪 MLP
給定誤差函數,學習率,甚至目標變量的大小,訓練神經網絡可能變得不穩定。訓練期間權重的較大更新會導致數值上溢或下溢,通常稱為梯度爆炸(gradients exploding)。
梯度爆炸在遞歸神經網絡中更為常見,例如LSTM,因為梯度的累積在數百個輸入時間步長上展開。
梯度爆炸的一種常見且相對容易的解決方案是:在通過網絡向后傳播誤差并使用其更新權重之前,更改誤差的導數。兩種方法包括:給定選定的向量范數( vector norm)來重新縮放梯度;以及裁剪超出預設范圍的梯度值。這些方法一起被稱為梯度裁剪(gradient clipping)。
1. 梯度爆炸和裁剪
使用隨機梯度下降優化算法訓練神經網絡。這首先需要在一個或多個訓練樣本上估算損失,然后計算損失的導數,該導數通過網絡反向傳播,以更新權重。使用學習率控制的反向傳播誤差的一小部分來更新權重。
權重的更新可能會很大,以至于權重的數值精度超出或低于該數值精度。權重在上溢或下溢時可以取“NaN”或“Inf”值,但網絡將毫無用處,因為信號流過無效權重時永遠預測NaN值。權重的上溢或下溢是指網絡訓練過程的不穩定性,并且由于不穩定的訓練過程導致網絡無法進行訓練,從而導致模型實質上是無用的,因此被稱為梯度爆炸。
在給定的神經網絡(例如卷積神經網絡或多層感知器)中,可能由于配置選擇不當而發生梯度爆炸:
- 學習率選擇不當會導致較大的權重更新。
- 準備的數據有很多噪聲,導致目標變量差異很大。
- 損失函數選擇不當,導致計算出較大的誤差值。
在遞歸神經網絡(例如長短期記憶網絡)中容易出現梯度爆炸。通常,可以通過精心配置網絡模型來避免爆炸梯度,例如,選擇較小的學習速率,按比例縮放目標變量和標準損失函數。盡管如此,對于具有大量輸入時間步長的遞歸網絡,梯度爆炸仍然是一個需要著重考慮的問題。
梯度爆炸的一種常見解決方法是先更改誤差導數,然后通過網絡反向傳播誤差導數,然后使用它來更新權重。通過重新縮放誤差導數,權重的更新也將被重新縮放,從而大大降低了上溢或下溢的可能性。更新誤差導數的主要方法有兩種:
- 梯度縮放(Gradient Scaling)
- 梯度裁剪(Gradient Clipping)
梯度縮放涉及對誤差梯度向量進行歸一化,以使向量范數大小等于定義的值,例如1.0。只要它們超過閾值,就重新縮放它們。如果漸變超出了預期范圍,則漸變裁剪會強制將漸變值(逐個元素)強制為特定的最小值或最大值。這些方法通常簡稱為梯度裁剪。
當傳統的梯度下降算法建議進行一個非常大的步長時,梯度裁剪將步長減小到足夠小,以至于它不太可能走到梯度最陡峭的下降方向的區域之外。
它是一種僅解決訓練深度神經網絡模型的數值穩定性,而不能改進網絡性能的方法。
梯度向量范數或預設范圍的值可以通過反復試驗來配置,可以使用文獻中使用的常用值,也可以先通過實驗觀察通用向量范數或范圍,然后選擇一個合理的值。
對于網絡中的所有層,通常都使用相同的梯度裁剪配置。不過,在某些示例中,與隱藏層相比,輸出層中允許更大范圍的誤差梯度。
2. TensorFlow.Keras 實現
2.1 梯度范數縮放(Gradient Norm Scaling)
梯度范數縮放:在梯度向量的L2向量范數(平方和)超過閾值時,將損失函數的導數更改為具有給定的向量范數。
例如,可以將范數指定為1.0,這意味著,如果梯度的向量范數超過1.0,則向量中的值將重新縮放,以使向量范數等于1.0。在Keras中通過在優化器上指定 clipnorm 參數實現:
.... opt = SGD(lr=0.01, momentum=0.9, clipnorm=1.0)2.2 梯度值裁剪(Gradient Value Clipping)
如果梯度值小于負閾值或大于正閾值,則梯度值剪切將損失函數的導數剪切為給定值。例如,可以將范數指定為0.5,這意味著如果梯度值小于-0.5,則將其設置為-0.5,如果梯度值大于0.5,則將其設置為0.5。通過在優化器上指定 clipvalue 參數實現:
... 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# 構造回歸問題數據集 X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)# 劃分訓練集和驗證集 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))# 訓練模型 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()在這種情況下,該模型無法學習,從而導致對NaN值的預測。給定非常大的誤差,然后在訓練中針對權重更新計算出的誤差梯度,模型權重會爆炸。傳統的解決方案是使用標準化或歸一化來重新調整目標變量。不過,本文使用替代方法–梯度修剪。
3.2 梯度范數縮放 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# 構造回歸問題數據集 X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)# 劃分訓練集和驗證集 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)# 訓練模型 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內從20000以上的大數值迅速下降到100以下的小數值。
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# 構造回歸問題數據集 X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=1)# 劃分訓練集和驗證集 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)# 訓練模型 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()
該圖表明,該模型可以快速學習問題,僅在幾個訓練周期內就損失了不到100的MSE。
參考:
https://machinelearningmastery.com/how-to-avoid-exploding-gradients-in-neural-networks-with-gradient-clipping/
總結
以上是生活随笔為你收集整理的【调参19】如何使用梯度裁剪(Gradient Clipping)避免梯度爆炸的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WSL2连接调用USB设备
- 下一篇: 言论(《读者》2007年第1-4