【深度学习】深入浅出卷积神经网络及实现!
作者:陳樺、夏雨柔、樊亮,Datawhale優(yōu)秀學(xué)習(xí)者
卷積神經(jīng)網(wǎng)絡(luò)CNN是深度學(xué)習(xí)中的基礎(chǔ)知識(shí)。本文對(duì)CNN的基礎(chǔ)原理及常見的CNN網(wǎng)絡(luò)進(jìn)行了詳細(xì)解讀,并介紹了Pytorch構(gòu)建深度網(wǎng)絡(luò)的流程。最后,以阿里天池零基礎(chǔ)入門CV賽事為學(xué)習(xí)實(shí)踐,對(duì)Pytorch構(gòu)建CNN模型進(jìn)行實(shí)現(xiàn)。
數(shù)據(jù)及背景??
https://tianchi.aliyun.com/competition/entrance/531795/introduction(阿里天池 - 零基礎(chǔ)入門CV賽事)
CNN原理
CNN,又稱卷積神經(jīng)網(wǎng)絡(luò),是深度學(xué)習(xí)中重要的一個(gè)分支。CNN在很多領(lǐng)域都表現(xiàn)優(yōu)異,精度和速度比傳統(tǒng)計(jì)算學(xué)習(xí)算法高很多。特別是在計(jì)算機(jī)視覺領(lǐng)域,CNN是解決圖像分類、圖像檢索、物體檢測(cè)和語義分割的主流模型。
1. 卷積
如圖1所示,圖中的X和O無論怎么旋轉(zhuǎn)或者縮放,人眼其實(shí)還是很容易識(shí)別出X和O。
圖1
但是計(jì)算機(jī)不同,它看到的其實(shí)是一個(gè)個(gè)的像素陣列,如圖2。如何對(duì)像素的陣列進(jìn)行特征的提取其實(shí)就是卷積神經(jīng)網(wǎng)絡(luò)要干的事情。
圖2
再看圖3,我們發(fā)現(xiàn)X即使進(jìn)行了旋轉(zhuǎn),但是綠、橙、紫框標(biāo)記的區(qū)域在兩張圖中還是一致的,某種程度上,這其實(shí)就是X的特征。
圖3
因此可以將這三個(gè)特征的區(qū)間提取出來,就形成了三個(gè)卷積核,如圖4所示。
圖4
既然有了卷積核,那么卷積核是如何進(jìn)行卷積操作的呢?其實(shí)很簡(jiǎn)單,可以看一下圖5,卷積核其實(shí)就是拿著這個(gè)矩陣在圖片的矩陣上一點(diǎn)點(diǎn)的平移,就像掃地一樣。每掃到一處地方就可以進(jìn)行卷積的運(yùn)算,計(jì)算方法很簡(jiǎn)單,如圖5所示,左上角的卷積核掃到綠色框的位置,則卷積核矩陣的數(shù)字就和掃到的位置的矩陣的數(shù)字一一對(duì)應(yīng)相乘然后相加,最后取一個(gè)均值,該值就是卷積核提取的特征。
圖5
卷積核提取的所有的特征組成了一個(gè)長(zhǎng)和寬變小的矩陣,這個(gè)矩陣又稱為feature map,如圖6。使用不同的卷積核也就能提取出不同的feature map。所以可以想象的是,如果不斷的進(jìn)行卷積操作,那么圖片的矩陣會(huì)逐步地長(zhǎng)寬減少,厚度增加。
圖6
可以看到卷積操作通過卷積核是可以分別提取到圖片的特征的,但是如何提前知道卷積核呢?像上文的例子,很容易可以找到3個(gè)卷積核,但是假如是人臉識(shí)別這樣成千上萬個(gè)特征的圖片,就沒辦法提前知道什么是合適的卷積核。其實(shí)也沒必要知道,因?yàn)檫x擇什么樣的卷積核,完全可以通過訓(xùn)練不斷優(yōu)化。初始時(shí)只需要隨機(jī)設(shè)置一些卷積核,通過訓(xùn)練,模型其實(shí)自己可以學(xué)習(xí)到合適的卷積核,這也是卷積神經(jīng)網(wǎng)絡(luò)模型強(qiáng)大的地方。
2.?池化(pooling)
池化,也叫下采樣,本質(zhì)上其實(shí)就是對(duì)數(shù)據(jù)進(jìn)行一個(gè)縮小。因?yàn)槲覀冎?#xff0c;比如人臉識(shí)別,通過卷積操作得到成千上萬個(gè)feature map,每個(gè)feature map也有很多的像素點(diǎn),這些對(duì)于后續(xù)的運(yùn)算的時(shí)間會(huì)變得很長(zhǎng)。
池化其實(shí)就是對(duì)每個(gè)feature map進(jìn)一步提煉的過程。如圖7所示,原來4X4的feature map經(jīng)過池化操作之后就變成了更小的2*2的矩陣。池化的方法包括max pooling,即取最大值,以及average pooling,即取平均值。
圖7
3. Normalization
這里的Normalization就是將矩陣中負(fù)數(shù)的值轉(zhuǎn)成0,也就是使用一個(gè)稱之為ReLu的激活函數(shù)進(jìn)行負(fù)數(shù)變?yōu)?的操作。ReLu函數(shù)本質(zhì)上就是max(0,x)。這一步其實(shí)也是為了方便運(yùn)算。
4. 卷積神經(jīng)網(wǎng)絡(luò)理解
因此卷積、ReLu、pooling,不斷重復(fù)其實(shí)也就基本上構(gòu)成了卷積神經(jīng)網(wǎng)絡(luò)的框架,如圖8。然后將最終得到的feaure map 排成一列(圖8),接到全連接層,這樣就形成了我們的卷積神經(jīng)網(wǎng)絡(luò)。值得注意的是,排成一列的數(shù)值,是有權(quán)重,而這些權(quán)重是通過訓(xùn)練、反向傳播得到的,通過權(quán)重的計(jì)算,可以知道不同分類的概率是怎么樣的。
圖8
卷積神經(jīng)網(wǎng)絡(luò)
卷積神經(jīng)網(wǎng)絡(luò)基礎(chǔ):LeNet5
手寫字體識(shí)別模型LeNet5誕生于1994年,是最早的卷積神經(jīng)網(wǎng)絡(luò)之一。LeNet5通過巧妙的設(shè)計(jì),利用卷積、參數(shù)共享、池化等操作提取特征,避免了大量的計(jì)算成本,最后再使用全連接神經(jīng)網(wǎng)絡(luò)進(jìn)行分類識(shí)別,這個(gè)網(wǎng)絡(luò)也是最近大量神經(jīng)網(wǎng)絡(luò)架構(gòu)的起點(diǎn)。
如下圖所示為L(zhǎng)eNet網(wǎng)絡(luò)結(jié)構(gòu),總共有7層網(wǎng)絡(luò)(不含輸入層),2個(gè)卷積層、2個(gè)池化層、3個(gè)全連接層。
LeNet分為卷積層塊和全連接層塊兩個(gè)部分。下面我們分別介紹這兩個(gè)模塊。卷積層塊里的基本單位是卷積層后接最大池化層:卷積層用來識(shí)別圖像里的空間模式,如線條和物體局部,之后的最大池化層則用來降低卷積層對(duì)位置的敏感性。卷積層塊由兩個(gè)這樣的基本單位重復(fù)堆疊構(gòu)成。在卷積層塊中,每個(gè)卷積層都使用5*5的窗口,并在輸出上使用sigmoid激活函數(shù)。第一個(gè)卷積層輸出通道數(shù)為6,第二個(gè)卷積層輸出通道數(shù)則增加到16。這是因?yàn)榈诙€(gè)卷積層比第一個(gè)卷積層的輸入的高和寬要小,所以增加輸出通道使兩個(gè)卷積層的參數(shù)尺寸類似。卷積層塊的兩個(gè)最大池化層的窗口形狀均為2*2,且步幅為2。由于池化窗口與步幅形狀相同,池化窗口在輸入上每次滑動(dòng)所覆蓋的區(qū)域互不重疊。
卷積層塊的輸出形狀為(批量大小, 通道, 高, 寬)。當(dāng)卷積層塊的輸出傳入全連接層塊時(shí),全連接層塊會(huì)將小批量中每個(gè)樣本變平(flatten)。也就是說,全連接層的輸入形狀將變成二維,其中第一維是小批量中的樣本,第二維是每個(gè)樣本變平后的向量表示,且向量長(zhǎng)度為通道、高和寬的乘積。全連接層塊含3個(gè)全連接層。它們的輸出個(gè)數(shù)分別是120、84和10,其中10為輸出的類別個(gè)數(shù)。
在卷積層塊中輸入的高和寬在逐層減小。卷積層由于使用高和寬均為5的卷積核,從而將高和寬分別減小4,而池化層則將高和寬減半,但通道數(shù)則從1增加到16。全連接層則逐層減少輸出個(gè)數(shù),直到變成圖像的類別數(shù)10。
通過多次卷積和池化,CNN的最后一層將輸入的圖像像素映射為具體的輸出。如在分類任務(wù)中會(huì)轉(zhuǎn)換為不同類別的概率輸出,然后計(jì)算真實(shí)標(biāo)簽與CNN模型的預(yù)測(cè)結(jié)果的差異,并通過反向傳播更新每層的參數(shù),并在更新完成后再次前向傳播,如此反復(fù)直到訓(xùn)練完成 。?
一個(gè)數(shù)字識(shí)別的效果如圖所示:
卷積神經(jīng)網(wǎng)絡(luò)進(jìn)階
隨著網(wǎng)絡(luò)結(jié)構(gòu)的發(fā)展,研究人員最初發(fā)現(xiàn)網(wǎng)絡(luò)模型結(jié)構(gòu)越深、網(wǎng)絡(luò)參數(shù)越多模型的精度更優(yōu)。比較典型的是AlexNet、VGG、InceptionV3和ResNet的發(fā)展脈絡(luò)。? ? ?
1. AlexNet(2012)
2012年,AlexNet橫空出世。這個(gè)模型的名字來源于論文第一作者的姓名Alex Krizhevsky。AlexNet使用了8層卷積神經(jīng)網(wǎng)絡(luò),并以很大的優(yōu)勢(shì)贏得了ImageNet 2012圖像識(shí)別挑戰(zhàn)賽。它首次證明了學(xué)習(xí)到的特征可以超越手工設(shè)計(jì)的特征,從而一舉打破計(jì)算機(jī)視覺研究的前狀。
AlexNet與LeNet的設(shè)計(jì)理念非常相似,但也有顯著的區(qū)別。
1. 與相對(duì)較小的LeNet相比,AlexNet包含8層變換,其中有5層卷積和2層全連接隱藏層,以及1個(gè)全連接輸出層。下面我們來詳細(xì)描述這些層的設(shè)計(jì):AlexNet第一層中的卷積窗口形狀是11*11。因?yàn)镮mageNet中絕大多數(shù)圖像的高和寬均比MNIST圖像的高和寬大10倍以上,ImageNet圖像的物體占用更多的像素,所以需要更大的卷積窗口來捕獲物體。第二層中的卷積窗口形狀減小到5*5,之后全采用3*3。此外,第一、第二和第五個(gè)卷積層之后都使用了窗口形狀為3*3、步幅為2的最大池化層。而且,AlexNet使用的卷積通道數(shù)也大于LeNet中的卷積通道數(shù)數(shù)十倍。緊接著最后一個(gè)卷積層的是兩個(gè)輸出個(gè)數(shù)為4096的全連接層。這兩個(gè)巨大的全連接層帶來將近1 GB的模型參數(shù)。由于早期顯存的限制,最早的AlexNet使用雙數(shù)據(jù)流的設(shè)計(jì)使一個(gè)GPU只需要處理一半模型。幸運(yùn)的是,顯存在過去幾年得到了長(zhǎng)足的發(fā)展,因此通常我們不再需要這樣的特別設(shè)計(jì)了。
2. AlexNet將sigmoid激活函數(shù)改成了更加簡(jiǎn)單的ReLU激活函數(shù)。一方面,ReLU激活函數(shù)的計(jì)算更簡(jiǎn)單,例如它并沒有sigmoid激活函數(shù)中的求冪運(yùn)算。另一方面,ReLU激活函數(shù)在不同的參數(shù)初始化方法下使模型更容易訓(xùn)練。這是由于當(dāng)sigmoid激活函數(shù)輸出極接近0或1時(shí),這些區(qū)域的梯度幾乎為0,從而造成反向傳播無法繼續(xù)更新部分模型參數(shù);而ReLU激活函數(shù)在正區(qū)間的梯度恒為1。因此,若模型參數(shù)初始化不當(dāng),sigmoid函數(shù)可能在正區(qū)間得到幾乎為0的梯度,從而令模型無法得到有效訓(xùn)練。
3. AlexNet通過丟棄法來控制全連接層的模型復(fù)雜度。而LeNet并沒有使用丟棄法。
4. AlexNet引入了大量的圖像增廣,如翻轉(zhuǎn)、裁剪和顏色變化,從而進(jìn)一步擴(kuò)大數(shù)據(jù)集來緩解過擬合。我們將在后面的圖像增廣中詳細(xì)介紹這種方法。
小結(jié):
AlexNet跟LeNet結(jié)構(gòu)類似,但使用了更多的卷積層和更大的參數(shù)空間來擬合大規(guī)模數(shù)據(jù)集ImageNet。它是淺層神經(jīng)網(wǎng)絡(luò)和深度神經(jīng)網(wǎng)絡(luò)的分界線。
雖然看上去AlexNet的實(shí)現(xiàn)比LeNet的實(shí)現(xiàn)也就多了幾行代碼而已,但這個(gè)觀念上的轉(zhuǎn)變和真正優(yōu)秀實(shí)驗(yàn)結(jié)果的產(chǎn)生令學(xué)術(shù)界付出了很多年。
2. VGG-16(2014) ?
AlexNet在LeNet的基礎(chǔ)上增加了3個(gè)卷積層。但AlexNet作者對(duì)它們的卷積窗口、輸出通道數(shù)和構(gòu)造順序均做了大量的調(diào)整。雖然AlexNet指明了深度卷積神經(jīng)網(wǎng)絡(luò)可以取得出色的結(jié)果,但并沒有提供簡(jiǎn)單的規(guī)則以指導(dǎo)后來的研究者如何設(shè)計(jì)新的網(wǎng)絡(luò)。VGG,它的名字來源于論文作者所在的實(shí)驗(yàn)室Visual Geometry Group。VGG提出了可以通過重復(fù)使用簡(jiǎn)單的基礎(chǔ)塊來構(gòu)建深度模型的思路。VGG的結(jié)構(gòu)圖如下:
VGG塊的組成規(guī)律是:連續(xù)使用數(shù)個(gè)相同的填充為1、窗口形狀為3*3的卷積層后接上一個(gè)步幅為2、窗口形狀為2*2的最大池化層。卷積層保持輸入的高和寬不變,而池化層則對(duì)其減半。我們使用vgg_block函數(shù)來實(shí)現(xiàn)這個(gè)基礎(chǔ)的VGG塊,它可以指定卷積層的數(shù)量和輸入輸出通道數(shù)。
對(duì)于給定的感受野(與輸出有關(guān)的輸入圖片的局部大小),采用堆積的小卷積核優(yōu)于采用大的卷積核,因?yàn)榭梢栽黾泳W(wǎng)絡(luò)深度來保證學(xué)習(xí)更復(fù)雜的模式,而且代價(jià)還比較小(參數(shù)更少)。例如,在VGG中,使用了3個(gè)3x3卷積核來代替7x7卷積核,使用了2個(gè)3x3卷積核來代替5*5卷積核,這樣做的主要目的是在保證具有相同感知野的條件下,提升了網(wǎng)絡(luò)的深度,在一定程度上提升了神經(jīng)網(wǎng)絡(luò)的效果。
與AlexNet和LeNet一樣,VGG網(wǎng)絡(luò)由卷積層模塊后接全連接層模塊構(gòu)成。卷積層模塊串聯(lián)數(shù)個(gè)vgg_block,其超參數(shù)由變量conv_arch定義。該變量指定了每個(gè)VGG塊里卷積層個(gè)數(shù)和輸入輸出通道數(shù)。全連接模塊則跟AlexNet中的一樣。
現(xiàn)在我們構(gòu)造一個(gè)VGG網(wǎng)絡(luò)。它有5個(gè)卷積塊,前2塊使用單卷積層,而后3塊使用雙卷積層。第一塊的輸入輸出通道分別是1(因?yàn)橄旅嬉褂玫腇ashion-MNIST數(shù)據(jù)的通道數(shù)為1)和64,之后每次對(duì)輸出通道數(shù)翻倍,直到變?yōu)?12。因?yàn)檫@個(gè)網(wǎng)絡(luò)使用了8個(gè)卷積層和3個(gè)全連接層,所以經(jīng)常被稱為VGG-11。
可以看到,每次我們將輸入的高和寬減半,直到最終高和寬變成7后傳入全連接層。與此同時(shí),輸出通道數(shù)每次翻倍,直到變成512。因?yàn)槊總€(gè)卷積層的窗口大小一樣,所以每層的模型參數(shù)尺寸和計(jì)算復(fù)雜度與輸入高、輸入寬、輸入通道數(shù)和輸出通道數(shù)的乘積成正比。VGG這種高和寬減半以及通道翻倍的設(shè)計(jì)使得多數(shù)卷積層都有相同的模型參數(shù)尺寸和計(jì)算復(fù)雜度。
VGG:通過重復(fù)使?簡(jiǎn)單的基礎(chǔ)塊來構(gòu)建深度模型。? ?Block: 數(shù)個(gè)相同的填充為1、窗口形狀為3×3的卷積層,接上一個(gè)步幅為2、窗口形狀為2×2的最大池化層。卷積層保持輸入的高和寬不變,而池化層則對(duì)其減半。VGG和AlexNet的網(wǎng)絡(luò)圖對(duì)比如下:
小結(jié):VGG-11通過5個(gè)可以重復(fù)使用的卷積塊來構(gòu)造網(wǎng)絡(luò)。根據(jù)每塊里卷積層個(gè)數(shù)和輸出通道數(shù)的不同可以定義出不同的VGG模型。? ? ?
3. 網(wǎng)絡(luò)中的網(wǎng)絡(luò)(NiN)
LeNet、AlexNet和VGG:先以由卷積層構(gòu)成的模塊充分抽取空間特征,再以由全連接層構(gòu)成的模塊來輸出分類結(jié)果。NiN:串聯(lián)多個(gè)由卷積層和“全連接”層構(gòu)成的小?絡(luò)來構(gòu)建?個(gè)深層?絡(luò)。??了輸出通道數(shù)等于標(biāo)簽類別數(shù)的NiN塊,然后使?全局平均池化層對(duì)每個(gè)通道中所有元素求平均并直接用于分類。?
1×1卷積核作用
放縮通道數(shù):通過控制卷積核的數(shù)量達(dá)到通道數(shù)的放縮;? ?
增加非線性。1×1卷積核的卷積過程相當(dāng)于全連接層的計(jì)算過程,并且還加入了非線性激活函數(shù),從而可以增加網(wǎng)絡(luò)的非線性;? ?
計(jì)算參數(shù)少。? ?
NiN塊我們知道,卷積層的輸入和輸出通常是四維數(shù)組(樣本,通道,高,寬),而全連接層的輸入和輸出則通常是二維數(shù)組(樣本,特征)。如果想在全連接層后再接上卷積層,則需要將全連接層的輸出變換為四維。回憶在多輸入通道和多輸出通道里介紹的1*1卷積層。它可以看成全連接層,其中空間維度(高和寬)上的每個(gè)元素相當(dāng)于樣本,通道相當(dāng)于特征。因此,NiN使用1*1卷積層來替代全連接層,從而使空間信息能夠自然傳遞到后面的層中去。
NiN塊是NiN中的基礎(chǔ)塊。它由一個(gè)卷積層加兩個(gè)充當(dāng)全連接層的1*1卷積層串聯(lián)而成。其中第一個(gè)卷積層的超參數(shù)可以自行設(shè)置,而第二和第三個(gè)卷積層的超參數(shù)一般是固定的。
NiN是在AlexNet問世不久后提出的。它們的卷積層設(shè)定有類似之處。NiN使用卷積窗口形狀分別為11*11、5*5和的3*3卷積層,相應(yīng)的輸出通道數(shù)也與AlexNet中的一致。每個(gè)NiN塊后接一個(gè)步幅為2、窗口形狀為3*3的最大池化層。
除使用NiN塊以外,NiN還有一個(gè)設(shè)計(jì)與AlexNet顯著不同:NiN去掉了AlexNet最后的3個(gè)全連接層,取而代之地,NiN使用了輸出通道數(shù)等于標(biāo)簽類別數(shù)的NiN塊,然后使用全局平均池化層對(duì)每個(gè)通道中所有元素求平均并直接用于分類。這里的全局平均池化層即窗口形狀等于輸入空間維形狀的平均池化層。NiN的這個(gè)設(shè)計(jì)的好處是可以顯著減小模型參數(shù)尺寸,從而緩解過擬合。然而,該設(shè)計(jì)有時(shí)會(huì)造成獲得有效模型的訓(xùn)練時(shí)間的增加。
小結(jié):
NiN重復(fù)使用由卷積層和代替全連接層的1*1卷積層構(gòu)成的NiN塊來構(gòu)建深層網(wǎng)絡(luò)。
NiN去除了容易造成過擬合的全連接輸出層,而是將其替換成輸出通道數(shù)等于標(biāo)簽類別數(shù)的NiN塊和全局平均池化層。
NiN的以上設(shè)計(jì)思想影響了后面一系列卷積神經(jīng)網(wǎng)絡(luò)的設(shè)計(jì)。
4. 含并行連結(jié)的網(wǎng)絡(luò)(GoogLeNet)
在2014年的ImageNet圖像識(shí)別挑戰(zhàn)賽中,一個(gè)名叫GoogLeNet的網(wǎng)絡(luò)結(jié)構(gòu)大放異彩。它雖然在名字上向LeNet致敬,但在網(wǎng)絡(luò)結(jié)構(gòu)上已經(jīng)很難看到LeNet的影子。GoogLeNet吸收了NiN中網(wǎng)絡(luò)串聯(lián)網(wǎng)絡(luò)的思想,并在此基礎(chǔ)上做了很大改進(jìn)。在隨后的幾年里,研究人員對(duì)GoogLeNet進(jìn)行了數(shù)次改進(jìn),本節(jié)將介紹這個(gè)模型系列的第一個(gè)版本。
由Inception基礎(chǔ)塊組成。?
Inception塊相當(dāng)于?個(gè)有4條線路的??絡(luò)。它通過不同窗口形狀的卷積層和最?池化層來并?抽取信息,并使?1×1卷積層減少通道數(shù)從而降低模型復(fù)雜度。?
可以?定義的超參數(shù)是每個(gè)層的輸出通道數(shù),我們以此來控制模型復(fù)雜度。?
Inception塊GoogLeNet中的基礎(chǔ)卷積塊叫作Inception塊,得名于同名電影《盜夢(mèng)空間》(Inception)。與上一節(jié)介紹的NiN塊相比,這個(gè)基礎(chǔ)塊在結(jié)構(gòu)上更加復(fù)雜。Inception塊里有4條并行的線路。前3條線路使用窗口大小分別是1*1、3*3和5*5的卷積層來抽取不同空間尺寸下的信息,其中中間2個(gè)線路會(huì)對(duì)輸入先做1*1卷積來減少輸入通道數(shù),以降低模型復(fù)雜度。第四條線路則使用3*3最大池化層,后接1*1卷積層來改變通道數(shù)。4條線路都使用了合適的填充來使輸入與輸出的高和寬一致。最后我們將每條線路的輸出在通道維上連結(jié),并輸入接下來的層中去。Inception塊中可以自定義的超參數(shù)是每個(gè)層的輸出通道數(shù),我們以此來控制模型復(fù)雜度。GoogLeNet跟VGG一樣,在主體卷積部分中使用5個(gè)模塊(block),每個(gè)模塊之間使用步幅為2的3*3最大池化層來減小輸出高寬。
第一模塊使用一個(gè)64通道的7*7卷積層。
第二模塊使用2個(gè)卷積層:首先是64通道的1*1卷積層,然后是將通道增大3倍的3*3卷積層。它對(duì)應(yīng)Inception塊中的第二條線路。
第三模塊串聯(lián)2個(gè)完整的Inception塊。第一個(gè)Inception塊的輸出通道數(shù)為64+128+32+32=256,其中4條線路的輸出通道數(shù)比例為64:128:32:32=2:4:1:1。其中第二、第三條線路先分別將輸入通道數(shù)減小至96/192=1/2和16/192=1/12后,再接上第二層卷積層。第二個(gè)Inception塊輸出通道數(shù)增至128+192+96+64=480,每條線路的輸出通道數(shù)之比為128:192:96:64 = 4:6:3:2。其中第二、第三條線路先分別將輸入通道數(shù)減小至128/256=1/2和32/256=1/8。
第四模塊更加復(fù)雜。它串聯(lián)了5個(gè)Inception塊,其輸出通道數(shù)分別是192+208+48+64=512、160+224+64+64=512、128+256+64+64=512、112+288+64+64=528和256+320+128+128=832。這些線路的通道數(shù)分配和第三模塊中的類似,首先含3*3卷積層的第二條線路輸出最多通道,其次是僅含1*1卷積層的第一條線路,之后是含5*5卷積層的第三條線路和含1*1最大池化層的第四條線路。其中第二、第三條線路都會(huì)先按比例減小通道數(shù)。這些比例在各個(gè)Inception塊中都略有不同。
第五模塊有輸出通道數(shù)為256+320+128+128=832和384+384+128+128=1024的兩個(gè)Inception塊。其中每條線路的通道數(shù)的分配思路和第三、第四模塊中的一致,只是在具體數(shù)值上有所不同。需要注意的是,第五模塊的后面緊跟輸出層,該模塊同NiN一樣使用全局平均池化層來將每個(gè)通道的高和寬變成1。最后我們將輸出變成二維數(shù)組后接上一個(gè)輸出個(gè)數(shù)為標(biāo)簽類別數(shù)的全連接層。GoogLeNet模型的計(jì)算復(fù)雜,而且不如VGG那樣便于修改通道數(shù)。
小結(jié):
Inception塊相當(dāng)于一個(gè)有4條線路的子網(wǎng)絡(luò)。它通過不同窗口形狀的卷積層和最大池化層來并行抽取信息,并使用1*1卷積層減少通道數(shù)從而降低模型復(fù)雜度。
GoogLeNet將多個(gè)設(shè)計(jì)精細(xì)的Inception塊和其他層串聯(lián)起來。其中Inception塊的通道數(shù)分配之比是在ImageNet數(shù)據(jù)集上通過大量的實(shí)驗(yàn)得來的。
GoogLeNet和它的后繼者們一度是ImageNet上最高效的模型之一:在類似的測(cè)試精度下,它們的計(jì)算復(fù)雜度往往更低。
5、殘差網(wǎng)絡(luò)(ResNet-50) ? ? ?
深度學(xué)習(xí)的問題:深度CNN網(wǎng)絡(luò)達(dá)到一定深度后再一味地增加層數(shù)并不能帶來進(jìn)一步地分類性能提高,反而會(huì)招致網(wǎng)絡(luò)收斂變得更慢,準(zhǔn)確率也變得更差。- - -殘差塊(Residual Block)恒等映射:
左邊:f(x)=x;
右邊:f(x)-x=0 (易于捕捉恒等映射的細(xì)微波動(dòng))。
ResNet沿用了VGG全3*3卷積層的設(shè)計(jì)。殘差塊里首先有2個(gè)有相同輸出通道數(shù)的3*3卷積層。每個(gè)卷積層后接一個(gè)批量歸一化層和ReLU激活函數(shù)。然后我們將輸入跳過這兩個(gè)卷積運(yùn)算后直接加在最后的ReLU激活函數(shù)前。這樣的設(shè)計(jì)要求兩個(gè)卷積層的輸出與輸入形狀一樣,從而可以相加。如果想改變通道數(shù),就需要引入一個(gè)額外的1*1卷積層來將輸入變換成需要的形狀后再做相加運(yùn)算。在殘差塊中,輸?可通過跨層的數(shù)據(jù)線路更快地向前傳播。
ResNet的前兩層跟之前介紹的GoogLeNet中的一樣:在輸出通道數(shù)為64、步幅為2的7*7卷積層后接步幅為2的3*3的最大池化層。不同之處在于ResNet每個(gè)卷積層后增加的批量歸一化層。ResNet-50網(wǎng)絡(luò)結(jié)構(gòu)如下:
GoogLeNet在后面接了4個(gè)由Inception塊組成的模塊。ResNet則使用4個(gè)由殘差塊組成的模塊,每個(gè)模塊使用若干個(gè)同樣輸出通道數(shù)的殘差塊。第一個(gè)模塊的通道數(shù)同輸入通道數(shù)一致。由于之前已經(jīng)使用了步幅為2的最大池化層,所以無須減小高和寬。之后的每個(gè)模塊在第一個(gè)殘差塊里將上一個(gè)模塊的通道數(shù)翻倍,并將高和寬減半。
小結(jié):
殘差塊通過跨層的數(shù)據(jù)通道從而能夠訓(xùn)練出有效的深度神經(jīng)網(wǎng)絡(luò)。
ResNet深刻影響了后來的深度神經(jīng)網(wǎng)絡(luò)的設(shè)計(jì)。
Pytorch構(gòu)建模型
import torch torch.manual_seed(0) torch.backends.cudnn.deterministic= False torch.backends.cudnn.benchmark = True import torchvision.models as models import torchvision.transforms as transforms import torchvision.datasets as datasets import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torch.autograd import Variable from torch.utils.data.dataset import Dataset1. pytorch常用網(wǎng)絡(luò)
1.1 Linear介紹 [全連接層]
nn.Linear(input_feature,out_feature,bias=True)1.2 卷積介紹 [2D卷積層]
nn.Conv2d(in_channels,out_channels,kernel_size,stride=1,padding=0, dilation=1,groups,bias=True,padding_mode='zeros')##kernel_size,stride,padding 都可以是元組 ##?dilation?為在卷積核中插入的數(shù)量1.3 轉(zhuǎn)置卷積介紹 [2D反卷積層]
nn.ConvTranspose2d(in_channels,out_channels,kernel_size,stride=1, padding=0,out_padding=0,groups=1,bias=True,dilation=1,padding_mode='zeros')##padding是輸入填充,out_padding填充到輸出1.4 最大值池化層 [2D池化層]
nn.MaxPool2d(kernel_size,stride=None,padding=0,dilation=1)1.5 批量歸一化層 [2D歸一化層]
nn.BatchNorm2d(num_features,eps,momentum,affine=True, track_running_stats=True)affine=True 表示批量歸一化的α,β是被學(xué)到的 track_running_stats=True?表示對(duì)數(shù)據(jù)的統(tǒng)計(jì)特征進(jìn)行關(guān)注2. pytorch 創(chuàng)建模型的四種方法
假設(shè)創(chuàng)建卷積層–》Relu層–》池化層–》全連接層–》Relu層–》全連接層
# 導(dǎo)入包 import torch import torch.nn.functional as F from collections import OrderedDict2.1.自定義型[定義在init,前向過程在forward]
class Net1(torch.nn.Module):def __init__(self):super(Net1, self).__init__()self.conv1 = torch.nn.Conv2d(3, 32, 3, 1, 1)self.dense1 = torch.nn.Linear(32 * 3 * 3, 128)self.dense2 = torch.nn.Linear(128, 10)def forward(self, x):x = F.max_pool2d(F.relu(self.conv(x)), 2)x = x.view(x.size(0), -1)x = F.relu(self.dense1(x))x = self.dense2(x)return?x2.2 序列集成型[利用nn.Squential(順序執(zhí)行的層函數(shù))]
訪問各層只能通過數(shù)字索引
class Net2(torch.nn.Module):def __init__(self):super(Net2, self).__init__()self.conv = torch.nn.Sequential(torch.nn.Conv2d(3, 32, 3, 1, 1),torch.nn.ReLU(),torch.nn.MaxPool2d(2))self.dense = torch.nn.Sequential(torch.nn.Linear(32 * 3 * 3, 128),torch.nn.ReLU(),torch.nn.Linear(128, 10))def forward(self, x):conv_out = self.conv(x)res = conv_out.view(conv_out.size(0), -1)out = self.dense(res)return?out2.3 序列添加型[利用Squential類add_module順序逐層添加]
給予各層的name屬性
class Net3(torch.nn.Module):def __init__(self):super(Net3, self).__init__()self.conv=torch.nn.Sequential()self.conv.add_module("conv1",torch.nn.Conv2d(3, 32, 3, 1, 1))self.conv.add_module("relu1",torch.nn.ReLU())self.conv.add_module("pool1",torch.nn.MaxPool2d(2))self.dense = torch.nn.Sequential()self.dense.add_module("dense1",torch.nn.Linear(32 * 3 * 3, 128))self.dense.add_module("relu2",torch.nn.ReLU())self.dense.add_module("dense2",torch.nn.Linear(128, 10))def forward(self, x):conv_out = self.conv(x)res = conv_out.view(conv_out.size(0), -1)out = self.dense(res)return?out2.4 序列集成字典型[OrderDict集成模型字典【‘name’:層函數(shù)】]
name為key
lass Net4(torch.nn.Module):def __init__(self):super(Net4, self).__init__()self.conv = torch.nn.Sequential(OrderedDict([("conv1", torch.nn.Conv2d(3, 32, 3, 1, 1)),("relu1", torch.nn.ReLU()),("pool", torch.nn.MaxPool2d(2))]))self.dense = torch.nn.Sequential(OrderedDict([("dense1", torch.nn.Linear(32 * 3 * 3, 128)),("relu2", torch.nn.ReLU()),("dense2", torch.nn.Linear(128, 10))]))def forward(self, x):conv_out = self.conv1(x)res = conv_out.view(conv_out.size(0), -1)out = self.dense(res)return?out3. pytorch 對(duì)模型參數(shù)的訪問,初始化,共享
3.1 訪問參數(shù)
訪問層
如果采用序列集成型,序列添加型或者字典集成性,都只能使用id索引訪問層。eg:net[1];
如果想以網(wǎng)絡(luò)的name訪問,eg:net.layer_name。
訪問參數(shù)【權(quán)重參數(shù)名:層名_weight/bias】
layer.params----訪問該層參數(shù)字典;
layer.weight , layer.bias-----訪問該層權(quán)重和偏置;
layer.weight.data()/grad() ------訪問該層權(quán)重的具體數(shù)值/梯度【bias也使用】;
net.collect_params() ----返回該網(wǎng)絡(luò)的所有參數(shù),返回一個(gè)由參數(shù)名稱到實(shí)例的字典。
3.2 初始化[若非首次初始化,force_reinit=True]
常規(guī)初始化【網(wǎng)絡(luò)初始化】
init 利用各種分布初始化
init 對(duì)網(wǎng)絡(luò)參數(shù)進(jìn)行常數(shù)初始化
特定參數(shù)初始化
(某參數(shù)).initialize(init=init.Xavier(),force_reinit=True)自定義初始化
繼承init的Initialize類,并實(shí)現(xiàn)函數(shù)_init_weight(self,name,data)
def _init_weight(self, name, data):print('Init', name, data.shape)data[:] = nd.random.uniform(low=-10, high=10, shape=data.shape)# 表示一半幾率為0,一半幾率為[-10,-5]U[5,10]的均勻分布data *= data.abs() >= 5 # 調(diào)用自定義初始化函數(shù)1 net.initialize(MyInit(), force_reinit=True)3.3 參數(shù)共享
參數(shù)共享,梯度共享,但是梯度計(jì)算的是所有共享層的和
梯度共享,且梯度只更新一次
pytorch在SVHN網(wǎng)絡(luò)構(gòu)建實(shí)戰(zhàn)
構(gòu)建網(wǎng)絡(luò)模型:繼承nn.Module函數(shù)的__init__ 函數(shù),重定義前向傳播函數(shù)forward
構(gòu)造優(yōu)化器
構(gòu)造損失函數(shù)
訓(xùn)練 確定幾個(gè)epoch【若運(yùn)用數(shù)據(jù)增廣,隨機(jī)增廣epoch次達(dá)到多樣性】
對(duì)每個(gè)batch損失函數(shù)后向傳播,優(yōu)化器更新參數(shù)
? ?optimizer.zero_grad()?清空梯度
? ?loss.backward()
? ?optimizer.step()
4.1 普通自建網(wǎng)絡(luò)
class SVHN_model(nn.Module):def __init__(self):super(SVHN_model,self).__init__()self.cnn = nn.Squential(nn.Conv2d(3,16,kernel_size=(3,3),stride=(2,2)), #3X64X128--> 16X31X63nn.Relu(),nn.MaxPool2d(2), #16X31X63--> 16X15X31nn.Conv2d(16,32,kernel_size=(3,3),stride=(2,2)),#16X15X31--> 32X7X15nn.Relu(),nn.MaxPool2d(2) #32X7X15--> 32X3X7)# 并行五次字符預(yù)測(cè)self.fc1 = nn.Linear(32*3*7,11)self.fc2 = nn.Linear(32*3*7,11)self.fc3 = nn.Linear(32*3*7,11)self.fc4 = nn.Linear(32*3*7,11)self.fc5 = nn.Linear(32*3*7,11)def forward(self,x):cnn_result = self.cnn(x)cnn_result = cnn_result.view(cnn_result.shape[0],-1)f1 = fc1(cnn_result)f2 = fc2(cnn_result)f3 = fc3(cnn_result)f4 = fc4(cnn_result)f5 = fc5(cnn_result)return?f1,f2,f3,f4,f54.2 利用resnet預(yù)訓(xùn)練模型
class SVHN_resnet_Model(nn.Module):def __init__(self):super(SVHN_resnet_Model,self).__init__()resnet_conv = models.resnet18(pretrain=True)resnet_conv.avgpool = nn.AdaptiveAvgPool2d(1)resnet_conv = nn.Sequential(*list(resnet_conv.children()[:-1]))self.cnn = model_convself.fc1 = nn.Linear(512,11)self.fc2 = nn.Linear(512,11)self.fc3 = nn.Linear(512,11)self.fc4 = nn.Linear(512,11)self.fc5 = nn.Linear(512,11)def forward(self):cnn_result = cnn(x)cnn_result.view(cnn_result.shape[0],-1)f1 = fc1(cnn_result)f2 = fc2(cnn_result)f3 = fc3(cnn_result)f4 = fc4(cnn_result)f5 = fc5(cnn_result)return?f1,f2,f3,f4,f5延伸閱讀:
書籍:《深度實(shí)踐OCR:基于深度學(xué)習(xí)的文字識(shí)別》
作者:劉樹春 阿里巴巴本地生活研究院算法專家,前復(fù)旦七牛云聯(lián)合實(shí)驗(yàn)室OCR算法負(fù)責(zé)人
????點(diǎn)擊閱讀原文,學(xué)習(xí)實(shí)踐
總結(jié)
以上是生活随笔為你收集整理的【深度学习】深入浅出卷积神经网络及实现!的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【深度学习】270篇CVPR 2020代
- 下一篇: 【深度学习】9 大主题卷积神经网络(CN