修正的線性激活函數(Relu)如何避免梯度消失

    2019年的第一篇博客。主要譯自Machine Learning Mastery,加上了一點點自己的想法。如有問題,歡迎批評指正~ 

    消失梯度問題是在訓練深度神經網絡時可能遇到的不穩定問題之一。它描述了深度多層前饋網絡或循環神經網絡無法將有用的梯度信息從模型的輸出端傳播回模型輸入端附近的層的情況。其結果是,具有許多層的模型通常無法在給定的數據集上學習或過早地收斂到較差的解決方案。

    目前,已經提出和研究了許多解決方案和變通方法,如改變權值初始化方案、提前使用無監督、分層訓練和梯度下降變體等。也許最常見的方法是使用修正的線性激活函數,它已經替代了自90年代末至21世紀初這十年一直被使用的雙曲正切激活函數,成爲新的默認值。

    在本教程中,您將瞭解如何在訓練神經網絡模型時診斷消失梯度問題,以及如何使用其他激活函數和權重初始化方案來修復它。

    完成本教程後,您將知道:

  1. 梯度消失問題限制了具有雙曲正切等經典激活函數的神經網絡的發展。
  2. 如何利用ReLU和He權值初始化修正深度神經網絡多層感知器的分類問題。
  3. 如何利用TensorBoard對消失梯度問題進行診斷,確定ReLU對模型中梯度流動的影響。

    讓我們開始吧。

教程概述

    本教程分爲五個部分;它們是:

  1. 梯度消失問題(總體介紹)
  2. 二圓二分類問題(構建數據集)
  3. 兩圓問題的多層感知器模型(構建MLP模型)
  4. 基於ReLU的兩圓問題的更深層次的MLP模型(加深模型及引入Relu激活函數)
  5. 在訓練中回顧平均梯度大小(使用Tensorboard對梯度進行可視化)

 梯度消失問題

    採用隨機梯度下降方法對神經網絡進行訓練,這包括首先計算模型的預測誤差,然後利用誤差來評估用於更新網絡中每個權重的梯度,從而減少下一次的誤差。這個誤差梯度通過網絡從輸出層向後傳播到輸入層。

    對多層神經網絡進行訓練是可取的,因爲多層神經網絡的加入增加了網絡的容量,使其能夠學習大量的訓練數據集,並能有效地表示從輸入到輸出的更復雜的映射函數。

    多層訓練網絡(如深度神經網絡)的一個問題是,當梯度在網絡中向後傳播時,梯度會急劇減小。當它到達接近模型輸入的層時,誤差可能非常小,以至於影響很小。因此,這個問題被稱爲“消失梯度”問題。

    消失的梯度讓我們很難知道參數應該向哪個方向移動以改善損失函數......

                                                                                                                                         — Page 290, Deep Learning, 2016.

     事實上,誤差梯度在深度神經網絡中是不穩定的,不僅會消失,而且會爆炸,梯度在網絡中向後傳播時呈指數增長。這就是所謂的“爆炸梯度”問題。

     消失梯度一詞指的是,在前饋網絡(FFN)中,反向傳播的誤差信號通常會隨着距離最後一層的距離呈指數形式減少(或增加)。

                                                              — Random Walk Initialization for Training Very Deep Feedforward Networks, 2014.

 

    在循環神經網絡中,梯度消失是一個特別的問題,因爲網絡的更新涉及到爲每個輸入時間步長展開網絡,實際上創建了一個非常深的網絡,需要更新權重。一個適度的循環神經網絡可能有200到400個輸入時間步長,在概念上產生一個非常深的網絡。

    梯度消失問題可能在多層感知機中表現爲訓練過程中模型的改進速度較慢,可能是過早收斂,例如,繼續訓練不會導致進一步的改進。檢查訓練過程中權重的變化,我們會發現在靠近輸出層的層中發生了更多的變化(即更多的學習),而在靠近輸入層的層中發生的變化更少。 

    有許多技術可以用來減少前饋神經網絡的梯度消失問題的影響,最著名的是替代權值初始化方案和使用替代激活函數。

     針對消失梯度問題,研究並應用了不同的深度網絡訓練方法(前饋和循環),如預處理、更好的隨機初始縮放、更好的優化方法、特定的體系結構、正交初始化等。

                                                              — Random Walk Initialization for Training Very Deep Feedforward Networks, 2014.

     在本教程中,我們將進一步研究如何使用其他的權值初始化方案和激活函數來訓練更深層次的神經網絡模型。

 二圓二分類問題

    作爲我們探索的基礎,我們將使用一個非常簡單的兩類或二元分類問題。

    scikit-learn類提供了make_circles()函數,該函數可用於創建具有指定樣本數量和統計噪聲的二進制分類問題。

    每個示例都有兩個輸入變量,用於定義二維平面上該點的x和y座標。這兩個類的點被排成兩個同心圓(它們有相同的中心)。

    數據集中點的數量由一個參數指定,其中一半將從每個圓中繪製。通過定義噪聲標準差的“噪聲”參數對採樣點進行採樣時,可以加入高斯噪聲,其中0.0表示沒有噪聲,或者從圓中準確地畫出點。僞隨機數生成器的種子可以通過“random_state”參數指定,該參數允許每次調用函數時採樣相同的點。

    下面的示例從兩個帶有噪聲和值1的循環中生成1,000個示例,用於生成僞隨機數生成器。

