神經網絡中還有一些激活函數,池化函數,正則化和歸一化函數等。需要詳細看看,啃一啃吧。。
1. 激活函數
1.1 激活函數作用
在生物的神經傳導中,神經元接受多個神經的輸入電位,當電位超過一定值時,該神經元激活,輸出一個變換后的神經電位值。而在神經網絡的設計中引入了這一概念,來增強神經網絡的非線性能力,更好的模擬自然界。所以激活函數的主要目的是為了引入非線性能力,即輸出不是輸入的線性組合。
假設下圖中的隱藏層使用的為線性激活函數(恆等激活函數:a=g(z)),可以看出,當激活函數為線性激活函數時,輸出不過是輸入特征的線性組合(無論多少層),而不使用神經網絡也可以構建這樣的線性組合。而當激活函數為非線性激活函數時,通過神經網絡的不斷加深,可以構建出各種有趣的函數。

1.2 激活函數發展
激活函數的發展過程:Sigmoid -> Tanh -> ReLU -> Leaky ReLU -> Maxout (目前大部分backbone中都采用ReLU或Leaky ReLU)。還有一個特殊的激活函數Softmax,但一般只用在網絡的最后一層,進行最后的分類和歸一化。借一張圖總結下:

sigmoid函數:能將輸入值映射到0-1范圍內,目前很少用作隱藏層的激活函數,用在二分類中預測最后層輸出概率值。函數特點如下:

存在問題:
- Sigmoid函數飽和使梯度消失。當神經元的激活在接近0或1處時會飽和,在這些區域梯度幾乎為0,這就會導致梯度消失,幾乎就有沒有信號通過神經傳回上一層。
- Sigmoid函數的輸出不是零中心的。因為如果輸入神經元的數據總是正數,那么關於
的梯度在反向傳播的過程中,將會要么全部是正數,要么全部是負數,這將會導致梯度下降權重更新時出現z字型的下降。
tanh雙曲正切函數:將輸入值映射到-1—1范圍內,目前很少用做隱藏層激活函數。

Tanh解決了Sigmoid的輸出是不是零中心的問題,但仍然存在飽和問題。
(為了防止飽和,現在主流的做法會在激活函數前多做一步batch normalization,盡可能保證每一層網絡的輸入具有均值較小的、零中心的分布。)
ReLU函數(Rectified Linear unit):相較於sigmoid和tanh函數,計算比較簡單,收斂速度較快,是目前主要的激活函數。
對比sigmoid類函數主要變化是:1)單側抑制;2)相對寬闊的興奮邊界;3)稀疏激活性。

存在問題:ReLU單元比較脆弱並且可能“死掉”,而且是不可逆的,因此導致了數據多樣化的丟失。通過合理設置學習率,會降低神經元“死掉”的概率。
Leaky ReLU:相比於ReLU,使負軸信息不會全部丟失,解決了ReLU神經元“死掉”的問題。

PReLU(Parametric ReLU): Leaky ReLU函數中的a,在訓練過程中固定不變,常取值為0.01,而PReLU函數的a是一個學習參數,會隨着梯度變化而更新。

ELUs函數(Exponential Linear Units):負半軸為指數函數,使x=0時的導數變化更加平滑


softmax: 用於多分類網絡的輸出,預測每一類的概率值,目的是讓大的更大。

