Lesson 16.6Lesson 16.6 复现经典架构:LeNet5 复现经典架构 (2):AlexNet
4 復現(xiàn)經(jīng)典網(wǎng)絡:LeNet5與AlexNet
4.1 現(xiàn)代CNN的奠基者:LeNet5
使用卷積層和池化層能夠創(chuàng)造的最簡單的網(wǎng)絡是什么樣呢?或許就是下面這樣的架構:
首先,圖像從左側輸入,從右側輸出,數(shù)據(jù)傳輸方向與DNN一致。整個網(wǎng)絡由2個卷積層、2個平均池化層和2個全連接層組成,雖然沒有標注出來,但每個卷積層和全連接層后都使用激活函數(shù)tanh或sigmoid。
這個架構就是著名的LeNet5架構,它在1998年被LeCun等人在論文《Gradient-Based Learning Applied to Document Recognition》中正式提出,它被認為是現(xiàn)代卷積神經(jīng)網(wǎng)絡的奠基者。在LeNet5被提出后,幾乎所有的卷積網(wǎng)絡都會連用卷積層、池化層與全連接層(也就是線性層)?,F(xiàn)在,這已經(jīng)成為一種非常經(jīng)典的架構:卷積層作為輸入層,緊跟激活函數(shù),池化層緊跟在一個或數(shù)個卷積+激活的結構之后。在卷積池化交替進行數(shù)次之后,轉向線性層+激活函數(shù),并使用線性層結尾,輸出預測結果。由于輸入數(shù)據(jù)結構的設置,以上架構圖中的網(wǎng)絡可能與論文中有一些細節(jié)上的區(qū)別(論文見下載資料),在其他教材中,你或許會見到添加了更多卷積層、或添加了更多池化層的相似架構,這些都是根據(jù)LeNet的核心思想“卷積+池化+線性”來搭建的LeNet5的變體。
在上面的架構中,每層的結構和參數(shù)都標識得非常清楚,在PyTorch中實現(xiàn)其架構的代碼如下:
import torch from torch import nn from torch.nn import functional as F data = torch.ones(size=(10,1,32,32)) class Model(nn.Module):def __init__(self):super().__init__()self.conv1 = nn.Conv2d(1,6,5)self.pool1 = nn.AvgPool2d(kernel_size=2,stride=2)self.conv2 = nn.Conv2d(6,16,5)self.pool2 = nn.AvgPool2d(kernel_size=2,stride=2)self.fc1 = nn.Linear(16*5*5,120) #這里的上層輸入是圖像中的全部像素self.fc2 = nn.Linear(120,84)def forward(self,x):x = F.tanh(self.conv1(x))x = self.pool1(x)x = F.tanh(self.conv2(x))x = self.pool2(x)x = x.view(-1,16*5*5) #需要將數(shù)據(jù)的特征部分“拉平”才能夠進入FC層x = F.tanh(self.fc1(x))output = F.softmax(self.fc2(x),dim=1) net = Model() net(data) #卷積網(wǎng)絡的一大特點是:改變輸入數(shù)據(jù)的尺寸,整個網(wǎng)絡就會開始花式報錯 #你可以試試將數(shù)據(jù)32*32的結構修改至28*28 #安裝torchinfo庫,查看網(wǎng)絡結構 !pip install torchinfo from torchinfo import summary net = Model() summary(net, input_size=(10, 1, 32, 32)) #========================================================================================== #Layer (type:depth-idx) Output Shape Param # #========================================================================================== #model -- -- #├─Conv2d: 1-1 [10, 6, 28, 28] 156 #├─AvgPool2d: 1-2 [10, 6, 14, 14] -- #├─Conv2d: 1-3 [10, 16, 10, 10] 2,416 #├─AvgPool2d: 1-4 [10, 16, 5, 5] -- #├─Linear: 1-5 [10, 120] 48,120 #├─Linear: 1-6 [10, 84] 10,164 #========================================================================================== #Total params: 60,856 #Trainable params: 60,856 #Non-trainable params: 0 #Total mult-adds (M): 4.22 #========================================================================================== #Input size (MB): 0.04 #Forward/backward pass size (MB): 0.52 #Params size (MB): 0.24 #Estimated Total Size (MB): 0.81 #==========================================================================================這是我們在之前的課程中學到的寫法,這種寫法適合層數(shù)較少、層次簡單的神經(jīng)網(wǎng)絡,但這個網(wǎng)絡和我們之前寫的只有線性層的網(wǎng)絡比起來還是略有些復雜了。在這段代碼中你可以清晰地看見卷積層、池化層以及全連接層是如何鏈接在一起共同作用的。我們將“層”放在init中定義,而將“函數(shù)”放在forward中定義,令init中的結構與forward中的結構一一對應,并使用forward函數(shù)執(zhí)行向前傳播流程。
從今天的眼光來看,LeNet5無疑是很簡單并且有些弱小的卷積網(wǎng)絡,然而,當加入學習率衰減等優(yōu)化方法并訓練恰當時,單一的LeNet5模型可以在Fashion-MNIST數(shù)據(jù)集上獲得超過91%的訓練準確率(可參考kaggle kernel:https://www.kaggle.com/jaeboklee/pytorch-fashion-mnist-lenet5-ensemble)。
這個效果已經(jīng)比只有線性層的網(wǎng)絡提升了約5%的成績。雖然簡單,LeNet5卻切實展示了卷積神經(jīng)網(wǎng)絡的實力。
4.2 從淺層到深度:AlexNet
在二十一世紀的前十年,LeNet5作為現(xiàn)代卷積網(wǎng)絡的奠基者并沒有引起太大的水花,大多數(shù)人并不相信機器提取的特征能夠比人親自提取的特征更強大,事實上,LeNet5也確實無法在復雜任務上戰(zhàn)勝傳統(tǒng)計算機視覺方法。卷積網(wǎng)絡在被提出的幾十年內一直在蟄伏,直到2012年,AlexNet橫空出世,真正第一次證明了,當數(shù)據(jù)量達標、訓練得當時,卷積神經(jīng)網(wǎng)絡提取的特征效果遠遠勝過人類提取的特征。
AlexNet誕生于有“視覺界奧林匹克”之稱的大規(guī)模視覺識別挑戰(zhàn)比賽ILSVRC(ImageNet Large Scale Visual Recognition Challenge)。ILSVRC使用ImageNet數(shù)據(jù)集,共有一千四百多萬幅圖像,共2萬多個類別,圖像的尺寸有224×224,227×227,256×256,299×299等不同類型。在ILSVRC中,參賽隊伍需要使用大約包含一百萬張圖片的訓練集來訓練模型,并識別出測試集中的一千個類別,再按“錯誤率”從低到高進行排名(具體錯誤率是如何計算的,將在我們使用ImageNet進行訓練的時候來說明)。在AlexNet出現(xiàn)之前,最好成績一直由手工提取特征+支持向量機的算法獲得,最低錯誤率為25.8%。2012年,AlexNet進入ILSVRC競賽,一下將錯誤率降低到了15.3%。在過去的ImageNet競賽中,哪怕有一個百分點的提升都是非常不錯的成績,而深度學習首次入場就取得了10個百分點的提升,這給整個圖像領域帶來了巨大的震撼,從那之后圖像領域的前沿研究就全面向深度學習傾斜了。AlexNet無疑是真正讓“深度視覺”出圈的架構,其架構如下所示:
AlexNet總共有11層,其中有5個卷積層、3個池化層、2個全連接隱藏層,1個全連接輸出層,實際上,在全連接層的前后AlexNet使用了Dropout層,但在大部分架構上都不會把DP表現(xiàn)出來。
在有的文獻上,你可能看到人們稱AlexNet為8層(不計算池化層)。實際上,在學術界,我們對于神經(jīng)網(wǎng)絡的“層”的計數(shù)方法沒有明確的定義,有的文獻會將所有卷積中的元素都算做“層”,例如、池化、BN、Dropout等都是層,而有的文獻認為,只有帶有訓練參數(shù)的才能夠被稱為“層”,其他對數(shù)據(jù)進行處理的、不帶訓練參數(shù)的功能(如池化、激活函數(shù)、Dropout等)都屬于參數(shù)層的附屬。大部分文獻中的架構圖只會寫出卷積、池化以及全連接層是如何排列,即便文獻中提到使用了Dropout或BN等計算方式,可能也不一定會明確這些計算在整體網(wǎng)絡架構的哪一部分。在PyTorch中進行計算時,就連激活函數(shù)也可以是單獨的層。因此,人們對于一個神經(jīng)網(wǎng)絡究竟有幾層是沒有明確定義的。在學習架構時,不用去糾結整體的層數(shù),而要從層與層之間的組合入手。例如,AlexNet的架構若用文字來表現(xiàn),則可以打包成4個組合:
輸入→(卷積+池化)→(卷積+池化)→(卷積x3+池化)→(線性x3)→輸出
相對的,LeNet5的架構可以打包成3個組合:
輸入→(卷積+池化)→(卷積+池化)→(線性x2)→輸出
雖然省略了不少細節(jié),但是這樣的組合比架構圖更容易記憶,可以快速幫助你判斷眼前的代碼究竟依賴于怎樣的經(jīng)典架構來建立。
和只有6層(包括池化層)的LeNet5比起來,AlexNet主要做出了如下改變:
1、相比之下,卷積核更小、網(wǎng)絡更深、通道數(shù)更多,這代表人們已經(jīng)認識到了圖像數(shù)據(jù)天生適合于多次提取特征,“深度”才是卷積網(wǎng)絡的未來。LeNet5是基于MNIST數(shù)據(jù)集創(chuàng)造,MNIST數(shù)據(jù)集中的圖片尺寸大約只有30*30的大小,LeNet5采用了5x5的卷積核,圖像尺寸/核尺寸大約在6:1。而基于ImageNet數(shù)據(jù)集訓練的AlexNet最大的卷積核只有11x11,且在第二個卷積層就改用5x5,剩下的層中都使用3x3的卷積核,圖像尺寸/核尺寸至少也超過20:1。小卷積核讓網(wǎng)絡更深,但也讓特征圖的尺寸變得很小,為了讓信息盡可能地被捕獲,AlexNet也使用了更多的通道。小卷積核、多通道、更深的網(wǎng)絡,這些都成為了卷積神經(jīng)網(wǎng)絡后續(xù)發(fā)展的指導方向。
2、使用了ReLU激活函數(shù),擺脫Sigmoid與Tanh的各種問題。
3、使用了Dropout層來控制模型復雜度,控制過擬合。
4、引入了大量傳統(tǒng)或新興的圖像增強技術來擴大數(shù)據(jù)集,進一步緩解過擬合。
5、使用GPU對網(wǎng)絡進行訓練,使得“適當?shù)挠柧殹?#xff08;proper training)成為可能。
除此之外,在原論文中,作者Alex Krizhevsky還提出了其他創(chuàng)新,例如,他們使用了overlap pooling的技術——一般的池化層在進行掃描的時候,都是步幅 >= 核尺寸,而在AlexNet中,池化層中的步幅是小于核尺寸的,這就讓池化過程中的掃描區(qū)域出現(xiàn)重疊(overlap),根據(jù)論文所示,這個技術可以緩解過擬合。從以上這些點,很容易看出為什么AlexNet對于卷積神經(jīng)網(wǎng)絡而言如此地重要,它幾乎從算法、算力、數(shù)據(jù)、架構技巧等各個方面影響了現(xiàn)代卷積神經(jīng)網(wǎng)絡地發(fā)展。AlexNet之后,ImageNet競賽上就很少看到傳統(tǒng)視覺算法的身影了。當然,競賽環(huán)境與工業(yè)環(huán)境有極大的區(qū)別,在實際落地的過程中,深度學習還受到各種限制,因此傳統(tǒng)方法依然很關鍵。
現(xiàn)在,讓我們在PyTorch中來復現(xiàn)AlexNet的架構:
import torch from torch import nn from torch.nn import functional as Fdata = torch.ones(size=(10,3,227,227)) #假設圖像的尺寸為227x227class Model(nn.Module):def __init__(self):super().__init__() #大卷積核、較大的步長、較多的通道self.conv1 = nn.Conv2d(3,96,kernel_size=11, stride=4) self.pool1 = nn.MaxPool2d(kernel_size=3,stride=2)#卷積核、步長恢復正常大小,進一步擴大通道self.conv2 = nn.Conv2d(96,256,kernel_size=5, padding=2) self.pool2 = nn.MaxPool2d(kernel_size=3,stride=2)#連續(xù)的卷積層,瘋狂提取特征self.conv3 = nn.Conv2d(256,384,kernel_size=3,padding=1) self.conv4 = nn.Conv2d(384,384,kernel_size=3,padding=1)self.conv5 = nn.Conv2d(384,256,kernel_size=3,padding=1)self.pool3 = nn.MaxPool2d(kernel_size=3,stride=2)#全連接層self.fc1 = nn.Linear(256*6*6,4096) #這里的上層輸入是圖像中的全部像素self.fc2 = nn.Linear(4096,4096)self.fc3 = nn.Linear(4096,1000) #輸出ImageNet的一千個類別def forward(self,x):x = F.relu(self.conv1(x))x = self.pool1(x)x = F.relu(self.conv2(x))x = self.pool2(x)x = F.relu(self.conv3(x))x = F.relu(self.conv4(x))x = F.relu(self.conv5(x))x = self.pool3(x)x = x.view(-1,256*6*6) #需要將數(shù)據(jù)的特征部分“拉平”才能夠進入FC層x = F.relu(F.dropout(self.fc1(x),0.5)) #dropout:隨機讓50%的權重為0x = F.relu(F.dropout(self.fc2(x),0.5)) output = F.softmax(self.fc3(x),dim=1) net = Model() net(data)from torchinfo import summary summary(net,input_size=((10,3,227,227))) # ========================================================================================== # Layer (type:depth-idx) Output Shape Param # # ========================================================================================== # model -- -- # ├─Conv2d: 1-1 [10, 96, 55, 55] 34,944 # ├─MaxPool2d: 1-2 [10, 96, 27, 27] -- # ├─Conv2d: 1-3 [10, 256, 27, 27] 614,656 # ├─MaxPool2d: 1-4 [10, 256, 13, 13] -- # ├─Conv2d: 1-5 [10, 348, 13, 13] 802,140 # ├─Conv2d: 1-6 [10, 348, 13, 13] 1,090,284 # ├─Conv2d: 1-7 [10, 256, 13, 13] 802,048 # ├─MaxPool2d: 1-8 [10, 256, 6, 6] -- # ├─Linear: 1-9 [10, 4096] 37,752,832 # ├─Linear: 1-10 [10, 4096] 16,781,312 # ├─Linear: 1-11 [10, 1000] 4,097,000 # ========================================================================================== # Total params: 61,975,216 # Trainable params: 61,975,216 # Non-trainable params: 0 # Total mult-adds (G): 10.68 # ========================================================================================== # Input size (MB): 6.18 # Forward/backward pass size (MB): 51.77 # Params size (MB): 247.90 # Estimated Total Size (MB): 305.85 # ==========================================================================================與LeNet5實現(xiàn)時候的情況一致,由于輸入數(shù)據(jù)的結構可能與原論文有區(qū)別,我們的網(wǎng)絡架構中的小細節(jié)可能與原論文有些許不同。從代碼量來看,其實AlexNet并沒有比LeNet5復雜太多,當結構清晰時,我們甚至認為AlexNet有些簡單。但在AlexNet出世之前,學術界普遍認為深度網(wǎng)絡是不可能訓練的,而今天我們都了解,只要有足夠的算力和數(shù)據(jù),即便是幾億參數(shù)的巨量網(wǎng)絡也是可以訓練的。AlexNet所帶來的觀念上的轉變才是真正推動卷積神經(jīng)網(wǎng)絡向前發(fā)展的核心。
只要有架構圖,復現(xiàn)經(jīng)典架構其實并不是一件難事(照著敲代碼,一點也不難),但我們不太可能完全記住經(jīng)典架構中的每個細節(jié)。在復現(xiàn)架構時,我們應該注意哪些方面呢?
1、首先,代碼可以不自己寫,但一定要通
2、了解此架構的核心思想,以及在此思想下其結構大概如何排布。對于比較簡單的網(wǎng)絡(比如AlexNet),可以記住具體的層的結構(包括參數(shù)大概的大小),但對于復雜網(wǎng)絡,可以不用難為自己,理解思想為主,看見代碼能認出是該網(wǎng)絡,并且看見代碼之后能夠理解每層具體在做什么,做的事情如何與該網(wǎng)絡的核心思想一致。
3、了解此架構被提出的背景,即可了解它的突破與局限
4、發(fā)現(xiàn)架構與自己熟悉的不一致,先多谷歌下不同的架構圖,或許你看到的只是別人基于經(jīng)典架構修改的變體。實在糾結,再回看提出框架的具體論文。
到這里,相信大家對卷積層和池化層的應用已經(jīng)有了基本的了解。作為進階內容,大家可以試著擺脫課件中的代碼,自己對著架構圖復現(xiàn)網(wǎng)絡架構,你甚至可以試著計算一下各層特征圖的大小,將此架構改寫為更簡潔的形式,并利用這些架構在經(jīng)典數(shù)據(jù)集上進行各種嘗試。
總結
以上是生活随笔為你收集整理的Lesson 16.6Lesson 16.6 复现经典架构:LeNet5 复现经典架构 (2):AlexNet的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lesson 16.5 在Pytorch
- 下一篇: Lesson 16.1016.1116.