resnet过拟合_重读经典:完全解析特征学习大杀器ResNet
公眾號關注?“ML-CVer”
設為 “星標”,DLCV消息即可送達!
作者丨劉昕宸@知乎來源丨https://zhuanlan.zhihu.com/p/268308900編輯丨極市平臺導讀
?通過堆疊神經網絡層數(增加深度)可以非常有效地增強表征,提升特征學習效果,但是會出現深層網絡的性能退化問題,ResNet的出現能夠解決這個問題。本文用論文解讀的方式展現了ResNet的實現方式、分類、目標檢測等任務上相比SOTA更好的效果。
論文標題:Deep Residual Learning for Image Recognition
1 motivation
通過總結前人的經驗,我們常會得出這樣的結論:通過堆疊神經網絡層數(增加深度)可以非常有效地增強表征,提升特征學習效果。為什么深度的網絡表征效果會好?深度學習很不好解釋,大概的解釋可以是:網絡的不同層可以提取不同抽象層次的特征,越深的層提取的特征越抽象。因此深度網絡可以整合low-medium-high各種層次的特征,增強網絡表征能力。那好,我們就直接增加網絡深度吧!但是事情好像并沒有那么簡單!梯度優化問題:我們不禁發問:Is learning better networks as easy as stacking more layers?首先,深度網絡優化是比較困難的,比如會出現梯度爆炸/梯度消失等問題。不過,這個問題已經被normalized initialization和batch normalization等措施解決得差不多了。退化問題:好,那就直接上deeper network吧!但是新問題又來了:deeper network收斂是收斂了,卻出現了效果上的degradationdeeper network準確率飽和后,很快就退化了為什么會這樣呢?網絡更深了,參數更多了,應該擬合能力更強了才對啊!噢,一定是過擬合了。但似乎也不是過擬合的問題:因為56-layer網絡(紅線)的training error(左圖)也比20-layer網絡(黃線)要高,這就應該不是過擬合了啊!那么究竟是什么原因導致了deeper network degradation問題呢?現在,我們換一種思路來構建deeper network:the added layers are identity mapping, and the other layers are copied from the learned shallower model.(在原始的淺層網絡基礎上增加的層視為是identity mapping)也就是假設淺層網絡已經可以得到一個不錯的結果了,那我接下來新增加的層啥也不干,只是擬合一個identity mapping,輸出就擬合輸入,這樣總可以吧。這樣的話,我們覺得:這樣構建的深層網絡至少不應該比它對應的淺層training error要高。對吧。但是實驗又無情地表明:這樣卻又不能得到(與淺層網絡)一樣好的結果,甚至還會比它差!看來,深度網絡的優化并不容易的!總結一下:直覺上深度網絡應該會有更好的表征能力,但是事實卻是深度網絡結果會變差,由此我們認為深度網絡的優化部分出了問題,深度網絡的參數空間變得更復雜提升了優化的難度。那么,ResNet來了。我們就想啊,與其直接擬合一個desired underlying mapping?,不如讓網絡嘗試擬合一個residual mapping?。也就是:原先的映射?,被轉換為了?我們在這里假設優化殘差映射(residual mapping)?是比優化原映射?要容易的。
比如如果現在恒等映射(identity mapping)是最優的,那么似乎通過堆疊一些非線性層的網絡將殘差映射為0,從而擬合這個恒等映射,最種做法是更容易的??梢酝ㄟ^如上圖所示的短路連接(shortcut connection)結構來實現。shortcut就是設計的可以skip幾層的結構,在ResNet中就是起到了相當于一個最最簡單的identity mapping,其輸出被加到了stacked layers的輸出上。這樣做既沒有增加新的參數,也沒有增加計算復雜性。ResNet的具體結構,后面會詳細介紹。接下來,本文在ImageNet和CIFAR-10等數據集上做實驗,主要是想驗證2個問題:
deep residual nets比它對應版本的plain nets更好優化,training error更低。
deep residual nets能夠從更深的網絡中獲得更好的表征,從而提升分類效果。
2 solution
ResNet想做什么?learning residual functions with reference to the layer inputs, instead of learning unreferenced functions.理解不了沒關系,接著往下看。2.1 Residual Learning
前提:如果假設多個非線性層能夠漸近一個復雜的函數,那么多個非線性層也一定可以漸近這個殘差函數。令?表示目標擬合函數。所以與其考慮擬合?,不如考慮擬合其對應的殘差函數?.這兩種擬合難度可能是不同的。回到上面的討論,如果被增加層能夠被構建成identity mapping,那么深層網絡的性能至少不應該比其對應的淺層版本要差。這個表明:網絡是在使用多層非線性網絡趨近identity mapping做優化這里出了問題。殘差學習的這種方式,使得“如果identity mapping是最優的,網絡的優化器直接將殘差學習為0”就可以了,這樣做是比較簡單的。但其實在真實情況下,identity mapping不一定是最優的映射啊!原文說這種設計仍有意義,這種設計help to precondition the problem.也就是如果optimal function更接近identity mapping的話,優化器應該能夠比較容易找到殘差,而不是重新學習一個新的。后面實驗也表明了:殘差網絡各層輸出的標準差是比較低的(如上圖,后面會解釋),這也驗證了在identity mapping的基礎上學習殘差,確實是會更容易(identity mappings provide reasonable preconditioning.).這里解釋得還是比較含糊,但總結來說就是作者想解釋,訓練學習殘差會有效降低學習的難度,可能據此來解決深層網絡的性能退化問題。2.2 Identity Mapping by Shortcuts
再回顧一下這個著名的殘差塊圖片:identity mapping實現得非常之簡單,直接就用了個shortcut形式化就是:表示residual mapping,比如上圖,實際上就是2層網絡,也就是?然后直接將?與?element-wise相加。最后,給?套一個激活函數?.這么設計(shortcut)有個巨大的好處,就是沒有引入任何新的參數,也沒有增加計算復雜度。下面還有2個小問題:問題1:關于?因為?是element-wise相加,那么如果?和?維度不一樣怎么辦?方案一:直接對?補0.方案二:增加一個網絡層(參數為?),改變?的維度。即:事實上,每個shortcut我們都可以加一個映射層?(實現起來可以就是個感知機)。不需要做維度轉化時,?就是個方陣。但是后面實驗表明,直接shortcut就已經足夠好了,不需要再加那么多參數浪費計算資源。問題2:關于?的結構應該是什么樣的呢?可以是2層或者3層,也可以是更多;但是不要是1層,效果會不好。最后,shortcut設計不僅針對全連接網絡,卷積網絡當然也是沒問題的!2.3 網絡架構
受VGGNet(左邊)啟發,設計了34層的plain network(中間),以及其對應的residual network版本(右邊)。注意:中間plain network和右邊residual network層數一致,網絡參數也可以設計得完全一樣(element-wise相加維度不match時直接補0就不會增加任何learnable parameters)。34-layer plain network設計原則(遵循VGGNet):for the same output feature map size, the layers have the same number of filters
if the feature map size is halved, the number of filters is doubled so as to preserve the time complexity per layer
3 dataset and experiments
3.1 ImageNet on Classification3.1.1 與plain network的對比實驗這個實驗是核心,為了說明residual network能夠非常完美地解決“深度增加帶來的degradation”問題!!!左邊是plain network,右邊是ResNet;細線是train error,加粗線是val errorPlain network會出現網絡的層數增加,train error和val error都會升高什么原因呢???首先排除過擬合,因為train error也會升高
其次排除梯度消失,網絡中使用了batch normalization,并且作者也做實驗驗證了梯度的存在
事實上,34-layers plain network也是可以實現比較好的準確率的,這說明網絡在一定程度上也是work了的。
作者猜測:We conjecture that the deep plain nets may have exponentially low convergence rates.?層數的提升會在一定程度上指數級別影響收斂速度。下面是Residual Network與plain network的量化對比:觀察上面兩張圖,我們可以得出結論:
而ResNet卻真正實現了網絡層數增加,train error和val error都降低了,證明了網絡深度確實可以幫助提升網絡的性能。degradation problem在一定程度上得到了解決。
相對于plain 34-layers,ResNet 34-layers的top-1 error rate也降低了3.5%。resnet實現了在沒有增加任何參數的情況下,獲得了更低error rate,網絡更加高效。
從plain/residual 18-layers的比較來看,兩者的error rate差不多,但是ResNet卻能夠收斂得更快。
3.1.2 Identity v.s. Projection shortcuts
所謂projection shortcuts,就是:shortcuts包括了一個可學習參數(可以用來對齊維度,使得element-wise相加可以實現):設計了A,B,C三種實驗:A:單純使用identity shortcuts:維度不能對齊時使用zero padding來提升維度此方案沒有增加任何參數B:僅僅在維度需要對齊時使用projection shortcuts,其余均使用parameter-free的identity shortcutsC:全部使用projection shortcuts下面是三種方案的實驗結果:ABC三種方案均明顯好于plain版本C雖然結果稍微優于B、C,但是卻引入了大量的參數,增加了時空計算復雜度。作者認為:projection shortcuts are not essential for addressing the degradation problem.因此后面的實驗仍然采用A或者B結構。3.1.3 Deeper Bottleneck Architectures.
為了探索更深層的網絡,保證訓練時間在可控范圍內,作者重又設計了bottleneck版本的building block左邊是原版本,右邊是bottleneck版本bottleneck版本是將卷積核換成了1*1,3*3,1*1的size,雖然層數增加到了3層,但是降低了參數量。作者在這里是想探索深度的真正瓶頸,而不是追求很低的error rate,因此在這里使用了更加精簡的bottleneck building block50-layers:將34-layers的每個2-layer block換成了3-layers bottleneck block101-layers/152-layers:增加更多的3-layers bottleneck block網絡具體參數可參考如下圖:實驗結果如下所示:網絡越深,確實取得了更好的結果。Plain network的degradation problem似乎消失了。
3.2 CIFAR-10實驗與分析
實線是test error,虛線是train error左邊是plain network,中間是ResNet,右邊是ResNet with 110 layers and 1202 layers.結論基本與之前一致,但在1202層時,ResNet還是出現了degradation現象(結果比110層差),作者認為是過擬合。另外:Analysis of Layer Responses關于response:The responses are the outputs of each layer, after BN and before other nonlinearity (ReLU/addition).從上圖我們可以直接看出:ResNet較于plain network,一般來說response std更小。并且:deeper ResNet has smaller magni- tudes of responses這就說明了:residual functions(即??) might be generally closer to zero than the non-residual functions.
When there are more layers, an individual layer of ResNets tends to modify the signal less.(也就是后面逐漸就接近identity mapping,要擬合的殘差越來越小,離目標越來越近)
4 code review
ResNet實現非常簡單,網上各種實現多如牛毛,這里僅隨意找了個實現版本作為對照:代碼基于CIFAR-10的:2層的BasicBlock:class BasicBlock(nn.Module):expansion = 1
def __init__(self, in_planes, planes, stride=1, option='A'):
super(BasicBlock, self).__init__()
self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(planes)
self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
self.bn2 = nn.BatchNorm2d(planes)
self.shortcut = nn.Sequential()
if stride != 1 or in_planes != planes:
if option == 'A':
"""
For CIFAR10 ResNet paper uses option A.
"""
self.shortcut = LambdaLayer(lambda x:
F.pad(x[:, :, ::2, ::2], (0, 0, 0, 0, planes//4, planes//4), "constant", 0))
elif option == 'B':
self.shortcut = nn.Sequential(
nn.Conv2d(in_planes, self.expansion * planes, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(self.expansion * planes)
)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.bn2(self.conv2(out))
out += self.shortcut(x)
out = F.relu(out)
return outResNet骨架:解釋一下:forward函數中定義resnet骨架:
首:1層conv
身:由BasicBlock構成layer1、layer2、layer3,個數分別為??,因為每個BasicBlock有2層,所以總層數是?
尾:1層fc
def __init__(self, block, num_blocks, num_classes=10):
super(ResNet, self).__init__()
self.in_planes = 16
self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False)
self.bn1 = nn.BatchNorm2d(16)
self.layer1 = self._make_layer(block, 16, num_blocks[0], stride=1)
self.layer2 = self._make_layer(block, 32, num_blocks[1], stride=2)
self.layer3 = self._make_layer(block, 64, num_blocks[2], stride=2)
self.linear = nn.Linear(64, num_classes)
self.apply(_weights_init)
def _make_layer(self, block, planes, num_blocks, stride):
strides = [stride] + [1]*(num_blocks-1)
layers = []
for stride in strides:
layers.append(block(self.in_planes, planes, stride))
self.in_planes = planes * block.expansion
return nn.Sequential(*layers)
def forward(self, x):
out = F.relu(self.bn1(self.conv1(x)))
out = self.layer1(out)
out = self.layer2(out)
out = self.layer3(out)
out = F.avg_pool2d(out, out.size()[3])
out = out.view(out.size(0), -1)
out = self.linear(out)
return out最后,像堆積木一樣,通過設置layer1、layer2、layer3的BasicBlock個數來堆出不同層的ResNet:def resnet20():
return ResNet(BasicBlock, [3, 3, 3])
def resnet32():
return ResNet(BasicBlock, [5, 5, 5])
def resnet44():
return ResNet(BasicBlock, [7, 7, 7])
def resnet56():
return ResNet(BasicBlock, [9, 9, 9])
def resnet110():
return ResNet(BasicBlock, [18, 18, 18])
def resnet1202():
return ResNet(BasicBlock, [200, 200, 200])
5 conclusion
ResNet核心就是residual learning和shortcut identity mapping,實現方式極其簡單,卻取得了極其好的效果,在分類、目標檢測等任務上均是大比分領先SOTA,這種非常general的創新是非常不容易的,這也是ResNet備受推崇的原因吧!另外給我的啟示就是:不僅僅是"talk is cheap, show me the code"了,而是"code is also relatively cheap, show me ur sense and thinking"!推薦閱讀:
推薦!CV預訓練模型全集!
國內外優秀的計算機視覺團隊匯總|最新版
YOLOv4中的數據增強
總結
以上是生活随笔為你收集整理的resnet过拟合_重读经典:完全解析特征学习大杀器ResNet的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nginx根据参数转发到不同服务器_Ng
- 下一篇: 生成对抗网络gan原理_必读!TOP10