項目地址:https://github.com/Daya-Jin/DL_for_learner
原博客:https://daya-jin.github.io/2018/12/13/NeuralNetworks/
神經網絡
模型結構
上圖是一個簡單的神經網絡,xi爲樣本特徵,y^爲網絡輸出,個變量之間的關係滿足:
a[1]y^=σ(i=1∑3θi[0]xi+b[0])=g(i=1∑4θi[1]a[1]+b[1])
其中σ(x)稱爲激活函數(activation function),g(x)爲輸出激活函數;輸入數據X所在的位置稱爲輸出層(input layer),aij所在的位置稱爲隱藏層(hidden layer),輸出預測結果的稱爲輸出層(output layer),圖中每一個圓圈稱爲神經元(Neuron)。
最常見的激活函數爲σ(x)=1+e−x1,即logistics regression中的sigmoid函數;而輸出激活函數需要根據模型任務來定,迴歸任務下爲g(x)=x,二分類任務下g(x)=1+e−x1,多分類任務下g(x)=softmax(這裏待補充);損失函數也由具體任務來定。
數學原理
保持與代碼實現上的一致性,令
a[1]=σ(xθ[0]+b[0])
x的形狀爲(1,3),a[0]的形狀爲(1,4),那麼有矩陣乘法的性質得θ[0]的形狀爲(3,4);
y^=σ(a[1]θ[1]+b[1])
y^的形狀爲(1,1),所以θ[1]的形狀爲(4,1)。由矩陣乘法性質不難推出,若當前層的單元數爲n[i],下層單元數爲n[i+1],則當前層權重矩陣的形狀爲:
dim(θ[i])=(n[i],n[i+1])
整個網絡的輸出可以寫成:
z[1]a[1]z[2]a[2]=a[0]θ[0]+b[0]=σ(z[1])=a[1]θ[1]+b[1]=σ(z[2])
以二分類爲例,簡單寫下神經網絡的反向傳播過程。爲便於後面的計算,先明確σ(x)=1+e−x1的導數:
∂x∂σ(x)=(1+e−x)2−1⋅(−e−x)=1+e−x1⋅1+e−xe−x+1−1=1+e−x1⋅(1−1+e−x1)=σ(x)⋅(1−σ(x))
首先,損失函數爲:
L=−ylna[2]−(1−y)ln(1−a[2])
逐層對變量求導:
Δa[2]Δz[2]Δθ[1]Δb[1]=∂a[2]∂L=−a[2]y+1−a[2]1−y=Δa[2]⋅∂z[2]∂a[2]=Δa[2]⋅a[2](1−a[2])=a[2]−y=Δz[2]⋅∂θ[1]∂z[2]=Δz[2]⋅a[1]=Δz[2]⋅∂b[1]∂z[2]=Δz[2]
更前一層的梯度爲:
Δa[1]Δz[1]Δθ[0]Δb[0]=Δz[2]⋅∂a[1]∂z[2]=Δz[2]⋅θ[1]=Δa[1]⋅∂z[1]∂a[1]=Δz[2]⋅θ[1]⋅a[1](1−a[1])=Δz[1]⋅∂θ[0]∂z[1]=Δz[1]⋅a[0]=Δz[1]⋅∂b[0]∂z[1]=Δz[1]
這是使用sigmoid函數爲激活函數下二分類神經網絡的梯度。其實如果在更深層的神經網絡中推導的話,假設有h層隱藏層,那麼除了最後一層隱藏層,前h−1層的梯度都可以寫成遞推表達式,因爲最後一層隱藏層的梯度是由損失函數推出來的,而前h−1層的梯度都是由當前層的輸出a推出來的。遞推式展開可以寫成累乘,那麼累乘就會有一個問題:當神經網絡層數過深並且每一層的梯度都小於1時,那麼越前面層的梯度就會越小,若都大於1,則越前面層的梯度就會越大。這就是深層神經網絡中梯度消失與梯度爆炸的問題。
原博客的Python實現指導
tensorflow實現指導
Activation Function
sigmoid
DNN初期默認採取的激活函數是sigmoid函數:
σ(x)=1+exp(−x)1
該函數圖像爲:
可以看到該函數在±5處就幾乎達到閾值了,相對應的是一個梯度飽和問題。每一層激活函數的輸入是z[i]=a[i−1]θ[i−1]+b[i−1],如果這個值稍微大一點(超出±5),那麼就會導致該層激活函數的梯度變得及其微小,影響反向傳播算法的執行。
另外,sigmoid函數的輸出範圍是(0,1),這會帶來另一個隱含問題。每一層的輸出都是正的,那麼該層對於優化參數的局部梯度爲:∂w[i]∂a[i+1]=a[i+1](1−a[i+1])a[i],該值恆爲正。在梯度下降法的優化過程中,該特性會導致參數在一次迭代中要麼都往正方向更新,要麼都往負方向更新,相當於每次更新參數都沿軸向更新。
sigmoid函數的第三個缺點就是其中的指數函數需要一定的計算量。
tanh
DNN激活函數的另一個選擇:
tanh(x)=ex+e−xex−e−x
其圖像爲:
可以看到tanh函數把輸入映射到了(−1,1)區間,雖然對梯度下降法的收斂有一定加速作用,但是對於sigmoid函數存在的另外兩個問題,tanh函數同樣存在,甚至比sigmoid函數更嚴重。
Rectified Linear Unit
整流線性單元(ReLU)激活函數的表達式爲:
f(x)=max(0,x)
其有如下優點:
- 在正域上不存在函數上限,也不會存在梯度飽和的問題
- 計算簡單
- 收斂速度快於sigmoid激活函數
- 更符合生物神經學
不過因爲ReLU的左半邊恆爲0,右邊恆爲正,同樣存在一個隱含的問題。ReLU函數在計算梯度時對負值是不響應的,負域的梯度恆爲0,如果在反向傳播時傳過來一個負的梯度值,那麼該神經元再往前傳播時的梯度貢獻始終是0,相當於該神經元已“壞死”。在某些情況下,如果在模型的整個訓練過程中,經過該神經元的梯度始終是負的,那麼該神經元在整個訓練過程持續性“壞死”,甚至還會影響到前面層的神經元,產生連鎖反應,導致前面的神經元更容易“壞死”或“持續性壞死”。
Leaky ReLU
帶泄露的ReLU是爲了解決ReLU“壞死”問題而出現的,其表達式爲:
f(x)=max(0.01x,x)
其圖像跟ReLU的區別在於負域,Leaky ReLU的負域不恆爲零,而是一個稍微傾斜的直線,這樣就避免了神經元“壞死”的問題。同時Leaky ReLU還有一個變種,當把負域直線的斜率參數化後,就得到了Parametric ReLU:
f(x)=max(αx,x)
其中α爲(0,1)區間的任何值。Leaky ReLU與PReLU的缺點顯而易見:引入了額外的超參數,並且在實踐上不見得比ReLU好。
ELU
待補充,其優點沒太看懂
Weight Initialization
Constant Initialization
如果把所有的權重參數都初始化爲同樣的常數,那麼同一層的所有的神經元只等效爲一個神經元。
Small random numbers
對於小型網絡,常見的初始化方法爲初始化一個服從N(0,0.01)的隨機分佈。但是該策略對大型網絡而言並不是一個好選擇,考慮前向傳播過程,由前往後每一層的輸出會越來越小,直至爲0,在反向傳播過程中同樣會造成梯度消失的問題。
類似地,如果權重初始化的太大,對於某些激活函數如sigmoid與tanh而言,會導致每一層激活後的輸出都在飽和區域,該層對參數的梯度非常小,然後造成梯度消失。
Xavier Initialization
看出權重參數初始化得太大或太小都不好,對於有飽和區的激活函數而言,需要儘量避免激活輸出進入飽和區。在講Xavier之前,先回顧一下方差的一些性質,對於獨立同分布的變量而言,有
D(X+Y)D(XY)=D(X)+D(Y)=D(X)D(Y)+D(X)E(Y)2+D(Y)E(X)2
若各變量都是零均值,上式可以寫爲:
D(X+Y)D(XY)=D(X)+D(Y)=D(X)D(Y)
現假設權重參數θ與輸入數據x都爲零均值,方差v的獨立同分布變量,那麼在忽略偏置項時某一層的線性輸出爲:
z[1]=xθ[0]=nI∑xjθj[0]
其中nI表示上一層的神經元數。那麼可以得到,當前層線性輸出值的均值爲0,方差爲:vz[1]=nI×vx×vθ[0]。我們希望的是每一層的輸入與輸出儘量同分布,那麼令vz[1]=vx,得:vθ[0]=1/nI。上面只考慮了正向傳播,那麼在反向傳播時,同樣希望每一層的參數梯度也同分布,那麼有:vθ[0]=1/nO,其中nO爲當前層的神經元數。
Xavier Initialization的推薦做法是將權重參數初始化爲一個均值爲0,方差爲nI+nI2。
Batch Normalization
在權重參數初始化一節中講到,如果希望神經網絡學到東西,那麼在正向傳播時激活輸出不能進入飽和區,即不能讓反向傳播過程中參數梯度過小,令每一層的輸出與輸入服從同分布即可解決該問題。Batch Normalization的思想就是預設一個分佈函數,並對每一層的線性輸出做操作,使其強行服從該分佈。
以標準正態分佈爲例,BN在對每一層的線性輸出都做一次Normalization,使得每層的激活函數總是接受一個服從標準正態分佈的輸入值:
z[i]z^[i]=a[i−1]θ[i−1]=D(z[i])z[i]−E(z[i])
經上述轉化過後的線性輸出服從標準正態分佈。當然標準正態分佈的線性輸出只是預設的一種特殊情況,爲了增強靈活性,BN在上述過程後還有一步線性變換的操作:
z^[i]=γz^[i]+β
最後這一部相當於把標準正態分佈推廣到了任意參數的正態分佈,其中γ與β可以通過學習得到。不難發現,若γ=D(z[i])且β=E(z[i]),則z^[i]=z[i]。