參考:https://www.jianshu.com/p/0cf1aff51117
https://www.cnblogs.com/lliuye/p/9486500.html
https://zhuanlan.zhihu.com/p/32610035
2. Batch Normalization(批量歸一化)
論文:Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift
2.1 背景
神經網絡越深時,出現了兩個問題,一是網絡十分難訓練,二是梯度消失。
而容易訓練的數據,具有如下特征:獨立同分布(Independent Identical Distribution); 不存在內部協方差偏移現象(Internal Covaraiant Shift)。
因此人們推測:在進行網絡訓練時,參數的變化導致每一層的輸入分布會發生改變,進而上層的網絡需要不停地去適應這些分布變化,使得我們的模型訓練變得困難,並且這種微弱變化隨着網絡層數的加深而被放大(類似蝴蝶效應),這種現象便被稱為Internal Covaraiant Shift。
Batch Normalization的原論文作者給了Internal Covariate Shift一個較規范的定義:在深層網絡訓練的過程中,由於網絡中參數變化而引起內部結點數據分布發生變化的這一過程被稱作Internal Covariate Shift。這句話該怎么理解呢?,我們定義每一層的線性變換為
,其中
代表層數;非線性變換為
,其中
為第
層的激活函數。隨着梯度下降的進行,每一層的參數
與
都會被更新,那么
的分布也就發生了改變,進而
也同樣出現分布的改變。而
作為第
層的輸入,意味着
層就需要去不停適應這種數據分布的變化,這一過程就被叫做Internal Covariate Shift。
總結Internal Covariate Shift帶來的問題有兩個:
(1)上層網絡需要不停調整來適應輸入數據分布的變化,導致網絡學習速度的降低
(2)網絡的訓練過程容易陷入梯度飽和區,減緩網絡收斂速度
(如采用飽和激活函數sigmoid時,數據分布落在了|x|>4.6的梯度飽和區域,即梯度為0)
2.2 解決思路
ICS(Internal Covariate Shift)產生的原因是由於參數更新帶來的網絡中每一層輸入值分布的改變,並且隨着網絡層數的加深而變得更加嚴重,因此我們可以通過固定每一層網絡輸入值的分布來對減緩ICS問題。
(1)白化whitening: 主要是PCA白化與ZCA白化
PCA白化保證了所有特征分布均值為0,方差為1;而ZCA白化則保證了所有特征分布均值為0,方差相同。
白化是對輸入數據分布進行變換,進而達到以下兩個目的:使得輸入特征分布具有相同的均值與方差;去除特征之間的相關性。
(2)Batch Normalization:Batch Normalization是對白化的簡化版,從而減少計算量和易於實現。這是因為白化存在兩個問題:
白化過程計算成本太高,並且在每一輪訓練中的每一層我們都需要做如此高成本計算的白化操作
白化過程由於改變了網絡每一層的分布,因而改變了網絡層中本身數據的表達能力。底層網絡學習到的參數信息會被白化操作丟失掉。
Batch Normalization實現算法:
(1) 白化計算量大,所以簡化為對每個特征維度進行Normalization,讓每個特征均值為0, 方差為1。 同時只是對每一個Mini-Batch進行Normalization
(2) 白化削弱了網絡中每一層輸入數據表達能力,所以Normalization后再加一個線性變換,盡可能恢復數據的表達能力。
論文中的算法公式如下:(注意求均值時是一個mini-batch上,同一個維度特征中數據的均值)

用pytorch使用了下BN方法, 並自己實現BN計算過程,代碼如下:
#coding:utf-8 import torch import torch.nn as nn m = nn.BatchNorm2d(num_features=3, momentum=1) #默認的momentum=0.1,不設為1時,計算結果會和自己計算的不一樣 input = torch.randn(4,3,4,4) #可以理解為batch為4, 3個channel, feature map尺寸為4*4 output = m(input) print(output) print(m.running_mean) #3個channnel的mean print(m.running_var) #3個channnel的var print(list(m.parameters())) #3個channel依次采用的γ 、β #自己計算BN weights = list(m.parameters()) output2 = torch.zeros(input.size()) me, va = [], [] for i in range(3): m = input[:, i, :, :].mean() #m1 = input[:, 0, :, :].sum()/(4*4*4), 四個batch,每個batch的feature map尺寸為4*4 v = input[:, i, :, :].var() me.append(m) va.append(v) output2[:, i, :, :] = ((input[:, i, :, :] - m)/v.sqrt()) * weights[0][i].item() + weights[1][i].item() print(output2) #和output相差0.001左右 print(me) print(va)
2.3 BN的作用
(1)BN使得網絡中每層輸入數據的分布相對穩定,加速模型學習速度
(2)BN使得模型對網絡中的參數不那么敏感,簡化調參過程,使得網絡學習更加穩定
(3)BN允許網絡使用飽和性激活函數(例如sigmoid,tanh等),緩解梯度消失問題
(4)BN具有一定的正則化效果
在Batch Normalization中,由於我們使用mini-batch的均值與方差作為對整體訓練樣本均值與方差的估計,盡管每一個batch中的數據都是從總體樣本中抽樣得到,但不同mini-batch的均值與方差會有所不同,這就為網絡的學習過程中增加了隨機噪音,與Dropout通過關閉神經元給網絡訓練帶來噪音類似,在一定程度上對模型起到了正則化的效果。另外,原作者通過也證明了網絡加入BN后,可以丟棄Dropout,模型也同樣具有很好的泛化效果。
參考:https://zhuanlan.zhihu.com/p/34879333
https://zhuanlan.zhihu.com/p/24810318
3. Dropout
論文:Improving neural networks by preventing co-adaptation of feature detectors
3.1 背景
在進行神經網絡的訓練時,經常會出現過擬合的現象,特別是模型的參數太多,而訓練樣本又太少。過擬合現象表現為:訓練集上損失函數較小,准確率高,測試集上損失函數大,准確率較低。而Dropout策略能在一定程度上緩解過擬合。
dropout策略:前向傳播的時候,讓神經元的激活值以一定的概率p停止工作,這樣部分特征不會傳遞下去,可以使模型泛化性更強,因為它不會太依賴某些局部的特征。如下圖所示:

