各位好,我是Kevin. 忽然發現頗久沒有更新我的medium...可能都快長蜘蛛網了(驚!
所以最近推出了一個新概念…就是…隨筆!!意思就是該篇文章幾乎是想到甚麼寫什麼…然後也不一定會有一個good end,可能會斷在某個奇怪的地方:),就是我暫時想不出來怎麼寫…未來某一天我會再把它補完。
Anyway ,因為前陣子比較忙,在面試一些公司,有些公司要到好幾面…(汗
其中我發現,有好幾家公司會問有關CNN深度學習-預訓練模型框架的問題。
當然還有另外一些很常被問到的題型,我之後有時間再寫文章提出。
這一篇主要是想整理自己對預訓練CNN模型框架的理解,和梳理一些原則和想法,除了對我自己有複習作用之外也幫讀者做一個小整理。

在深度學習上,竟有如此快速的進展在這約8~9年間!
倒回至2012年,AlexNet以第一名的成績: scored 63.3% 的準確率(Accuracy)拿下了ImageNet圖片分類競賽的第一名。
現在,有絕大多數的人都在使用名為"EfficientNet"的架構做teacher-student training.
如果我們繪製出所有在imagenet上作品的準確性報告,會得到以下的結果:

在本文中,我們將重點於介紹卷積神經網路(CNN)架構的演變。我們將重點介紹基本原則,而不是報告簡單的數據。為了提供另一個視覺概述,可以在單個圖像中獲得2018年之前性能最佳的CNN框架:

請注意,X軸的(G-FLOPs) 表示模型的複雜性,而在y-axis垂直軸上,我們具有 Imagenet 準確度。圓的半徑表示參數的大小。
從上圖中可以明顯看出,擁有較多的參數並不見得總是能帶來更好的準確性。我們將嘗試概括對CNN更廣泛的視角,看看這是為什麼。
這邊推薦一門Andrew Ng 的一門CNN課程。:Convolutional Neural Networks | Coursera 有解釋到此的概念。
術語介紹:
首先,我們必須定義一些術語:
- 更寬的(wider)網路意味著卷積層中有更多的特徵映射機制(過濾器filters)
- 更深層次(deeper)的網路意味著更多的卷積層
- 具有更高解析度的網路(higher resolution)意味著它處理具有較大寬度和深度(空間解析度)的輸入圖像。這樣,生成的要素地圖將具有更高的空間維度。

架構工程就是關於scaling。我們將徹底使用這些術語,因此在繼續之前請務必了解它們。
AlexNet: ImageNet Classification with Deep Convolutional Neural Networks (2012)
Alexnet [1] 由 5 個 conv 層組成,從 11x11 內核開始。它是第一個採用最大池層、ReLu 啟動函數和 3 個巨大線性層的 dropout 架構。該網路用於具有1000個類別的圖像分類任務,這在當時是非常state-of-the-art的。現在,我們可以在 35 行 PyTorch 代碼中實現它:以下分享我的colab(我不喜歡在medium上面打code,會有些縮排問題)
https://colab.research.google.com/drive/1hHgYb-_6o3lb_v6j8e5w1CF0cu_dowYa?usp=sharing
這是第一個在Imagenet上成功訓練的捲積模型,當時,在CUDA中實現這樣的模型要困難得多。Dropout 在線性變換中被大量使用,以用來避免過度擬合。在2015–2016年auto-differentiation出現之前,在GPU上實現反向傳播需要幾個月的時間。
VGG (2014)
非常有名的論文"Very Deep Convolutional Networks for Large-Scale Image Recognition”將deep這一詞的概念以傳染病般傳播開來。
這是第一項深度疊層研究,裡面數據說明只需添加更多層即可提高性能,由於是第一篇deep layers研究,所以也並沒有此一論述的反駁。
所以這一假設在當時是成立的,他們使用3*3的內核,而不適AlexNet。該架構使用224*224pixel 的RGB圖像進行練習。
主要原理是疊三層的 3*3 convolutional layers 類似於單層的 7*7 layer.(Note:VGG核心之一 -> 疊三層3x3好過於一層7x7)
而結果甚至更好!因為中間有利用三個非線性激活層,讓整個function更具有鑑別度。
再者,這樣的設計減低了途中訓練的參數。
具體來說,您需要3∗(3²)C²=27×C² 個權重(Weights), 相比於7*7 conv. 層需要7*7C² = 49 C² 來說還要小。(81% more)
所以總的來說,減低conv layer中kernel size的大小然後疊更多層的performence會好於只有單層而且kernel size很大的performance。
直觀地說,它可以被視為對7×7轉化濾鏡,收縮它們以具有3x3非線性分解。
最後,這也是讓normalization成為一大議題的開端架構。
儘管如此,pretrained 的 VGGs 網路依然用在 feature matching loss在GANs中,和網路風格轉換( style transfer)還有特徵視覺化(feature visualizations)。
以下影片是應用VGG網路在真實世界任務的有趣例子:
VGG16 Neural Network Visualization — YouTube
最後,在Alexnet旁邊進行視覺化比較:

InceptionNet/GoogleNet (2014)
在VGG之後,Christian Szegedy等人的論文”Going Deep with Convolutions”是一個巨大的突破。
動機:增加深度(層數)並不是使模型變大的唯一方法。增加網路的深度和寬度,同時保持計算在恆定水平,如何?
InceptionNet靈感來自人類的視覺系統,其中資訊在多個尺度上被處理,然後在本地聚合。如何在沒有記憶體爆炸的情況下實現這一目標?
答案是 1×1卷積!主要目的是通過減少每個捲積塊的輸出通道來減小尺寸。然後,我們可以處理具有不同內核大小的輸入。只要輸出是填充的,它就是與輸入中的相同。
為了找到適合的padding在單個步伐且無dilation, padding p 和 kernel k 被定義為 out = in (input 和 output 空間維度):
在out = in 的條件下 。就意味著 out=in+2∗p−k+1,等同於p=(k-1)/2.
在Keras中,可以簡單的設定超參數 padding=”same”.
通過這種方式,我們可以連接與不同內核卷積的功能。
然後我們需要以 1×1捲積層將特徵「投影」到更少的通道,以贏得計算能力。有了這些額外的資源,我們便可以添加更多的層。實際上,1×1convs的工作方式類似於低維嵌入。
以下影片為Andrew Ng解釋 1X1 Convolutions:
Neural Networks — Networks in Networks and 1x1 Convolutions — YouTube
下個小結論: 1X1的convolution kernel的兩大功能 → 1.做更多的非線性轉換,2.縮小輸入的通道數, 也就是所小記憶體的計算量!
加入了1X1的卷積概念後就不只是造就了增加網路深度的InceptionNet,還造就了運用InceptionNet模組再進行寬度增大後的GoogleNet.
此核心區塊被稱為 inception module, 看起來就像下面的圖所示:

整個架構被稱為GoogLeNet或InceptionNet。從本質上講,作者聲稱他們試圖接近具有正常密集層的稀疏卷積網路(如圖所示)。
為什麼?因為他們相信只有少數神經元是有效的。這符合 Hebbian principle:「一起發射的神經元,最後也連接在一起,那些才是有效的神經元計算」。
此外它使用不同內核大小的卷積(5×5,3×3,1×1) 以捕獲多個比例下的細節.
通常,對於全域駐留(resides globally)的資訊,較大的內核是首選,對於本地分散式的資訊,較小的內核是首選的。
InceptionNet/GoogLeNet架構由9個堆疊在一起的inception模組組成,兩者之間有最大池化層(Maxpooling)=>(將空間維度減半)。它由22層組成(包含池化層為27層)。它在最後一個 inception 模組之後使用全域平均池層(global average pooling)。
以下寫了一個非常簡單的 Inception block implementation:
您可以在此處找到上述code的Google colab。
當然,您可以在啟動函數之前添加歸一化層(Normalization layer)。但是由於歸一化技術還沒有很好的建立,作者引入了兩個輔助分類器。原因是:梯度消失問題。
Inception V2, V3 (2015)
後來,在論文「Rethinking the Inception Architecture for Computer Vision」中,作者根據以下原則改進了 Inception 模型:
- 將 5x5 和 7x7(在 InceptionV3 中)卷積分別分解為兩個和三個 3x3 順序卷積。這提高了計算速度。這與 VGG 的原理相同(幫大家回想一下… → 降低kernel size,增加層數可以導致performence變好,乃VGG之核心概念之一(請牢記此點)。
- 他們使用spatially separable卷積。簡單地說,一個 3x3 內核被分解成兩個較小的內核:一個 1x3 和一個 3x1 內核,且它們是按此順序應用的。
- Inception module變得更寬。
- 他們試圖在網路的深度和寬度之間以平衡的方式分配計算預算。
- 他們增加了 batch normalization這個重要的概念.
之後的版本為 InceptionV4和 Inception-Resnet
ResNet: Deep Residual Learning for Image Recognition (2015)
之前所提到的一個問題,如梯度消失,都通過兩個技巧得到解決:
- batch normalization
2. short skip connections
非H(x)=F(x), 我們要求模型學習差異(又名殘差)H’(x) = F(x) + x,這意味著H(x) — x = F(x)將是殘餘部分。

有了這個簡單但有效的block,作者設計了更深層次的架構,範圍從18(Resnet-18)到150(Resnet-150)層。
對於最深的模型,他們採用了1x1卷積,如下右圖所示:

總之,下面是整個架構的概念圖:
有關更多詳細資訊,您可以觀看來自Henry AI Labs on ResNets:
我們可以通過直接從torchvision導入一堆ResNet來try他們:
import torchvisionpretrained = True# A lot of choices :Pmodel = torchvision.models.resnet18(pretrained)model = torchvision.models.resnet34(pretrained)model = torchvision.models.resnet50(pretrained)model = torchvision.models.resnet101(pretrained)model = torchvision.models.resnet152(pretrained)model = torchvision.models.wide_resnet50_2(pretrained)model = torchvision.models.wide_resnet101_2(pretrained)#試試吧!
DenseNet: Densely Connected Convolutional Networks (2017)
忽略連接是一個非常酷的想法。我們為什麼不直接跳過連接所有內容呢?
Densenet是將這個想法推向極端的一個例子。當然,與ResNets的主要區別在於,我們將連接而不是添加feature maps。
因此,其背後的核心思想是特徵重用,這導致了非常緊湊的模型。因此,它需要的參數比其他CNN少,因為沒有重複的特徵圖。
好吧,為什麼不呢?嗯……這裡有兩個問題:
- feature maps必須是相同的尺寸
- 與所有先前feature maps的串聯可能會導致記憶體爆炸
為了解決第一個問題,我們有兩種解決方案:
a)用 conv layer 搭配 padding超參數來保持空間維度或是
b)僅在Dense block內使用Dense跳過連接。
範例如下所示:

Transition Layer可以使用平均池化對圖像尺寸進行下採樣(down sampleing)。
為了解決第二個問題,即記憶體爆炸,feature map通過1x1 的convs減少計算量(有點壓縮)。請注意,在圖中使用了 K,但 densenet 使用了K=featmaps/2
此外,當不使用數據增強(Data Augmentation)時,它們在每個捲積層之後添加一個 p=0.2 的Dropout layer。
Growth rate:
更重要的是,還有另一個參數可以控制整個體系結構的feature maps數量。即為增長率。它指定每個超密集convolution layer的輸出要素。鑒於k0初始特徵圖和k增長率,可以計算出每層輸入特徵圖的數量l如k0 + k * (l − 1).在框架中,數字 k 是 4 的倍數,稱為bottleneck size (bn_size)。
最後,我在這裡引用了DenseNet在torchvision中最重要的論點作為總結:
import torchvisionmodel = torchvision.models.DenseNet(growth_rate = 16, # how many filters to add each layer (`k` in paper)block_config = (6, 12, 24, 16), # how many layers in each pooling blocknum_init_features = 16, # the number of filters to learn in the first convolution layer (k0)bn_size= 4, # multiplicative factor for number of bottleneck (1x1 cons) layersdrop_rate = 0, # dropout rate after each dense conv layernum_classes = 30 # number of classification classes)print(model) # see snapshot below
在“dense”layer(快照中的密集層5和6)內有一個bottleneck(1x1)層,它將channels數減少至 bn_size*growth_rate=64。否則,輸入通道的數量將會爆炸式的增長。如下圖所示,每層添加 16=growth_rate channels。

即使DenseNet一開始被提倡用來做圖像分類的任務,但它卻已經因為其特徵功能重複性使用的關鍵功能,而使用於各個領域上。下面的Pie diagram為DenseNet的使用領域分布圖,來自DenseNet的paper with code。

Big Transfer (BiT): General Visual Representation Learning (2020)
儘管已經提出了ResNet的許多變體,但最新和最著名的是BiT。Big Transfer(BiT)是一種可擴展的基於ResNet的模型,用於有效的圖像預訓練。
作者他們開發了3種基於ResNet152的BiT模型(小型,中型和大型)。對於BiT的重大改變,他們使用了ResNet152x4,這意味著每層的通道(channels)是其4倍。他們在比imagenet還要更大很多的數據集中預先訓練了該模型一次。最大的模型是在瘋狂大型的JFT數據集上訓練的,該數據集由3億張->三百個百萬(300M)標記圖像組成。
架構中的主要貢獻是Normalization layer的選擇。為此,作者將批次處理歸一化(BN)替換為組歸一化(GN)和權重標準化(WS)。

為什麼?因為第一個BN的參數(均值和方差)需要在預訓練和轉移之間進行調整。另一方面,GN 不依賴於任何參數狀態。另一個原因是BN使用批處理級統計資訊,這對於像TPU這樣設備中的分散式訓練變得不可靠。分佈在 500 個 TPU 上的 4K 批意味著每個工作線程 8 個批次,這並不能很好地估計統計數據。通過將規範化技術更改為 GN+WS,可以避免在工作線程之間進行同步。
顯然,擴展到更大的數據集與模型大小密切相關。

在此圖中,說明瞭與數據並行擴展體系結構的重要性。ILSVER是具有100萬張圖像的Imagenet數據集,ImageNet-21K擁有大約1400萬張圖像,JFT則有 300M張!
最後,這種大型預訓練模型可以微調到應用在很小的數據集上,並且也可實現其非常好的性能。

EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks (2019)
EfficientNet是關於engineering和scale的。它證明,如果您仔細設計架構,則可以通過合理的參數獲得最佳結果。

上圖展示了 ImageNet Accuracy VS model parameters.(非常屌的是EfficientNet-B1 計算上小ResNet-152 7.6倍 然後又比ResNet-152快5.7倍)
Individual upscaling
- 使用更多的圖層(深度),可以捕獲更豐富,更複雜的特徵,但此類模型很難訓練(由於漸變消失)
- 更廣泛的網路更容易訓練。它們往往能夠捕獲更細粒度的特徵,但很快就會飽和。
建議從一個相對較小的baseline模型F開始。並逐漸擴展它。
為了進一步限制設計空間,作者將所有層限制為具有恆定比率的均勻縮放。這樣一來,我們就有了一個更易於處理的優化問題。最後,也必須想到我們infrastructure的最大記憶體和FLOPs數量。下圖很好地示範了這一點:

w是寬度,d為深度,以及r為解析度比例因數。通過縮放其中一個,其中只有一個將在一點上飽和。我們能做得更好嗎?
Compound scaling
暫時斷在此…