# generate circles
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)

     我們可以創建數據集的圖形,繪製輸入變量(x)的x和y座標,並用類值(0或1)爲每個點着色。下面列出了完整的示例。

# scatter plot of the circles dataset with points colored by class
from sklearn.datasets import make_circles
from numpy import where
from matplotlib import pyplot
# generate circles
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
# select indices of points with each class label
for i in range(2):
	samples_ix = where(y == i)
	pyplot.scatter(X[samples_ix, 0], X[samples_ix, 1], label=str(i))
pyplot.legend()
pyplot.show()

    運行該示例將創建一個繪圖,顯示生成的1000個數據點,每個點的類值用於爲每個點着色。我們可以看到0類的點是藍色的,代表外圓,1類的點是橙色的,代表內圓。

    生成的樣本的統計噪聲意味着兩個圓之間存在一些點的重疊,增加了問題的模糊性,使其非平凡性。這是可取的,因爲神經網絡可能會從許多可能的解決方案中選擇一個來分類兩個圓之間的點,並且總是會犯一些錯誤。

     現在我們已經定義了一個問題作爲我們探索的基礎,我們可以考慮開發一個模型來解決它。

 兩圓問題的多層感知器模型

    我們可以開發一個多層感知器模型來解決這兩個圓的問題。這將是一個簡單的前饋神經網絡模型,就像我們在20世紀90年代末和21世紀初學到的那樣。

    首先,我們將從兩個圓問題中生成1000個數據點,並將輸入重新縮放到範圍[- 1,1](數據幾乎已經在這個範圍內了,做這一步僅僅是確保萬無一失)。

    通常,我們將使用訓練數據集準備數據縮放,並將其應用於測試數據集。爲了在本教程中簡化流程,我們將在將所有數據分割爲訓練集和測試集之前,將它們縮放到一起。

# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
# scale input data to [-1,1]
scaler = MinMaxScaler(feature_range=(-1, 1))
X = scaler.fit_transform(X)

    接下來,我們將把數據分爲訓練集和測試集。

    一半的數據將用於培訓和剩餘的500例子將被用作測試集。在本教程中,測試集也將作爲驗證數據這樣我們可以瞭解模型的執行期間如何在控制外的集上訓練。

# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]

    接下來,我們將定義模型。

    對於數據集中的兩個變量,該模型將有一個具有兩個輸入的輸入層,一個具有五個節點的隱藏層,以及一個具有一個節點的輸出層,用於預測類概率。隱藏層將使用雙曲正切激活函數(tanh),輸出層將使用logistic激活函數(sigmoid)來預測class 0或class 1或介於兩者之間的值。

    在隱層中使用雙曲正切激活函數是20世紀90年代和21世紀初的最佳實踐,在隱層中使用雙曲正切激活函數的效果通常優於logistic函數。將網絡權值從均勻分佈初始化爲小的隨機值也是一種很好的做法。在這裏,我們將從範圍[0.0,1.0]中隨機初始化權重。

# define model
model = Sequential()
init = RandomUniform(minval=0, maxval=1)
model.add(Dense(5, input_dim=2, activation='tanh', kernel_initializer=init))
model.add(Dense(1, activation='sigmoid', kernel_initializer=init))

     該模型採用二元交叉熵損失函數,採用學習速率爲0.01、動量較大的隨機梯度下降法進行優化。

# compile model
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])

    對模型進行500個epochs的訓練,並在每個時點結束時對測試數據集和訓練數據集進行評估。

# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, 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 training history
pyplot.plot(history.history['acc'], label='train')
pyplot.plot(history.history['val_acc'], label='test')
pyplot.legend()
pyplot.show()

     將所有這些結合在一起,下面列出了完整的示例。

 

# mlp for the two circles classification problem
from sklearn.datasets import make_circles
from sklearn.preprocessing import MinMaxScaler
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.initializers import RandomUniform
from matplotlib import pyplot
# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
# scale input data to [-1,1]
scaler = MinMaxScaler(feature_range=(-1, 1))
X = scaler.fit_transform(X)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
init = RandomUniform(minval=0, maxval=1)
model.add(Dense(5, input_dim=2, activation='tanh', kernel_initializer=init))
model.add(Dense(1, activation='sigmoid', kernel_initializer=init))
# compile model
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, 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 training history
pyplot.plot(history.history['acc'], label='train')
pyplot.plot(history.history['val_acc'], label='test')
pyplot.legend()
pyplot.show()

    運行這個示例只需幾秒鐘就可以適應這個模型。計算並顯示了模型在訓練集和測試集上的性能。由於學習算法的隨機性,具體結果可能會有所不同。考慮運行該示例幾次。

    我們可以看到,在這種情況下,模型很好地學習了這個問題,在訓練集和測試數據集上都達到了大約81.6%的準確性。

    在訓練和測試集上創建了模型精度的直線圖,顯示了500個epochs內的性能變化。

    圖中顯示,對於這次運行,訓練集和測試集的性能在epoch=300左右開始下降,準確率都在80%左右。

     現在我們已經瞭解瞭如何使用tanh激活函數開發一個經典的MLP來解決兩個圓的問題,我們可以看看如何修改模型,使其具有更多的隱藏層。

 兩個圓問題的更深層次的MLP模型

    傳統上,開發深層多層感知器模型是具有挑戰性的。使用雙曲正切激活函數的深層模型不容易訓練,這種糟糕的性能在很大程度上要歸咎於漸近梯度問題。

    我們可以嘗試使用上一節中開發的MLP模型來研究這一點。隱藏層的數量可以從1增加到5;例如:

