深度學習中神經網絡的幾種權重初始化方法


深度學習中神經網絡的幾種權重初始化方法

版權聲明:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接: https://blog.csdn.net/u012328159/article/details/80025785
深度學習中神經網絡的幾種權重初始化方法

       在深度學習中,神經網絡的權重初始化方法對(weight initialization)對模型的收斂速度和性能有着至關重要的影響。說白了,神經網絡其實就是對權重參數w的不停迭代更新,以期達到較好的性能。在深度神經網絡中,隨着層數的增多,我們在梯度下降的過程中,極易出現梯度消失或者梯度爆炸。因此,對權重w的初始化則顯得至關重要,一個好的權重初始化雖然不能完全解決梯度消失和梯度爆炸的問題,但是對於處理這兩個問題是有很大的幫助的,並且十分有利於模型性能和收斂速度。在這篇博客中,我們主要討論四種權重初始化方法:

  • 把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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

我們可以看看cost function是如何變化的: 

把w初始化為0

能夠看到代價函數降到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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

乘0.01是因為要把W隨機初始化到一個相對較小的值,因為如果X很大的話,W又相對較大,會導致Z非常大,這樣如果激活函數是sigmoid,就會導致sigmoid的輸出值1或者0,然后會導致一系列問題(比如cost function計算的時候,log里是0,這樣會有點麻煩)。隨機初始化后,cost function隨着迭代次數的變化示意圖為: 

隨機初始化

能夠看出,cost function的變化是比較正常的。但是隨機初始化也有缺點,np.random.randn()其實是一個均值為0,方差為1的高斯分布中采樣。當神經網絡的層數增多時,會發現越往后面的層的激活函數(使用tanH)的輸出值幾乎都接近於0,如下圖所示: 
隨機初始化分布

順便把畫分布的圖的代碼也貼出來吧:

 

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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

還記得我們在上一篇博客一步步手寫神經網絡中關於bp部分導數的推導嗎?激活函數輸出值接近於0會導致梯度非常接近於0,因此會導致梯度消失。 
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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

來看下Xavier initialization后每層的激活函數輸出值的分布: 

r Xavier 初始化分布

能夠看出,深層的激活函數輸出值還是非常漂亮的服從標准高斯分布。雖然Xavier initialization能夠很好的 tanH 激活函數,但是對於目前神經網絡中最常用的ReLU激活函數,還是無能能力,請看下圖: 
ReLU分布

當達到5,6層后幾乎又開始趨向於0,更深層的話很明顯又會趨向於0。 
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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

來看看經過He initialization后,當隱藏層使用ReLU時,激活函數的輸出值的分布情況: 

he初始化分布

效果是比Xavier initialization好很多。 現在神經網絡中,隱藏層常使用ReLU,權重初始化常用He initialization這種方法。 

 

關於深度學習中神經網絡的幾種初始化方法的對比就介紹這么多,現在深度學習中常用的隱藏層激活函數是ReLU,因此常用的初始化方法就是 He initialization。


以上所有代碼都放到github上了,感興趣的可以看一波:compare_initialization




參考文獻 
1. Xavier Glorot et al., Understanding the Difficult of Training Deep Feedforward Neural Networks 
2. Kaiming He et al., Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classfication 
3. Andrew ng coursera 《deep learning》課 
4. 夏飛 《聊一聊深度學習的weight initialization》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM