神經網路基本組成 - 池化層、Dropout層、BN層、全連線層 13
1. 池化層
在卷積網路中, 通常會在卷積層之間增加池化(Pooling) 層, 以降低特徵圖的引數量, 提升計算速度, 增加感受野, 是一種降取樣操作。池化是一種較強的先驗, 可以使模型更關注全域性特徵而非區域性出現的位置, 這種降維的過程可以保留一些重要的特徵資訊, 提升容錯能力, 並且還能在一定程度上起到防止過擬合的作用 。
在物體檢測中, 常用的池化有最大值池化(Max Pooling) 與平均值池化(Average Pooling) 。 池化層有兩個主要的輸入引數, 即核尺寸kernel_size與步長stride。 如圖3.7所示為一個核尺寸與步長都為2的最大值池化過程, 以左上角為例, 9、 20、 15與26進行最大值池化, 保留26。
下面是PyTorch對於池化層的實現。
1 import torch 2 from torch import nn 3 4 max_pooling = nn.MaxPool2d(2, stride=2) 5 aver_pooling = nn.AvgPool2d(2, stride=2) 6 7 input = torch.randn(1, 1, 4, 4) 8 print(input) 9 >> tensor([[[[ 1.2237, -0.8173, -0.2594, 0.1698], 10 [-0.1023, 0.6973, -0.6429, 0.8561],View Code11 [-0.3660, 0.1269, 0.2488, 0.0576], 12 [ 0.0859, 0.1622, -0.0725, -0.0237]]]]) 13 14 # 池化主要需要兩個引數, 第一個引數代表池化區域大小, 第二個引數表示步長 15 out_max = max_pooling(input) 16 print(out_max) 17 >> tensor([[[[1.2237, 0.8561], 18 [0.1622, 0.2488]]]]) 19 20 # 呼叫最大值池化與平均值池化, 可以看到size從[1, 1, 4, 4]變為了[1, 1, 2, 2]21 out_aver = aver_pooling(input) 22 print(out_aver) 23 >> tensor([[[[0.2503, 0.0309], 24 [0.0023, 0.0525]]]])
2. Dropout層
在深度學習中, 當引數過多而訓練樣本又比較少時, 模型容易產生過擬合現象。 過擬合是很多深度學習乃至機器學演算法的通病, 具體表現為在訓練集上預測準確率高, 而在測試集上準確率大幅下降。 2012年, Hinton等人提出了Dropout演算法, 可以比較有效地緩解過擬合現象的發生, 起到一定正則化的效果。
Dropout的基本思想如圖3.8所示, 在訓練時, 每個神經元以概率p保留, 即以1-p的概率停止工作, 每次前向傳播保留下來的神經元都不同, 這樣可以使得模型不太依賴於某些區域性特徵, 泛化效能更強。 在測試時, 為了保證相同的輸出期望值, 每個引數還要乘以p。 當然還有另外一種計算方式稱為Inverted Dropout, 即在訓練時將保留下的神經元乘以1/p, 這樣測試時就不需要再改變權重。
至於Dropout為什麼可以防止過擬合, 可以從以下3個方面解釋。
·多模型的平均: 不同的固定神經網路會有不同的過擬合, 多個取平均則有可能讓一些相反的擬合抵消掉, 而Dropout每次都是不同的神經元失活, 可以看做是多個模型的平均, 類似於多數投票取勝的策略。
·減少神經元間的依賴: 由於兩個神經元不一定同時有效, 因此減少了特徵之間的依賴, 迫使網路學習有更為魯棒的特徵, 因為神經網路不應該對特定的特徵敏感, 而應該從眾多特徵中學習更為共同的規律,這也起到了正則化的效果。
·生物進化: Dropout類似於性別在生物進化中的角色, 物種為了適應環境變化, 在繁衍時取雄性和雌性的各一半基因進行組合, 這樣可以適應更復雜的新環境, 避免了單一基因的過擬合, 當環境發生變化時也不至於滅絕。
在PyTorch中使用Dropout非常簡單, 示例如下:
import torch from torch import nn # PyTorch將元素置0來實現Dropout層, 第一個引數為置0概率, 第二個為是否原地操作 dropout = nn.Dropout(0.5, inplace=False) input = torch.randn(2, 64, 7, 7) output = dropout(input) print(output.shape)View Code
Dropout被廣泛應用到全連線層中, 一般保留概率設定為0.5, 而在較為稀疏的卷積網路中則一般使用下一節將要介紹的BN層來正則化模型, 使得訓練更穩定。
3. BN層
為了追求更高的效能, 卷積網路被設計得越來越深, 然而網路卻變得難以訓練收斂與調參。 原因在於, 淺層引數的微弱變化經過多層線性變換與啟用函式後會被放大, 改變了每一層的輸入分佈, 造成深層的網路需要不斷調整以適應這些分佈變化, 最終導致模型難以訓練收斂。
由於網路中引數變化導致的內部節點資料分佈發生變化的現象被稱做ICS(Internal Covariate Shift) 。 ICS現象容易使訓練過程陷入飽和區, 減慢網路的收斂。 前面提到的ReLU從啟用函式的角度出發, 在一定程度上解決了梯度飽和的現象, 而2015年提出的BN層, 則從改變資料分佈的角度避免了引數陷入飽
和區。 由於BN層優越的效能, 其已經是當前卷積網路中的“標配”。
BN層首先對每一個batch的輸入特徵進行白化操作, 即去均值方差過程。 假設一個batch的輸入資料為x: B={ x1,…,xm} , 首先求該batch資料的均值與方差, 如式(3-5) 和式(3-6) 所示。
以上公式中, m代表batch的大小, μB為批處理資料的均值, σ2B為批處理資料的方差。 在求得均值方差後, 利用式(3-7) 進行去均值方差操作:
白化操作可以使輸入的特徵分佈具有相同的均值與方差, 固定了每一層的輸入分佈, 從而加速網路的收斂。 然而, 白化操作雖然從一定程度上避免了梯度飽和, 但也限制了網路中資料的表達能力, 淺層學到的引數資訊會被白化操作遮蔽掉, 因此, BN層在白化操作後又增加了一個線性變換操作, 讓資料儘可能地恢復本身的表達能力, 如公式(3-7) 和公式(3-8) 所示。
公式(3-8) 中, γ與β為新引進的可學習引數, 最終的輸出為yi。
BN層可以看做是增加了線性變換的白化操作, 在實際工程中被證明了能夠緩解神經網路難以訓練的問題。 BN層的優點主要有以下3點:
·緩解梯度消失, 加速網路收斂。 BN層可以讓啟用函式的輸入資料落在非飽和區, 緩解了梯度消失問題。 此外, 由於每一層資料的均值與方差都在一定範圍內, 深層網路不必去不斷適應淺層網路輸入的變化,實現了層間解耦, 允許每一層獨立學習, 也加快了網路的收斂。
·簡化調參, 網路更穩定。 在調參時, 學習率調得過大容易出現震盪與不收斂, BN層則抑制了引數微小變化隨網路加深而被放大的問題, 因此對於引數變化的適應能力更強, 更容易調參。
·防止過擬合。 BN層將每一個batch的均值與方差引入到網路中, 由於每個batch的這兩個值都不相同, 可看做為訓練過程增加了隨機噪音,
可以起到一定的正則效果, 防止過擬合。
在測試時, 由於是對單個樣本進行測試, 沒有batch的均值與方差,通常做法是在訓練時將每一個batch的均值與方差都保留下來, 在測試時使用所有訓練樣本均值與方差的平均值。
PyTorch中使用BN層很簡單, 示例如下:
1 import torch 2 from torch import nn 3 # 使用BN層需要傳入一個引數為num_features, 即特徵的通道數 4 bn = nn.BatchNorm2d(64) 5 print(bn) 6 >> BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True) 7 8 # eps為公式中的є, momentum為均值方差的動量, affine為新增可學習引數 9 input = torch.randn(4, 64, 224, 224) 10 output = bn(input) 11 # BN層不改變輸入、 輸出的特徵大小 12 print(output.shape) 13 >> torch.Size([4, 64, 224, 224])View Code
儘管BN層取得了巨大的成功, 但仍有一定的弊端, 主要體現在以下兩點:
·由於是在batch的維度進行歸一化, BN層要求較大的batch才能有效地工作, 而物體檢測等任務由於佔用記憶體較高, 限制了batch的大小, 這會限制BN層有效地發揮歸一化功能。
·資料的batch大小在訓練與測試時往往不一樣。 在訓練時一般採用滑動來計算平均值與方差, 在測試時直接拿訓練集的平均值與方差來使用。 這種方式會導致測試集依賴於訓練集, 然而有時訓練集與測試集的資料分佈並不一致。
因此, 我們能不能避開batch來進行歸一化呢? 答案是可以的, 最新的工作GN(Group Normalization) 從通道方向計算均值與方差, 使用更
為靈活有效, 避開了batch大小對歸一化的影響。
具體來講, GN先將特徵圖的通道分為很多個組, 對每一個組內的引數做歸一化, 而不是batch。 GN之所以能夠工作的原因, 筆者認為是在特徵圖中, 不同的通道代表了不同的意義, 例如形狀、 邊緣和紋理等, 這些不同的通道並不是完全獨立地分佈, 而是可以放到一起進行歸一化分析。
4. 全連線層
全連線層(Fully Connected Layers) 一般連線到卷積網路輸出的特徵圖後邊, 特點是每一個節點都與上下層的所有節點相連, 輸入與輸出都被延展成一維向量, 因此從引數量來看全連線層的引數量是最多的,如圖3.9所示。
在物體檢測演算法中, 卷積網路的主要作用是從區域性到整體地提取影象的特徵, 而全連線層則用來將卷積抽象出的特徵圖進一步對映到特定維度的標籤空間, 以求取損失或者輸出預測結果。
在第2章中的感知機例子即是使用了三層的全連線網路進行分類,PyTorch使用全連線層需要指定輸入的與輸出的維度。
示例如下:
import torch from torch import nn # 第一維表示一共有4個樣本 input = torch.randn(4, 1024) linear = nn.Linear(1024, 4096) output = linear(input) print(input.shape) >> torch.Size([4, 1024]) print(output.shape) >> torch.Size([4, 4096])View Code
然而, 隨著深度學習演算法的發展, 全連線層的缺點也逐漸暴露了出來, 最致命的問題在於其引數量的龐大。 在此以VGGNet為例說明, 其第一個全連線層的輸入特徵為7×7×512=25088個節點, 輸出特徵是大小為4096的一維向量, 由於輸出層的每一個點都來自於上一層所有點的權重相加, 因此這一層的引數量為25088×4096≈108。 相比之下, VGGNet最後一個卷積層的卷積核大小為3×3×512×512≈2.4×106, 全連線層的引數量是這一個卷積層的40多倍。
大量的引數會導致網路模型應用部署困難, 並且其中存在著大量的引數冗餘, 也容易發生過擬合的現象。 在很多場景中, 我們可以使用全域性平均池化層(Global Average Pooling, GAP) 來取代全連線層, 這種思想最早見於NIN(Network in Network) 網路中, 總體上, 使用GAP有如下3點好處:
·利用池化實現了降維, 極大地減少了網路的引數量。
·將特徵提取與分類合二為一, 一定程度上可以防止過擬合。
·由於去除了全連線層, 可以實現任意影象尺度的輸入。