神經網絡初始化

本文經過對《Understanding the difficulty of training deep feedforward neural networks》文章翻譯和解讀,和知乎、CSDN幾位博主的文章總結、分析深度網絡初始化方法。python

首先是《Understanding the difficulty of training deep feedforward neural networks》

敲黑板:這裏有一個發文章技巧,行不行有待驗證你們應該感受到通常的深度學習文章上來就是實驗,告訴讀者這個實驗結果好,而後由實驗結果再反向給出一些無從驗證的可能對可能不對的緣由。而這篇文章雖然總體來看比較簡單,但結構很是嚴謹:首先經過實驗分析標準初始化方法的問題;而後根據兩個目標——狀態方差和梯度方差保持不變推導出參數的特色,給出Xavier初始化方法的具體形式;最後經過實驗驗證Xavier初始化的效果確實不錯。bash

總結髮文章方法:

  • 實驗
  • 告訴讀者實驗結果好
  • 由結果反正無從驗證的可能對與不對的緣由,設立目標
  • 根據目標驗證猜測效果不錯

文章翻譯解讀網絡

下面之說一下我認爲的重點:

分析的前提:dom

    1. 網絡在初始化處於線性條件下,即激活活函數的導數爲1;
    1. 初始化的權值的mean 爲0,且獨立同分布的;
  • 3.輸入特徵 x 的 variance是相同的。通過一系列推導,獲得了下面這樣的結果:
    第一:
    第二:公式5有用哦:

第三:公式6有用;

推出這玩意來了之後呢, 下面是關鍵:

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:學習

就是說,爲了在前向傳播過程當中,可讓信息向前傳播,作法就是讓:激活單元的輸出值的方差持不變。爲何要這樣呢??有點小不理解。。

  1. 反向傳播:在反向傳播過程當中,也是爲了讓梯度能夠反向傳播,讓:對激活單元輸入值的梯度 保持不變,即:
    最後獲得的結論就是:

上面兩個式子折衷一下,爲:

因此呢,權值初始化時,服從這樣的分佈:
這個方法就叫作: normalized initialization.

在訓練過程當中,梯度問題:ui

這時,咱們就不能單純地用梯度的 variance 去分析了,由於已經不知足咱們的假設條件了啊。spa

文章後面的一大堆基本沒有什麼重點的東西了吧,我以爲。寫幾個以爲有必要的總結吧:.net

  1. softsign激活函數與雙曲正切函數相比,效果還 很不錯的,翻譯

  2. 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隨着迭代次數的變化示意圖爲:

可以看出,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()

複製代碼

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用

初始化 2.rule:
用He 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》
    5.blog.csdn.net/victoriaw/a…
    6.blog.csdn.net/u012328159/…
相關文章
相關標籤/搜索