1. 程式人生 > >神經網路引數初始化

神經網路引數初始化

    神經網路引數的初始化,在網路層數很深的情況下變得尤為重要。如果引數初始化的過小,很可能導致網路每一層的輸出為都接近於0,那麼可以這樣認為每一層的輸入都會很接近於0,在進行反向傳播的時候,假如我們要更新某一層的引數W,該層的輸出是g(WX)暫且先不考慮偏置項,則求W的梯度就會是:上一層的梯度 * 該層對g的梯度 * X,由於每層的輸入都很小所以梯度就會很小,從而導致引數更新很慢,隨著層數的增加就會產生梯度消失的現象。我們也可把所有的梯度都連起來看,當進行反向傳播的時候,計算某一層的引數梯度為:本層的輸出對引數求梯度 * 上一層的梯度,而上一層的梯度中會有上層的權重W這一項(可以自己求一下驗證),而W初始化的很小,隨著層數的加深,每一層的W累乘,就會導致梯度越來越小直至為0。

下面是做的一個演示:

一個10層的神經網路,每一層都使用tanh做為啟用函式,資料是1000個從標準正態分佈中隨機取樣得到。

首先如果每一層的引數如果都初始化的很小:

W = np.random.randn(fan_in, fan_out) * 0.01 #layer initialization

那麼每層輸出的均值、標準差和分佈如下:

可以看出在第一層的時候還保持著原有資料的分佈,但從第二層輸出開始資料的分佈就逐漸的靠近0,最終均值和標準差都為0。

那麼引數的初始化如果大一些會如何?如果引數初始化的過大,則每一層的輸出就會很大,也就是每個神經元都很飽和,如果進行反向傳播,考慮啟用函式的影象,則對啟用函式的梯度就會趨近於0,也會造成梯度消失的情況。下面是引數初始化過大的情況。

W = np.random.randn(fan_in, fan_out) * 1.0 #layer initialization

一個很好的初始化引數的方法就是使用Xavier initialization,這個方法是Glorot在2010年發表的論文中提出的,該方法初始化的公式為:W = np.random.randn(fan_in, fan_out) / np.sqrt(fan_in)

這個公式的目的就是令輸出和輸入的方差保持一致。具體推導可以在網上有很多,也可以去看論文。

在使用了Xavier方法初始化後每一層的輸出分佈如下:

可以看出每一層輸出都有較好的分佈。

對於使用relu啟用函式,則使用上面的Xavier方法進行初始化效果就不好,這是因為relu啟用函式只會啟用大約一半的神經元,其他神經元的輸出為則為0,那麼使用原始的Xavier的公式就會改變原來的方差。每層輸出分佈如下:

可以看到,隨著層數的加深,每一層的分佈越來越向0進行收縮。解決這個問題可以使用如下的式子(He et al.,2015):

W = np.random.randn(fan_in, fan_out) / np.sqrt(fan_in / 2)

由與每次實際上只有大約一半的神經元被啟用,相當於只有一半的輸入有效,所以在進行引數縮放的時候除以2。

總之在深度神經網路的實現中,引數的初始化尤為重要。如果要想網路表現的好,訓練的快,合適的引數初始化勢必不可少的。

 

參考資料:cs231n課程