# define model
init = RandomUniform(minval=0, maxval=1)
model = Sequential()
model.add(Dense(5, input_dim=2, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(1, activation='sigmoid', kernel_initializer=init))

    然後我們可以重新運行示例並查看結果。下面列出了更深入的MLP的完整示例。

# deeper mlp for the two circles classification problem
from sklearn.datasets import make_circles
from sklearn.preprocessing import MinMaxScaler
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.initializers import RandomUniform
from matplotlib import pyplot
# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
scaler = MinMaxScaler(feature_range=(-1, 1))
X = scaler.fit_transform(X)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
init = RandomUniform(minval=0, maxval=1)
model = Sequential()
model.add(Dense(5, input_dim=2, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(1, activation='sigmoid', kernel_initializer=init))
# compile model
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, 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 training history
pyplot.plot(history.history['acc'], label='train')
pyplot.plot(history.history['val_acc'], label='test')
pyplot.legend()
pyplot.show()

    運行該示例首先在訓練集和測試數據集中打印fit模型的性能。由於學習算法的隨機性,具體結果可能會有所不同。考慮運行該示例幾次。

    在這種情況下,我們可以看到訓練集和測試集的性能都很差,達到了大約50%的準確率。這表明所配置的模型不能瞭解問題,也不能泛化解決方案。

     模型訓練過程中在訓練集和測試集上的精度曲線表明了相似的事情。我們可以看到,這種表現很差,而且隨着訓練的進行,這種表現會越來越差。

 基於ReLU的兩圓問題的更深層次的MLP模型

    在開發多層感知器網絡以及其他網絡類型(如CNNs)時,校正後的線性激活函數已經取代雙曲正切激活函數成爲新的首選默認值。這是因爲Relu的外觀和行爲都像一個線性函數,使得它更容易訓練和不太可能飽和,但實際上,它是一個非線性函數,迫使負輸入值爲0。它被認爲是一種可能的方法來解決消失梯度問題時,訓練更深的模型。(Relu起作用的原因)

    在使用修正的線性激活函數(ReLU)時,使用權重初始化方案是一種很好的做法。我們可以使用ReLU和He初始化定義帶有五個隱藏層的MLP,如下所示。

# define model
model = Sequential()
model.add(Dense(5, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='sigmoid'))

     下面列出了完整的代碼示例。

# deeper mlp with relu for the two circles classification problem
from sklearn.datasets import make_circles
from sklearn.preprocessing import MinMaxScaler
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.initializers import RandomUniform
from matplotlib import pyplot
# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
scaler = MinMaxScaler(feature_range=(-1, 1))
X = scaler.fit_transform(X)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
model.add(Dense(5, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='sigmoid'))
# compile model
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])
# fit model
history = model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, 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 training history
pyplot.plot(history.history['acc'], label='train')
pyplot.plot(history.history['val_acc'], label='test')
pyplot.legend()
pyplot.show()

    運行該示例將在訓練集和測試數據集上打印模型的性能。

    由於學習算法的隨機性,具體結果可能會有所不同。考慮運行該示例幾次。

    在這種情況下,我們可以看到這個小的變化使模型能夠了解問題,在兩個數據集上實現了84%的準確性,比使用tanh激活函數的單層模型的性能要好。

    在訓練週期內,建立了訓練集和測試集模型精度的線圖。這個圖顯示了與我們目前所看到的完全不同的動態。該模型似乎可以快速地瞭解問題,在大約100個epochs內匯聚到一個解決方案。

 

    使用ReLU激活函數使我們能夠爲這個簡單的問題擬合一個更深入的模型,但是這種能力不會無限擴展。例如,增加層的數量會導致學習速度變慢,達到大約20層,此時模型不再能夠學習問題,至少對於所選的配置是這樣。

    例如,下面是同一模型的15個隱含層的訓練和測試精度的線段圖,表明該模型仍然能夠學習問題。

     下面是同一模型20層的具有500個epochs的訓練和測試精度線圖,顯示配置不再能夠學習問題。

 

     雖然ReLU的使用是有效的,但是我們不能確信tanh函數的使用因爲梯度的消失而失敗,而ReLU的成功是因爲它克服了這個問題。

 在訓練中回顧平均梯度大小

    本節假設您正在使用帶有Keras的TensorFlow後端。如果不是這樣,您可以跳過這一部分。

    在使用tanh激活函數的情況下,我們知道網絡有足夠的能力來學習這個問題,但是層數的增加阻止了它這樣做。很難將消失梯度診斷爲性能差的原因。一個可能的信號是檢查每個訓練曆元每層梯度的平均大小。

    我們希望靠近輸出的層比靠近輸入的層有更大的平均梯度。

    Keras提供了TensorBoard回調函數,可用於在訓練期間記錄模型的屬性,如每層的平均梯度。然後可以使用TensorFlow提供的TensorBoard接口查看這些統計信息。

    我們可以配置這個回調函數來記錄每層每訓練的平均梯度,然後確保這個回調函數被用作模型訓練的一部分。

# prepare callback
tb = TensorBoard(histogram_freq=1, write_grads=True)
# fit model
model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, verbose=0, callbacks=[tb])

    我們可以使用這個回調函數,首先使用雙曲正切激活函數研究深度模型擬閤中梯度的動力學,然後使用修正的線性激活函數將動力學與相同的模型擬合進行比較。

    首先,下面列出了使用tanh和TensorBoard回調的deep MLP模型的完整示例。

# deeper mlp for the two circles classification problem with callback
from sklearn.datasets import make_circles
from sklearn.preprocessing import MinMaxScaler
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.initializers import RandomUniform
from keras.callbacks import TensorBoard
# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
scaler = MinMaxScaler(feature_range=(-1, 1))
X = scaler.fit_transform(X)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
init = RandomUniform(minval=0, maxval=1)
model = Sequential()
model.add(Dense(5, input_dim=2, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(5, activation='tanh', kernel_initializer=init))
model.add(Dense(1, activation='sigmoid', kernel_initializer=init))
# compile model
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])
# prepare callback
tb = TensorBoard(histogram_freq=1, write_grads=True)
# fit model
model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, verbose=0, callbacks=[tb])

    運行該示例將創建一個新的“logs/”子目錄,其中包含訓練期間回調記錄的統計信息。我們可以在TensorBoard web界面中查看統計數據。可以從命令行啓動該接口,這要求您指定到日誌目錄的完整路徑。

    例如,如果您在“/code”目錄中運行代碼,那麼日誌目錄的完整路徑將是“/code/logs/”。

    下面是在命令行(命令提示符/powershell)上啓動TensorBoard接口的命令。請確保將路徑更改爲您的logs目錄。

python -m tensorboard.main --logdir=/code/logs/

    接下來,在命令行(命令提示符/powershell)中將出現一個網址,複製並在瀏覽器打開。如果一切順利,您將看到TensorBoard web界面。

    這裏可能會有小問題。比如我剛剛開始打開時是醬紫的:

     後來參考了一下tensorboard 無法顯示的問題這篇博客,原來問題出現在了路徑上。所以,我的解決方法是:在logs的上一層目錄shift+右擊-在此處打開powershell窗口(當然也可能是cmd或者其他),輸入:

python -m tensorboard.main --logdir=logs

    複製網址在瀏覽器打開後即可看到:

     在界面的“DISTRIBUTIONS”和“HISTOGRAMS”選項卡中可以查看每個訓練曆元每層的平均梯度圖。可以使用搜索過濾器“kernel_0_grad”過濾這些圖,使其僅顯示密集層的梯度,排除偏差。

    我提供了下面的圖的副本,儘管由於學習算法的隨機性,您的具體結果可能會有所不同。

    首先,爲6個層(5個隱藏層,1個輸出層)中的每個層創建行圖。圖的名稱表示層,其中“dense_1”表示輸入層之後的隱藏層,“dense_6”表示輸出層。

    我們可以看到輸出層在整個運行過程中有很多活動,每個曆元的平均梯度在0.05到0.1之間。我們還可以在第一個隱藏層中看到類似範圍的一些活動。因此,漸變到達第一個隱藏層,但是最後一層和最後一層看到的是大部分活動。

     我們可以利用ReLU激活函數從深層MLP中收集相同的信息。下面列出了完整的示例。

# deeper mlp with relu for the two circles classification problem with callback
from sklearn.datasets import make_circles
from sklearn.preprocessing import MinMaxScaler
from keras.layers import Dense
from keras.models import Sequential
from keras.optimizers import SGD
from keras.callbacks import TensorBoard
# generate 2d classification dataset
X, y = make_circles(n_samples=1000, noise=0.1, random_state=1)
scaler = MinMaxScaler(feature_range=(-1, 1))
X = scaler.fit_transform(X)
# split into train and test
n_train = 500
trainX, testX = X[:n_train, :], X[n_train:, :]
trainy, testy = y[:n_train], y[n_train:]
# define model
model = Sequential()
model.add(Dense(5, input_dim=2, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(5, activation='relu', kernel_initializer='he_uniform'))
model.add(Dense(1, activation='sigmoid'))
# compile model
opt = SGD(lr=0.01, momentum=0.9)
model.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])
# prepare callback
tb = TensorBoard(histogram_freq=1, write_grads=True)
# fit model
model.fit(trainX, trainy, validation_data=(testX, testy), epochs=500, verbose=0, callbacks=[tb])

     如果你是新手,TensorBoard界面可能會讓你感到困惑。爲了簡單起見,在運行第二個示例之前刪除“logs”子目錄。一旦運行,您可以以相同的方式啓動TensorBoard接口,並通過web瀏覽器訪問它。

    與使用tanh的深度模型的梯度相比,每個訓練曆元的每層平均梯度圖顯示了不同的情況。

    我們可以看到,第一個隱藏層的梯度更大,與較大的擴展更一致,可能是0.2到0.4,而tanh的梯度爲0.05和0.1。我們還可以看到中間的隱藏層有很大的漸變。

     ReLU激活函數允許在訓練過程中有更多的梯度向後流過模型,這可能是性能提高的原因。

 拓展

     本節列出了一些擴展教程的想法,您可能希望對其進行研究。

  • Weight Initialization:使用tanh激活更新deep MLP,使用Xavier均勻權值初始化並報告結果。
  • Learning Algorithm:.使用tanh激活更新deep MLP,使用Adam等自適應學習算法並報告結果。
  • Weight Changes:更新tanh和relu的例子,記錄並繪製模型權值每一曆元的L1向量範數,作爲訓練過程中每一層變化量的代理,並比較結果。
  • Study Model Depth:使用激活tanh的MLP創建一個實驗,當隱藏層的數量從1增加到10時,報告模型的性能。
  • Increase Breadth:激活tanh後,將MLP隱藏層中的節點數量從5個增加到25個,當層數從1個增加到10個時,報告性能。

 總結

    在本教程中,您瞭解瞭如何在訓練神經網絡模型時診斷消失梯度問題,以及如何使用備用激活函數和權重初始化方案來修復它。具體來說,你學會了:

  • 梯度消失問題限制了具有雙曲正切等經典激活函數的神經網絡的發展。
  • 如何利用ReLU和He權值初始化修正深度神經網絡多層感知器的分類問題。
  • 如何利用Relu對消失梯度問題進行診斷,確定ReLU對模型中梯度流動的影響。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章