本文通過對《Understanding the difficulty of training deep feedforward neural networks》文章翻譯和解讀,和知乎、CSDN幾位博主的文章總結、分析深度網路初始化方法。
首先是《Understanding the difficulty of training deep feedforward neural networks》
敲黑板:這裡有一個發文章技巧,行不行有待驗證大家應該感覺到一般的深度學習文章上來就是實驗,告訴讀者這個實驗結果好,然後由實驗結果再反向給出一些無從驗證的可能對可能不對的原因。而這篇文章雖然整體來看比較簡單,但結構非常嚴謹:首先通過實驗分析標準初始化方法的問題;然後根據兩個目標——狀態方差和梯度方差保持不變推匯出引數的特點,給出Xavier初始化方法的具體形式;最後通過實驗驗證Xavier初始化的效果確實不錯。
總結髮文章方法:
- 實驗
- 告訴讀者實驗結果好
- 由結果反正無從驗證的可能對與不對的原因,設立目標
- 根據目標驗證猜想效果不錯
下面之說一下我認為的重點:
分析的前提:
-
- 網路在初始化處於線性條件下,即啟用活函式的導數為1;
-
- 初始化的權值的mean 為0,且獨立同分布的;
- 3.輸入特徵 x 的 variance是相同的。經過一系列推導,得到了下面這樣的結果:
第一: 第二:公式5有用哦:
1.前向傳播:用文中的話說:From a forward-propagation point of view, to keep information flowing we would like that:推出這玩意來了以後呢, 下面是關鍵:
1.前向傳播:用文中的話說:From a forward-propagation point of view, to keep information flowing we would like that:
就是說,為了在前向傳播過程中,可以讓資訊向前傳播,做法就是讓:啟用單元的輸出值的方差持不變。為什麼要這樣呢??有點小不理解。。- 反向傳播:在反向傳播過程中,也是為了讓梯度可以反向傳播,讓:對啟用單元輸入值的梯度 保持不變,即: 最後得到的結論就是:
在訓練過程中,梯度問題:
這時,我們就不能單純地用梯度的 variance 去分析了,因為已經不滿足我們的假設條件了啊。
文章後面的一大堆基本沒有什麼重點的東西了吧,我覺得。寫幾個覺得有必要的總結吧:
-
softsign啟用函式與雙曲正切函式相比,效果還 很不錯的,
-
normalized initialization 的方法很不錯。
初始化比較
- 把w初始化為0
- 對w隨機初始化
- Xavier initialization
- He initialization
1.把w初始化為0
我們線上性迴歸,logistics迴歸的時候,基本上都是把引數初始化為0,我們的模型也能夠很好的工作。然後在神經網路中,把w初始化為0是不可以的。這是因為如果把w初始化0,那麼每一層的神經元學到的東西都是一樣的(輸出是一樣的),而且在bp的時候,每一層內的神經元也是相同的,因為他們的gradient相同。下面用一段程式碼來演示,當把w初始化為0:
def initialize_parameters_zeros(layers_dims):
"""
Arguments:
layer_dims -- python array (list) containing the size of each layer.
Returns:
parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":
W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])
b1 -- bias vector of shape (layers_dims[1], 1)
...
WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])
bL -- bias vector of shape (layers_dims[L], 1)
"""
parameters = {}
np.random.seed(3)
L = len(layers_dims) # number of layers in the network
for l in range(1, L):
parameters['W' + str(l)] = np.zeros((layers_dims[l], layers_dims[l - 1]))
parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
return parameters
複製程式碼
我們可以看看cost function是如何變化的:
能夠看到代價函式降到0.64(迭代1000次)後,再迭代已經不起什麼作用了。2.對w隨機初始化
目前常用的就是隨機初始化,即W隨機初始化。隨機初始化的程式碼如下:
def initialize_parameters_random(layers_dims):
"""
Arguments:
layer_dims -- python array (list) containing the size of each layer.
Returns:
parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":
W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])
b1 -- bias vector of shape (layers_dims[1], 1)
...
WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])
bL -- bias vector of shape (layers_dims[L], 1)
"""
np.random.seed(3) # This seed makes sure your "random" numbers will be the as ours
parameters = {}
L = len(layers_dims) # integer representing the number of layers
for l in range(1, L):
parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1])*0.01
parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
return parameters
複製程式碼
乘0.01是因為要把W隨機初始化到一個相對較小的值,因為如果X很大的話,W又相對較大,會導致Z非常大,這樣如果啟用函式是sigmoid,就會導致sigmoid的輸出值1或者0,然後會導致一系列問題(比如cost function計算的時候,log裡是0,這樣會有點麻煩)。
隨機初始化後,cost function隨著迭代次數的變化示意圖為:
import numpy as np
import matplotlib.pyplot as plt
def initialize_parameters(layer_dims):
"""
:param layer_dims: list,每一層單元的個數(維度)
:return:dictionary,儲存引數w1,w2,...,wL,b1,...,bL
"""
np.random.seed(3)
L = len(layer_dims)#the number of layers in the network
parameters = {}
for l in range(1,L):
parameters["W" + str(l)] = np.random.randn(layer_dims[l],layer_dims[l-1])*0.01
parameters["b" + str(l)] = np.zeros((layer_dims[l],1))
return parameters
def forward_propagation():
data = np.random.randn(1000, 100000)
# layer_sizes = [100 - 10 * i for i in range(0,5)]
layer_sizes = [1000,800,500,300,200,100,10]
num_layers = len(layer_sizes)
parameters = initialize_parameters(layer_sizes)
A = data
for l in range(1,num_layers):
A_pre = A
W = parameters["W" + str(l)]
b = parameters["b" + str(l)]
z = np.dot(W,A_pre) + b #計算z = wx + b
A = np.tanh(z)
#畫圖
plt.subplot(2,3,l)
plt.hist(A.flatten(),facecolor='g')
plt.xlim([-1,1])
plt.yticks([])
plt.show()
複製程式碼
3.Xavier initialization Xavier initialization是 Glorot 等人為了解決隨機初始化的問題提出來的另一種初始化方法,他們的思想倒也簡單,就是儘可能的讓輸入和輸出服從相同的分佈,這樣就能夠避免後面層的啟用函式的輸出值趨向於0。他們的初始化方法為:
def initialize_parameters_he(layers_dims):
"""
Arguments:
layer_dims -- python array (list) containing the size of each layer.
Returns:
parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":
W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])
b1 -- bias vector of shape (layers_dims[1], 1)
...
WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])
bL -- bias vector of shape (layers_dims[L], 1)
"""
np.random.seed(3)
parameters = {}
L = len(layers_dims) # integer representing the number of layers
for l in range(1, L):
parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) * np.sqrt(1 / layers_dims[l - 1])
parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
return parameters
複製程式碼
來看下Xavier initialization後每層的啟用函式輸出值的分佈:
能夠看出,深層的啟用函式輸出值還是非常漂亮的服從標準高斯分佈。雖然Xavier initialization能夠很好的 tanH 啟用函式,但是對於目前神經網路中最常用的ReLU啟用函式,還是無能能力,請看下圖:4.He initialization
為了解決上面的問題,提出了一種針對ReLU的初始化方法,一般稱作 He initialization。初始化方式為:
def initialize_parameters_he(layers_dims):
"""
Arguments:
layer_dims -- python array (list) containing the size of each layer.
Returns:
parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":
W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])
b1 -- bias vector of shape (layers_dims[1], 1)
...
WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])
bL -- bias vector of shape (layers_dims[L], 1)
"""
np.random.seed(3)
parameters = {}
L = len(layers_dims) # integer representing the number of layers
for l in range(1, L):
parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) * np.sqrt(2 / layers_dims[l - 1])
parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
return parameters
複製程式碼
來看看經過He initialization後,當隱藏層使用ReLU時,啟用函式的輸出值的分佈情況:
全文(廢話)總結:
1.啟用函式:
tanh、softsign好於sigmoid用
用He initialization初始化
感謝
參考文獻
- Xavier Glorot et al., Understanding the Difficult of Training Deep Feedforward Neural Networks
- Kaiming He et al., Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classfication
- Andrew ng coursera 《deep learning》課
- 夏飛 《聊一聊深度學習的weight initialization》
5.blog.csdn.net/victoriaw/a…
6.blog.csdn.net/u012328159/…