進一步聊聊weight initialization

深度學習模型訓練的過程本質是對weight(即參數W)進行更新,這須要每一個參數有相應的初始值。
有人可能會說:「參數初始化有什麼難點?直接將全部weight初始化爲0或者初始化爲隨機數!」 對一些簡單的機器學習模型,或當optimization function是convex function時,這些簡單的方法確實有效。
然而對於深度學習而言,非線性函數被瘋狂疊加,這即是一個非凸函數,如何選擇參數初始值便成爲一個值得探討的問題。
研究的目的是:選擇更適合的初始化方法,使得目標函數更容易被優化。html

初始化爲0

若是全部參數都被初始化爲0,那麼全部神經元的輸出將是相同的,反向傳播時每一層內全部的神經元的梯度也是相同的,這顯然是一個不可行的方案。node

預訓練

pre-training是早期訓練神經網絡的有效初始化方法。第一步,將神經網絡的每一層取出來,構建auto-encoder作訓練,使得輸入層和輸出層保持一致。在這個過程當中參數獲得更新,造成初始值;第二步,將每一層放回神經網絡中,使用訓練數據fine-tuning網絡。
隨着數據量的增長以及activation function的發展,這種方案已不多采用,你們直接奔着訓練的主題去了。如今咱們每每是拿任務A(imagenet競賽)中訓練好的模型(可稱爲pre-training model),將其放在任務B上作fine-tuning。網絡

random initialization

隨機初始化,是最容易想到的方案。可是一旦隨機分佈選擇不當,會致使網絡優化陷入困境。app

data = tf.constant(np.random.randn(2000, 800))
layer_sizes = [800 - 50 * i for i in range(0,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 
    fc = tf.matmul(X, W)
    fc = tf.nn.tanh(fc)
    fcs.append(fc)

這裏咱們建立了一個10層的神經網絡,非線性變換爲tanh,每一層的參數都是隨機正態分佈,均值爲0,標準差爲0.01。每一層輸出值分佈的直方圖:dom

隨着層數的增長,網絡輸出迅速向0靠攏。在反向傳播中,根據鏈式法則,梯度等於當前輸入x(上一層的輸出)乘之後一層的梯度,x趨向於0,意味着梯度將很小,參數更新緩慢。機器學習

調整初始化策略,增長方差:函數

W = tf.Variable(np.random.randn(node_in, node_out))

均值仍然爲0,標準差如今變爲1,此時每一層輸出值分佈的直方圖:
學習

此時,全部值會集中到-1或1附近,神經元飽和saturated了,也就是說tanh在-1和1附近的gradient都接近0,參數亦難更新。優化

Xavier initialization

澤維爾初始化的基本思想是:保持輸入和輸出的方差一致。注意:Xavier推到過程是基於線性函數的,可是它在非線性的神經元中依然表現不錯。code

W = tf.Variable(np.random.randn(node_in, node_out)) / np.sqrt(node_in)

輸出值在不少層以後依然保持着良好的分佈,這頗有利於咱們優化神經網絡!以前談到Xavier是在線性函數上推導得出,這說明它對非線性函數並不具備普適性,因此這個例子僅僅說明它對tanh頗有效,那麼對於目前最經常使用的ReLU神經元呢?

W = tf.Variable(np.random.randn(node_in, node_out)) / np.sqrt(node_in)
...
fc = tf.nn.relu(fc)

前面看起來還不錯,可是後面的趨勢倒是愈來愈接近0。幸運的是,He initialization能夠用來解決ReLU初始化的問題。

He initialization

He initialization的思想是:在ReLU網絡中,假定每一層有一半的神經元被激活,另外一半爲0,因此,要保持variance不變,只須要在Xavier的基礎上再除以2。

W = tf.Variable(np.random.randn(node_in,node_out)) / np.sqrt(node_in/2)
...
fc = tf.nn.relu(fc)

效果獲得了很大改善。

Batch Normalization Layer

BN是一種巧妙又粗暴的方法,能夠來削弱bad initialization的影響。在網絡傳播中,咱們想要的是在非線性activation以前,輸出值應該有較好的分佈(如高斯分佈),以便於反向傳播時計算梯度。BN的作法就是將輸出值強制作一次高斯歸一化和線性變換。BN的知識能夠參考LRN和Batch Norm
隨機初始化,有Batch Normalization:

W = tf.Variable(np.random.randn(node_in, node_out)) * 0.01
...
fc = tf.contrib.layers.batch_norm(fc, center=True, scale=True, is_training=True)
fc = tf.nn.relu(fc)

很容易看到,Batch Normalization的效果很是好。

參考

Xavier initialization是由Xavier Glorot et al.在2010年提出,He initialization是由Kaiming He et al.在2015年提出,Batch Normalization是由Sergey Ioffe et al.在2015年提出。

相關文章
相關標籤/搜索