原文連接:https://www.leiphone.com/news/201703/3qMp45aQtbxTdzmK.htmlhtml
原文是谷歌大神工程師寫的一篇文章,看到以後以爲很不錯,可以直觀地讓你深刻理解權重初始化方式以及激活函數對模型訓練的影響。node
本文是對原文的解讀,並附上了本身的理解以及代碼實現。網絡
首先,一個好的權重初始化方法可以幫助神經網絡更快的找到最優解決方案。app
初始化權重的必要條件1:各網絡層激活值不會落在激活函數的飽和區域;dom
初始化權重的必要條件2:各網絡層激活值不會都很是接近0,也不會都遠離0,最好是均值爲0(以0爲中心分佈)iphone
一、初始化爲0的可行性:函數
不可行,若是將全部的權重都初始化爲0,那麼全部神經元的輸出數值都是同樣的,那麼反向傳播時,同一層的全部的梯度都是同樣的,權重更新也是同樣的,這樣的訓練是沒有意義的。學習
二、可行的幾種初始化方式:優化
pre-training:spa
便是利用訓練好的模型的參數進行初始化,而後再作fine-tuning。
Random initialization:
10層網絡,採用隨機初始化權重,每層輸出數據分佈
import tensorflow as tf import numpy as np import matplotlib.pyplot as plt data = tf.constant(np.random.randn(2000, 800),dtype=tf.float32) layer_sizes = [800 - 50 * i for i in range(0,10)]#10層網絡,輸入和 num_layers = len(layer_sizes) fcs = [] # To store fully connected layers' output for i in range(0, num_layers-1): X = data if i == 0 else fcs[i - 1] node_in = layer_sizes[i] node_out = layer_sizes[i + 1] # W = tf.Variable(np.random.randn(node_in, node_out))# * 0.01 W = tf.Variable(np.random.randn(node_in, node_out),dtype=tf.float32)*0.01 fc = tf.matmul(X, W) fc = tf.nn.tanh(fc) fcs.append(fc) plt.figure() with tf.Session() as sess: sess.run(tf.global_variables_initializer()) for i in range(0,num_layers-1): plt.subplot(1,num_layers,i+1) x=fcs[i] x=np.array(x.eval()) x=x.flatten() plt.hist(x=x,bins=20,range=(-1,1)) plt.show()
建立10層神經網絡,激活函數爲tanh,每層權重都採用隨機正態分佈,均值0,標準差爲0.01,如下爲輸出分佈:
由上圖能夠看出,隨着網絡層數的增長,輸出數值分佈逐漸往0靠近,後面幾層輸出都很是接近0。根據f=W.X+b可知反向傳播時對權重W求偏導時,當前層輸出的數值X便是反向傳播時計算的梯度中的乘積因子,致使梯度很是小,使得參數更新困難。
將以上正態分佈初始化參數值調大,標準差變爲1:
W = tf.Variable(np.random.randn(node_in, node_out))
再看看輸出分佈:
能夠看出輸出數值都集中在1和-1附近,激活函數用的tanh函數,可見已經落入激活函數的飽和區域,tanh函數1和-1附近的梯度爲0,參數難以被更新。
Xavier initialization 能夠解決上面的問題,Xavier初始化是保持輸入和輸出的方差一致,這樣能夠避免全部輸出值都趨向於0:
W = tf.Variable(np.random.randn(node_in, node_out)) / np.sqrt(node_in)
下面就是採用Xavier初始化以後的每層的數據分佈直方圖:
哇,輸出不少層以後依然保持良好的分佈,很是有利於咱們優化神經網絡!
xavier initialization是在線性函數上推導出來,這說明它對非線性函數並不具備廣泛適用性,這裏僅僅是討論了tanh激活函數,下面討論ReLu激活函數的試驗
由上圖能夠看出,因爲relu函數特性致使各層網絡數據輸出都偏向0-1,輸出分佈不是zero-centred,且到後面幾層,數據都在0附近 。
看來Xavier初始化對於Relu激活函數不是很適用。下面看看He initialization是否能解決relu初始化問題,He initialization的思想:在relu 網絡中,假定每一層有一半的神經元被激活,另外一半爲0,因此要保持方差不變,只需在Xavier的基礎上除以2。
W = tf.Variable(np.random.randn(node_in, node_out)) / np.sqrt(node_in/2)
......
fc = tf.nn.relu(fc)
下面看看輸出分佈,雖然不是zero-centred(以0爲中心即0均值)可是起碼輸出數值分佈很穩定都在0-1之間,再也不像以前用xavier那樣在0附近,效果不錯,推薦在relu網絡中使用。
Batch Normalization Layer:
Batch Normalization是一種巧妙粗暴的方法來削弱很差的initialization帶來的影響,想要在非線性激活以前,輸出值應該有比較好的分佈(例如高斯分佈),以便於反向傳播計算梯度,更新權重。Batch Normalization將輸出值強行作了一次Gaussian Normalization和線性變換。
Batch Normalization中全部的操做都是平滑可導,這使得反向傳播時候能夠有效學習到相應的參數Υβ,Bach Normalization 在train 和test時行爲有所差異。訓練時的
μβ和σβ由當前batch計算得出;在testing時μβ和σβ應該使用訓練時保存的均值或相似的通過處理的值,而不是當前batch計算
Batch Normalization 試驗:
Relu激活,隨機初始化,無Batch Normal:
隨機初始化,有batchNormalization:
fc=tf.contrib.layers.batch_norm(fc,center=True,scale=True,is_training=True)
#這裏注意須要將以前的輸入數據,輸出數據所有轉換成dtype=float32,batchnorm要求數據類型爲float32,數據不一致會報錯哦
由圖上能夠看出,加了batch Normalization以後,隨着網絡的加深,後面幾層的輸出數據分佈仍然保持的很好,沒有趨向於0,效果不錯。
初始化推薦
·在ReLU activation function中推薦使用Xavier Initialization的變種,稱之爲He Initialization:
總結:良好的權重初始化和激活函數可讓數據在網絡中正常流動,權重正常更新,以達到學習的目的,經過以上實驗能夠直觀的理解權重初始化和激活函數對輸出分佈的影響。