3.2 dropout算法實現
dropout算法的具體實現有兩種,目前多用第二種,且概率常設置為0.5
(1)訓練階段以概率p舍棄神經元輸出值,測試階段神經元輸出值乘以概率1-p。numpy實現代碼如下:
#coding:utf-8 import numpy as np (1)訓練階段以概率p舍棄神經元輸出值,測試階段神經元輸出值乘以概率1-p #train def dropout_train_step(x, p): zeros = np.zeros_like(x) x = np.max((zeros, input),axis=3) #ReLU mask = np.random.rand(*(x.shape)) < p #randn產生0-1的隨機分布,其中值小於概率值p的為0 return x*mask #dropout,值小於概率值p的會被舍棄掉 #test def dropout_test_step(x, p): zeros = np.zeros_like(x) x = np.max((zeros, input),axis=3) #ReLU return x*(1-p) #直接乘以概率1-p, 因為每個值保留的概率值為1-p """ 由於不確定訓練階段舍棄的是那些神經元的值,測試階段采取求期望的方式,推理如下: 假設x=[x0, x1], p=0.3; 在訓練階段的mask會出現四種情況:[0, 0], [0, 1], [1, 1], [1, 0], 每種情況的輸出如下: mask=[0, 0]時, 概率為p1 = p*p=0.09, 結果為r1=[x0, x1]*[0, 0]=[0, 0] mask=[0, 1]時, 概率為p2 = p*(1-p)=0.21, 結果為r2=[x0, x1]*[0, 1]=[0, x1] mask=[1, 1]時, 概率為p3 = (1-p)*(1-p)=0.49, 結果為r3=[x0, x1]*[1, 1]=[x0, x1] mask=[1, 0]時, 概率為p4 = p*(1-p)=0.21, 結果為r4=[x0, x1]*[1, 0]=[x0, 0] 取平均值 r = (r1*p1 + r2*p2 +r3*p3 + r4*p4) = [0.7*x0, 0.7*x1] = x*(1-p) 因此在測試階段,可以直接采用輸出值乘以概率 """
由於不確定訓練階段舍棄的是那些神經元的值,測試階段采取求期望的方式,推理如下:
假設x=[x0, x1], p=0.3; 在訓練階段的mask會出現四種情況:[0, 0], [0, 1], [1, 1], [1, 0], 每種情況的輸出如下:
mask=[0, 0]時, 概率為p1 = p*p=0.09, 結果為r1=[x0, x1]*[0, 0]=[0, 0]
mask=[0, 1]時, 概率為p2 = p*(1-p)=0.21, 結果為r2=[x0, x1]*[0, 1]=[0, x1]
mask=[1, 1]時, 概率為p3 = (1-p)*(1-p)=0.49, 結果為r3=[x0, x1]*[1, 1]=[x0, x1]
mask=[1, 0]時, 概率為p4 = p*(1-p)=0.21, 結果為r4=[x0, x1]*[1, 0]=[x0, 0]
取平均值 r = (r1*p1 + r2*p2 +r3*p3 + r4*p4) = [0.7*x0, 0.7*x1] = x*(1-p)
因此在測試階段,可以直接采用輸出值乘以概率1-p
(2)訓練階段以概率p舍棄神經元輸出值,並除以概率值1-p,測試階段不處理。numpy實現代碼如下:
#(2)訓練階段以概率p舍棄神經元輸出值,並除以概率值1-p,測試階段不處理 #train def dropout_train_step(x, p): zeros = np.zeros_like(x) x = np.max((zeros, input),axis=3) #ReLU mask = np.random.rand(*(x.shape)) < p #randn產生0-1的隨機分布,其中值小於概率值p的為0 return x*mask/(1-p) #dropout,值小於概率值p的會被舍棄掉 #test def dropout_test_step(x, p): zeros = np.zeros_like(x) x = np.max((zeros, input),axis=3) #ReLU return x #訓練階段除以了概率1-p, 所以測試時不處理 if __name__=="__main__": input = np.random.randn(4, 3, 2, 2) print(dropout_train_step(input, 0.5))
由上面(1)可知,在測試階段需要乘以概率1-p,可以改為在訓練階段除以概率1-p, 這樣測試階段可以的不用處理
4.正則化和歸一化(Regularization and Normalization)
4.1 正則化(Regularization)
正則化(Regularization):在機器學習中,為了遏制過擬合,常使用正則化的方法。即在loss函數中引入正則化項,其實質是削弱特征值對loss的影響。
正則化形式:如下圖中紅色框中部分即引入的正則化項,依次為線性回歸,邏輯回歸和神經網絡中的表示形式:

正則化原理: 下圖中,對比Linear regression的正則化前后表達式,可以發現loss函數引入正則化項后,在進行梯度下降時,原參數θj 引入了一個權重(綠框部分),因此對應的輸入數據對結果的影響變小了,從而削弱了特征值的影響。

正則化種類:上述式子中都采用的是L2 Regularization,還有L1 Regularization。如下圖所示:

4.2 歸一化(Normalization)
歸一化:一條數據中常有多個特征,而每個特征維度上數據大尺度不一致,如房子有價格和房間數兩個特征,價格都是過萬的數字,而房間數都是十以內,差距較大,為了使其具有可比性,常在每個特征維度上進行歸一化,將數據歸一化到0-1范圍內。歸一化方法如下所示:

參考:https://www.zhihu.com/question/20924039/answer/131421690
https://www.cnblogs.com/maybe2030/p/9231231.html
