PyTorch-07 卷积神经网络(什么是卷积、卷积神经网络、池化层、Batch normalization、经典卷积网络、深度残差网络 ResNet、nn.Module、数据增强)
PyTorch-07 卷積神經(jīng)網(wǎng)絡(luò)(什么是卷積、卷積神經(jīng)網(wǎng)絡(luò)、池化層、Batch normalization、經(jīng)典卷積網(wǎng)絡(luò)(LeNet-5、AlexNet、VGG、GoogLeNet)、深度殘差網(wǎng)絡(luò) ResNet、nn.Module(使用nn.Module的好處)、數(shù)據(jù)增強(qiáng)(Data Argumentation))
一、什么是卷積
deep learning 一般使用0-1這個(gè)范圍,但是數(shù)據(jù)存儲(chǔ)是0-255,所以我們加載進(jìn)來時(shí)除以255這個(gè)分?jǐn)?shù),使其達(dá)到0-1這個(gè)區(qū)間內(nèi)。
What’s wrong with Linear
全連接層,在pytorch中也叫線性層,線性層僅僅表達(dá)了全連接網(wǎng)絡(luò)的線性部分,線性層后面會(huì)增加一個(gè)非線性單元,叫做激活函數(shù),因此不論是叫線性層還是叫全連接層,都指的是整個(gè)網(wǎng)絡(luò)。我們實(shí)現(xiàn)的時(shí)候linear是沒有包含激活函數(shù)這部分的。
對(duì)于下圖,這個(gè)神經(jīng)網(wǎng)絡(luò)一共有4層,3個(gè)隱藏層和1個(gè)輸出層,input layer輸入層是不把它計(jì)算在內(nèi)的,僅僅作為數(shù)據(jù)的輸入。對(duì)于某一層,是指這一層的權(quán)值w和這一層的輸出加在一起叫做一層。
輸入的本來是28*28的matrix,這里為了方便全連接層的處理,因此打平為784維的向量。中間節(jié)點(diǎn)全部去256,一共有4個(gè)hidden layer,每個(gè)hidden layer是256,和一個(gè)輸出層10類。
這個(gè)網(wǎng)絡(luò)一共有多少參數(shù)量呢?多少參數(shù)量就意味著有多少條線,784256 + 256256 + 256256 + 25610 = 390k parameter參數(shù)量。每一個(gè)參數(shù)是用一個(gè)四字節(jié)的浮點(diǎn)數(shù)來表示的。390k *4 = 1.6MB memory的顯存
Receptive Field 感受領(lǐng)域(局部相關(guān)性)
模仿人眼感受局部相關(guān)性的一個(gè)機(jī)制,提出了卷積神經(jīng)網(wǎng)絡(luò),這里卷積指的就是局部相關(guān)性,一次感受的就是一個(gè)小方塊,而不是一個(gè)大區(qū)間,一次感受的是一個(gè)小的視野,。
Weight sharing 權(quán)值共享
這樣一個(gè)小窗口,會(huì)掃過一個(gè)大的圖片,在掃合格大的圖片過程中使用了同一個(gè)權(quán)值w,也就是w這個(gè)參數(shù)是不變的,掃完一次之后,只有在back propagate反向傳播之后才更新一次w。因此小窗口掃描大圖片的過程中,通過局部相關(guān)性促成這種共享的機(jī)制存在,使用相同的w掃描大圖的過程叫做權(quán)值共享。以后會(huì)在RNN中使用權(quán)值共享這個(gè)概念在里面。使用權(quán)值共享會(huì)讓參數(shù)量下降。
用于郵編的識(shí)別:LeNet-5,會(huì)讓參數(shù)量下降了原來的1/6。
Convolution Operation卷積操作
卷積操作就是小窗口33不斷的與大窗體對(duì)應(yīng)位置進(jìn)行計(jì)算,獲得一個(gè)輸出點(diǎn),通過不斷的循環(huán),完成對(duì)所有點(diǎn)的計(jì)算后生成新的窗體,該新窗體大小和原來的窗體大小是相同的。
之前的連接是2828整個(gè)大窗體的連接,每個(gè)點(diǎn)都通過整個(gè)大窗體來進(jìn)行計(jì)算,每個(gè)節(jié)點(diǎn)都有784條連線;而卷積計(jì)算只會(huì)和每個(gè)節(jié)點(diǎn)相關(guān)的9個(gè)節(jié)點(diǎn)相連線,這個(gè)小窗口只有9根線進(jìn)行連接,其他的775條都是斷開的。
小窗體和小窗體對(duì)應(yīng)大窗體位置的小窗體可以進(jìn)行矩陣的計(jì)算,也可以進(jìn)行相應(yīng)元素上的相乘后再累加,從而會(huì)的一個(gè)點(diǎn),這種相乘在累加后生成一個(gè)點(diǎn)的操作叫做卷積操作。
Why call Convolution?為什么叫卷積?
卷積操作在信號(hào)處理鄰域中:
是一個(gè)偏移的積分運(yùn)算過程。
這種信號(hào)鄰域的計(jì)算如何才能對(duì)應(yīng)到圖片中的卷積運(yùn)算中呢?
圖像上的積分運(yùn)行,就是對(duì)應(yīng)窗口位置相乘再累加。
Convolution卷積
對(duì)一個(gè)圖片進(jìn)行Sharpen操作,讓圖片變得更銳化,因此我們就使用一個(gè)5*5的kernal核,和圖片進(jìn)行卷積運(yùn)算。圖像上的積分運(yùn)行,就是對(duì)應(yīng)窗口位置相乘再累加。
除了上面的Sharpen銳化操作,還有一個(gè)是Blur模糊化。
除了上面兩個(gè)還有一個(gè)是Edge Detect邊緣檢測(cè)。
CNN on feature maps CNN專題圖
每次掃描使用的不同kernal,則獲得的圖片是不一樣的,下面可以看到小窗體在開始是紅色框,第二次是綠色框,這兩次計(jì)算后生成的結(jié)果是不同,因?yàn)閗ernal不同。
二、卷積神經(jīng)網(wǎng)絡(luò)
計(jì)算結(jié)束后,原來的圖片如果是2828,由于kernal是33,在靠近到原圖片的邊緣時(shí),最邊緣的一圈kernal是無法靠近的,因?yàn)槿绻麑ernal中心點(diǎn)對(duì)準(zhǔn)原圖片邊緣的像素,則kernal會(huì)有一些超出原圖像,為了不讓kernal不超出原圖片,所以新圖片是26*26,會(huì)比原來的小一圈。
如果進(jìn)行padding填充操作,即讓原來的2828周圍增加一圈0,就可以保證輸出的新函數(shù)是2828。
多個(gè)kernal代表著有多個(gè)不同的觀察角度。
原圖像(1,28,28)
kernal(7,3,3)
新圖像(7,26,26)
卷積運(yùn)算:
Notation注意
1、Input_channels:表示輸入圖片有幾個(gè)通道,黑白圖片通道數(shù)是1,彩色圖片通道數(shù)3。
2、Kernal_channels:2 ch,這里是指kernal有一個(gè)Blur和一個(gè)Edge Detect組成的2 ch,(注意,如果輸入圖片是三通道的,則kernal就有三個(gè)與原圖片對(duì)應(yīng),但是kernal的channel不是與原圖片通道數(shù)來相互對(duì)應(yīng)的,是與Blur和Edge Detect來對(duì)應(yīng)的,所以kernal有2 ch,可以理解為kernal的類型數(shù)量,但是有3個(gè)一樣的Blur和3個(gè)一樣Edge與彩色圖片相互對(duì)應(yīng),這里的3表示1個(gè)ch的與原彩色圖片對(duì)應(yīng)的三個(gè)通道)(這個(gè)很容易混淆,需要理解)。
3、Kernal_size:3*3
4、Stride:這里指的是移動(dòng)的步長,kernal小窗體移動(dòng)的步長。
5、Padding:這里指填充于原圖片周圍的一圈0,padding=1,表示一圈0,padding=2為二圈。
更加常見的情況 Multi-Kernels
LeNet-5 郵政編碼的識(shí)別的網(wǎng)絡(luò):
Pyramid Architecture金字塔結(jié)構(gòu)
representation learning 特征學(xué)習(xí)的由來:特征不斷的提取的過程。
nn.Conv2d 類接口
stride=2,會(huì)有降維的效果,輸出的新圖會(huì)比原圖小。
這里盡量不要直接使用layer.forward()。推薦使用實(shí)例的方法,out = layer(x),這個(gè)其實(shí)是調(diào)用的是python的魔法 .call 函數(shù)。pytorch在__call__函數(shù)中封裝了一些hooks,這些hooks有一些高階的特性,如果要使用這些hooks,就必須要使用layer()類的實(shí)例來調(diào)用,如果使用layer.forward()這種方法,就沒辦法使用pytorch提供的一些hooks方法。不要直接調(diào)用.forward()。
Inner weight & bias
F.conv2d 函數(shù)式接口
三、池化層 (Pooling層,即下采樣)Down/up sample
Outline
? upsample 上采樣,和圖片放大非常類似。
? Pooling 下采樣,就意味著是將feature map變小的操作。
卷積神經(jīng)網(wǎng)絡(luò)配套的一個(gè)網(wǎng)絡(luò)層叫:Pooling層
? ReLU
1、Downsample 下采樣
對(duì)于圖片數(shù)據(jù)而言,Downsample下采樣類似于降維的這種操作。
Max pooling
在卷積神經(jīng)網(wǎng)絡(luò)中,是使用max pooling來進(jìn)行類似于上面那種降維操作的。
Avg pooling
就是獲取小窗口所有像元的均值。
使用pytorch來完成pooling這樣的操作
這里可以注意,convolution的kernal會(huì)改變?cè)瓐D像的channel,而這里的pooling是不會(huì)改變?cè)瓐D像channel的數(shù)量的。下面的代碼可以看出輸入和輸出的channel是沒有改變的,依然是16。
2、Upsample 上采樣
在pytorch中如何使用 F.interpolate
interpolate表示插值的意思。
這里依然是不會(huì)改變?cè)瓐D像的channel數(shù)量。下面的代碼是pytorch自帶的實(shí)現(xiàn)擴(kuò)大方法,的對(duì)于tensor這種數(shù)據(jù)類型的可以直接使用的。
ReLU
是將圖片feature map中負(fù)的像元給去掉的過程,那些像元響應(yīng)太低了,就把那些小于0的像元給去掉。下圖中黑色部分就是響應(yīng)太小了,去掉后就變成了右圖的樣子,像素值變?yōu)榱?。
在pytorch中如何使用 F.relu()
inplace = True 表示可以使用原先變量的內(nèi)存空間,這樣可以減少內(nèi)存的使用。可以發(fā)現(xiàn)使用relu后,輸入和輸出的shape是沒有變化的,但是像元值發(fā)生了改變,最小值不在是負(fù)值,而變?yōu)榱?。
四、Batch normalization(數(shù)據(jù)歸一化方法)
重要概念,Batch normalization,這個(gè)概念會(huì)在卷積神經(jīng)網(wǎng)絡(luò)中、循環(huán)神經(jīng)網(wǎng)絡(luò)中等都會(huì)用到。
Batch Normalization是2015年一篇論文中提出的數(shù)據(jù)歸一化方法,往往用在深度神經(jīng)網(wǎng)絡(luò)中激活層之前。. 其作用可以加快模型訓(xùn)練時(shí)的收斂速度,使得模型訓(xùn)練過程更加穩(wěn)定,避免梯度爆炸或者梯度消失。. 并且起到一定的正則化作用,幾乎代替了Dropout(dropout是指在深度學(xué)習(xí)網(wǎng)絡(luò)的訓(xùn)練過程中,對(duì)于神經(jīng)網(wǎng)絡(luò)單元,按照一定的概率將其暫時(shí)從網(wǎng)絡(luò)中丟棄)。
直觀的解釋Intuitive explanation
對(duì)于使用sigmoid函數(shù)來說,當(dāng)輸入大于某個(gè)區(qū)間范圍后,sigmoid的導(dǎo)數(shù)會(huì)接近于0,這樣就容易出現(xiàn)梯度離散的情況。因此很有必要對(duì)輸入做一定的限制。normalization就是用來最這個(gè)限制的,讓輸入數(shù)據(jù)等效變換,使其輸入的數(shù)據(jù)滿足一定的正太分布N(0,σ^2),希望這個(gè)值能夠均勻落在0的附近,使其在小的范圍內(nèi)變動(dòng),這樣再做下一層的時(shí)候,優(yōu)化起來會(huì)更加方便。
可以發(fā)現(xiàn)下圖中,左側(cè)的圖,x1是在一個(gè)比較小的值,x2是在一個(gè)比較大的值,因此當(dāng)w1進(jìn)行改變時(shí),x1的變化會(huì)比較小,當(dāng)w2進(jìn)行改變時(shí),x2會(huì)急劇的變化,這樣在搜索全局最小值的過程會(huì)比較曲折一些,左圖的箭頭所示。
如果x1和x2的范圍是一樣的,w1和w2對(duì)最終loss是一樣的,會(huì)形成右圖圓形的路徑,在進(jìn)行搜索的時(shí)候,不管從哪個(gè)點(diǎn)出發(fā),梯度所指的方向都是全局最小解的方向。這樣搜索過程會(huì)比較快捷方便穩(wěn)定。
Feature scaling特征縮放
Batch Normalization
我們這里只看Batch normalization。
這里需要注意的是,μ和σ是統(tǒng)計(jì)出來的,是不需要參與反向傳播(back propagation)的,是根據(jù)當(dāng)前的batch里面的統(tǒng)計(jì)數(shù)據(jù)統(tǒng)計(jì)出來的;但是γ和β是學(xué)出來的,是需要梯度信息的,剛開始(γ=1, β=0)是沒有影響,慢慢會(huì)學(xué)到一個(gè)N(β為均值, γ為方差)的偏置。
此外μ和σ會(huì)有一個(gè)歷史記錄,記錄每一個(gè)batch的總的均值和總的方差,總的均值和總的方差在命名上μ和σ前面增加了一個(gè)running,running-μ和running-σ2,這樣命名就是表示運(yùn)行時(shí)候的統(tǒng)計(jì)數(shù)據(jù),表示所有training出來的總的均值和總的方差。
利用pytorch進(jìn)行這樣的一個(gè)計(jì)算:這里是針對(duì)1維數(shù)據(jù)進(jìn)行操作的。
batch normalize規(guī)范化的寫法
下面的步驟就是batch normalize train的過程。
1、第一步統(tǒng)計(jì)當(dāng)前batch的μв均值和方差σ2в,這里還自動(dòng)更新running-μ和running-σ2。
2、第二步進(jìn)行normalize的操作。注意在進(jìn)行normalize的時(shí)候分母會(huì)加一個(gè)很小的值10^-8,這樣做是為了避免除零錯(cuò)誤。計(jì)算完成后再進(jìn)行一個(gè)平移β和縮放γ,使其達(dá)到N(均值β,方差γ)的正太分布。在反向傳播(back propagation) 的時(shí)候,β和γ需要梯度信息,因此這兩個(gè)參數(shù)會(huì)自動(dòng)更新。
利用pytorch進(jìn)行這樣的一個(gè)計(jì)算:這里是針對(duì)2維數(shù)據(jù)進(jìn)行操作的。
Test
這里需要注意的是,和drop out一樣,batch normalization這個(gè)layer在training和test情況下行為是不太一樣的。
在training的時(shí)候,μ和σ會(huì)統(tǒng)計(jì)出來,并自動(dòng)更新一下running,之后再反向傳播(back propagation),更新一下γ和β。
在test的時(shí)候,一般我們只會(huì)test一個(gè)sample,因此μ和σ2是沒法統(tǒng)計(jì)的,因?yàn)橹挥幸粋€(gè)sample,所以就沒有必要進(jìn)行統(tǒng)計(jì)。因此在test的時(shí)候,μ和σ取的不是當(dāng)前batch的μ和σ2,而在test的時(shí)候?qū)⑷祌unning-μ和running-σ賦值為μ和σ2。此外在test的時(shí)候,是沒有backward的,γ和β是不需要更新的。要實(shí)現(xiàn)test,就需要提前切換到test這個(gè)模式下,必須調(diào)用.eval()這個(gè)函數(shù),設(shè)置為test模式,再調(diào)用BatchNorm1d()進(jìn)行變換。
可視化Visualization
曲線圖中的虛線是使用batch normalize的,實(shí)線的是沒有使用的。
可以發(fā)現(xiàn)使用了batch normalize,收斂速度更快,精度也有所提升。我們可以查看每一層參數(shù)分布的情況,使用了后,其分布情況是圍繞均值為0附近,總的來說會(huì)往0附近來靠近,并不一定是0,方差為1來進(jìn)行分布,這樣操作后更加利于training。這里需要注意一下,為什么不完完全全在以N(均值=0, 方差=1)這樣進(jìn)行分布呢?這是因?yàn)榇嬖讦煤挺聦?duì)其分布的調(diào)整,如果γ=1且β=0,那么其分布情況就不會(huì)是以N(均值=0, 方差=1)這樣進(jìn)行分布,其分布就是N(β為均值, γ為方差)這樣的分布。
總結(jié)一下使用batch normalize的好處Advantage
? Converge faster
收斂速度更快,在使用sigmoid激活函數(shù)的時(shí)候,如果把數(shù)據(jù)限制到0均值單位方差,那么相當(dāng)于只使用了激活函數(shù)中近似線性的部分,這顯然會(huì)降低模型表達(dá)能力,但是收斂速度更快。
? Better performance
更加容易搜索出最優(yōu)解
? Robust
? stable
變得更穩(wěn)定了
? larger learning rate
更大的設(shè)置學(xué)習(xí)率的范圍
五、經(jīng)典卷積網(wǎng)絡(luò)(CNN)
下圖中是最近10年內(nèi)的網(wǎng)絡(luò)變化,其中柱狀圖上面的數(shù)字表示錯(cuò)誤率(準(zhǔn)確率=1-錯(cuò)誤率)。
LeNet-5
在1989年,Yann LeCun提出了一種用反向傳導(dǎo)進(jìn)行更新的卷積神經(jīng)網(wǎng)絡(luò),稱為LeNet。
最開始的LeNet-5是用于一個(gè)手寫數(shù)字的識(shí)別,手寫數(shù)字的圖片只有28*28,當(dāng)時(shí)的網(wǎng)絡(luò)結(jié)構(gòu)并不深,而且每一層的參數(shù)量也不大,即使這樣這個(gè)網(wǎng)絡(luò)結(jié)構(gòu)也是沒有辦法在GPU上跑的,因?yàn)楫?dāng)時(shí)GPU還沒有,是386的年代。
這個(gè)準(zhǔn)確度達(dá)到了99.2%,就可以很成熟的使用起來了。
所以在當(dāng)時(shí)美國的支票票號(hào)識(shí)別,郵編識(shí)別就已經(jīng)廣泛使用了。
AlexNet
LeNet-5是80年代出來的,那個(gè)時(shí)候deep learning還沒有很熱,在2012年,出來了AlexNet,準(zhǔn)確率有了很高的提升。當(dāng)時(shí)是使用兩個(gè)GTX580(3GB)顯卡來進(jìn)行該網(wǎng)絡(luò)的運(yùn)算,該網(wǎng)絡(luò)是5個(gè)卷積層,總共是8個(gè)layers,AlexNet的小窗口是11*11的,此外第一次使用ReLU函數(shù)、Max pooling、Dropout。
VGG
VGGNet是來自于牛津大學(xué)的視覺研究組。VGG一共有6個(gè)版本(比如VGG11、VGG16、VGG19)。VGG探索出了,小的kernal窗口不但可以減少計(jì)算量,并且沒有損害精度,而且計(jì)算速度很快,這也是VGG最核心的價(jià)值。探索出了更小型的kernal更好的效果。
這里我們先了解一下1*1卷積核:
1*1 Convolution
可以注意到的是,輸入的channel是32,而到了輸出channel就是16了。這里的輸出的channel取決于kernal的channel。此外1*1的kernal可以保證輸入和輸出的圖片大小是一樣的。
GoogLeNet
這里有不同大小的kernal,此外還有一個(gè)合并的操作Filter concatenation,為滿足這個(gè)操作的要求,需要確保所有不同kernal輸出來的圖像大小相同,因?yàn)橛幸粋€(gè)33 max pooling,所有輸出來的圖像大小要與max pooling輸出來的相同即可。假設(shè)max pooling輸出的是77的圖像,則kernal輸出的也必須是77(為了保證輸出的是77,可以使用stride步長來控制),這樣可以獲得很多個(gè)(32,7,7),之后通過concatenation操作后,可以獲得(32,4,7,7)的feature map。
之前使用的都是統(tǒng)一的kernal,而這里為什么要使用不同類型的kernal呢?這是因?yàn)槊恳粋€(gè)不同大小的kernal所感受的視野是不同,11 kernal感受的視野是一個(gè)點(diǎn),33 kernal感受的視野是一個(gè)小窗體,通過綜合不同大小的視野(綜合局部和全局的信息),可以感受到一個(gè)跟好的信息量。
具體的結(jié)構(gòu)是:
堆疊更多層更好嗎?Stack more layers?
通過堆疊更多層數(shù)的時(shí)候,層數(shù)更多的時(shí)候,反而準(zhǔn)確度并沒有提升,反而training比較困難,training的error比較高,這告訴我們并不能簡單的堆疊更多的層數(shù)(這里需要拋開4層到22層,這范圍內(nèi)是堆疊更多層數(shù)效果會(huì)有提升,但是超過20層以上之后,僅是簡單的堆疊的話,training的難度非常大,training找到最優(yōu)解的難度很大)。
現(xiàn)在我們的網(wǎng)絡(luò)結(jié)構(gòu)以及是1000多層了,那如何解決這個(gè)問題的呢?也就是ResNet。
六、深度殘差網(wǎng)絡(luò) ResNet
殘差神經(jīng)網(wǎng)絡(luò)(ResNet)是由微軟研究院的何愷明、張祥雨、任少卿、孫劍等人提出的。ResNet 在2015 年的ILSVRC(ImageNet Large Scale Visual Recognition Challenge)中取得了冠軍。
殘差神經(jīng)網(wǎng)絡(luò)的主要貢獻(xiàn)是發(fā)現(xiàn)了“退化現(xiàn)象(Degradation)”,并針對(duì)退化現(xiàn)象發(fā)明了 “快捷連接(Shortcut connection)”,極大的消除了深度過大的神經(jīng)網(wǎng)絡(luò)訓(xùn)練困難問題。神經(jīng)網(wǎng)絡(luò)的“深度”首次突破了100層、最大的神經(jīng)網(wǎng)絡(luò)甚至超過了1000層。
ResNet構(gòu)建塊Unit,選擇什么樣的結(jié)構(gòu)合適
這個(gè)Unit選擇一個(gè)2-3層的卷積層,再加一個(gè)short。為了能夠shortcut,就意味著經(jīng)過shortcut輸出的結(jié)果要與輸入時(shí)的維度相同且channel也不能衰減,即輸入時(shí)256層,則shortcut時(shí)也要是256層。
我們來計(jì)算一下參數(shù)量,輸出256,輸入256,每一個(gè)kernal是33,則參數(shù)量大概有25625633 = 589,824 大約有600k左右。如果有三個(gè)這樣的Convolution的話,還需要再乘以3。
所以為了減少這個(gè)參數(shù)量,將unit中的kernal的size減小。比如說輸入時(shí)是(256,224,224),通過第一個(gè)11 kernal時(shí)的結(jié)果是(64,224,224);再通過第二個(gè)33 kernal時(shí),進(jìn)行padding處理后保持shape不變,依然是(64,224,224);最后通過第三個(gè)1*1 kernal后,將輸出結(jié)果恢復(fù)到原來的(256,224,224)。
輸入時(shí)一個(gè)(256,224,224),和shortcut的(256,224,224)進(jìn)行對(duì)應(yīng)元素位置的相加,同樣大小的矩陣相加,是同位置直接相加,不改變其維度,最后結(jié)果也是(256,224,224),是沒有改變的。
再來計(jì)算一下參數(shù)量,2566411 ~ 16k;646433 ~ 36k;642561*1 ~ 16k;這樣總共 Total是大約70k。和原來的600k參數(shù)量相比,大概少了9倍。
查看一下34層的ResNet的基本結(jié)構(gòu):
ResNet的爆炸性的效果
Why call Residual? 為什么叫殘差呢?
F(x)表示unit卷積層學(xué)習(xí)的內(nèi)容,原來學(xué)習(xí)的是H(x),F(x) + x = H(x) 原來學(xué)習(xí)的情況。
對(duì)于unit中的卷積層來說,F(x) = H(x) - x。這個(gè)減號(hào)就是殘差的由來。
跨時(shí)間維度上不同網(wǎng)絡(luò)的比較
對(duì)于一個(gè)ResNet是如何實(shí)現(xiàn)的呢?
這里關(guān)鍵是如何實(shí)現(xiàn)一個(gè)Unit基本單元。
這里需要注意的是,我們可以將輸入數(shù)據(jù)的channel和輸出數(shù)據(jù)的channel設(shè)置成為不同的,我們只需在shortcut路徑上增加一個(gè)1*1 kernal 將輸入的channel通過這個(gè)kernal的channel進(jìn)行調(diào)整,從而將輸入的channel和輸出channel有相同的數(shù)量。在shortcut的路徑上增加一個(gè)調(diào)整channel的kernal,就可以不用擔(dān)心輸入和輸出channel不同的問題了。
ResNet例子:
DenseNet
基于ResNet的短接層思路,可以推廣到,每一個(gè)unit都可以和前面所有層都有一個(gè)短接,這樣就有了DenseNet。
這里需要注意的是,DenseNet的每一次短接過程,其實(shí)是一個(gè)concat過程,是將之前的所有信息進(jìn)行綜合,不是element wise的相加操作,這樣會(huì)使得最后的channel越來越大,所以DenseNet對(duì)于channel的選擇要設(shè)計(jì)的很精妙,舍得后面的channel不至于過大。
七、nn.Module(使用nn.Module的好處)
pytorch中使用非常廣泛的類叫nn.Module。
nn.Module類是所有網(wǎng)絡(luò)層的父類,當(dāng)需要實(shí)現(xiàn)自己的一個(gè)層的時(shí)候就必須要繼承這個(gè)父類。
Magic
? Every Layer is nn.Module
nn.Module在pytorch中是一個(gè)基本的父類,當(dāng)要實(shí)現(xiàn)一個(gè)前項(xiàng)計(jì)算圖的時(shí)候,如果繼承了nn.Module就可以非常方便的使用現(xiàn)成的一些類:
比如說: ? nn.Linear
? nn.BatchNorm2d
? nn.Conv2d
? nn.Module nested in nn.Module
除了使用這些類以外,還可以嵌套nested,每個(gè)nn.Module后又可以嵌套nn.Module。
使用nn.Module的好處
1、embed current layers
在nn.Module中啟用了大量現(xiàn)成的神經(jīng)網(wǎng)絡(luò)計(jì)算的模塊。使用這些現(xiàn)成的模塊非常方便,只需要調(diào)用初始函數(shù),再通過.__call__方法調(diào)用forward函數(shù),就可以使用這個(gè)模塊提供的功能了。
比如: ? Linear
? ReLU
? Sigmoid
? Conv2d
? ConvTransposed2d
? Dropout
? etc.
2、Container容器
Container就是nn.Sequential()這個(gè)類。
nn.Sequetial:按順序包裝多個(gè)網(wǎng)絡(luò)層(使用容器的時(shí)候,容器里面的model屬于容器,而這個(gè)容器屬于本網(wǎng)絡(luò))注意哦,這個(gè)容器后面括號(hào)里都是參數(shù)所以要用逗號(hào)隔開。
sequential是一個(gè)時(shí)序模型,根據(jù)每個(gè)submodule傳入的順序?qū)懙接?jì)算圖里,在forward的時(shí)候也會(huì)順序執(zhí)行。
self.net(x)就可以直接自動(dòng)完成多次的forward的操作。
3、parameters 權(quán)值和偏置參數(shù)
使用nn.Module可以很好的對(duì)模型參數(shù)進(jìn)行管理,named_parameters()/parameters()方法,這兩個(gè)方法都會(huì)返回一個(gè)用于迭代模型參數(shù)的迭代器(named_parameters還包括參數(shù)名字)。
比如說下圖中使用Container容器,將兩個(gè)線性層容在一起,名為net;通過net.parameters()這個(gè)方法可以返回一個(gè)生成器,其保存著每層的weight權(quán)值和bias偏置。
import torch from torch import nn print(nn.Linear(4,2)) print('=======================') net = nn.Sequential(nn.Linear(4,2),nn.Linear(2,2)) # torch.nn.Linear(in_features, # 輸入的神經(jīng)元個(gè)數(shù) # out_features, # 輸出神經(jīng)元個(gè)數(shù) # bias=True # 是否包含偏置 # ) print(net.parameters()) print('=======================') print(list(net.parameters())) print('=======================') print(list(net.parameters())[0]) print(list(net.parameters())[0].shape) #這里為什么是(2,4)呢?因?yàn)閣是輸出維度在前面,所以是(2,4),是layer0的w。 print('=======================') print(list(net.parameters())[3]) #這里是layer1的bias偏置,所以是(2)print('=======================') #通過.parameters可以很好的返回一個(gè)迭代器,返回當(dāng)前這個(gè)net的所有參數(shù)。 #因此繼承了nn.Module就不需要額外管理這些參數(shù)。 print('=======================')#這個(gè)參數(shù)有兩種形式, # 一個(gè)是帶名字的parameters,返回一個(gè)dictionary字典,字典第一個(gè)是名字,這個(gè)名字是pytorch自動(dòng)生成的。 #這里我們將字典轉(zhuǎn)換為了list。 #'0.weight' 表示第0層的weight print(list(net.named_parameters())[0]) #'0.bias' 表示第0層的bias print(list(net.named_parameters())[1]) print('=======================') #這里我們以字典形式打印出來 print(dict(net.named_parameters()).items()) #這里item()方法是返回這個(gè)字典中的所有屬性 print('******************************') #一個(gè)是不帶名字的parameters。 print(list(net.parameters())[0])#我們可以通過.parameter,將所有參數(shù)傳入到優(yōu)化器中。 #之前我們是通過[w1,b1,w2,b2]這種方式自己管理參數(shù),將這些傳入到optimizer中的。 #現(xiàn)在就直接將這個(gè)類的.parameters()傳入其中即可,這樣使用時(shí)非常非常方便的。
4. modules 查看子類
? modules: all nodes
對(duì)于內(nèi)部的類可以做到很好的管理。所有的子節(jié)點(diǎn)可以稱為modules。
? children: direct children
直接的子節(jié)點(diǎn),我們稱其為children。
使用nn.modules()可以查看所有的子類情況。
其返回的是module其本身0號(hào)、其直系兒子1號(hào)和其它所有子節(jié)點(diǎn)。
5、to(device) 切換GPU或CPU
可以非常方便的將一個(gè)類所有的成員函數(shù)、所有內(nèi)部的tensor和所有的操作轉(zhuǎn)移到GPU或CPU上。
這里需要特別注意,當(dāng)a是tensor類型時(shí),a.to 返回的是 a_GPU,注意這里的a_GPU和原來的a.to的a不是同一個(gè)東西,a.to的a是CPU上的。而對(duì)于nn.Module來說,net = net.to。
6、save and load 保存和加載網(wǎng)絡(luò)中間狀態(tài)
在使用pytorch進(jìn)行深度學(xué)習(xí)訓(xùn)練的時(shí)候,經(jīng)常會(huì)有些內(nèi)容需要保存下來,保存到硬盤張,不管什么時(shí)候我們都可以讀取到,那么這個(gè)時(shí)候,使用torch.save()就可以將內(nèi)容存儲(chǔ)器來,使用torch.load()就可以將存取的內(nèi)容讀取出來。
save and load保存和加載 Checkpoint 用于推理/繼續(xù)訓(xùn)練,Checkpoint是網(wǎng)絡(luò)的中間狀態(tài)。
保存和加載是可以用于在斷電等特殊情況下,不至于失去之前訓(xùn)練的結(jié)果,避免讓其從頭開始從新訓(xùn)練。
Save操作:
#save: torch.save(net.state_dict(), 'ckpt.mdl')Load操作:
net.load_state_dict(torch.load('ckpt.mdl'))7、train / test 切換
train和test的方便切換狀態(tài)。如果每個(gè)類都繼承與nn.Module的話,直接通過對(duì)根節(jié)點(diǎn)net.train(),或.eval(),就可以切換了。
8、implement own layer 實(shí)現(xiàn)自己的類
比如說在pytorch中沒有一個(gè)展平的功能Flatten,因此我們可以自己實(shí)現(xiàn)一個(gè)繼承與nn.Module的用于展平功能的類。
此外還有一個(gè)是reshape操作,這個(gè)功能只有方法,沒有一個(gè)類,所以很多情況下我們也需要自己創(chuàng)建一個(gè)繼承與nn.Module的用于Reshape的類。
實(shí)現(xiàn)一個(gè)自己的MyLinear,這個(gè)自己創(chuàng)建的類和nn.Linear功能是一樣的,這里可以發(fā)現(xiàn)nn.Parameter()會(huì)自動(dòng)將需要梯度設(shè)置為true,requires_grad=True,而且參數(shù)也會(huì)受到nn.parameters()的管理,注意類是大寫的Parameter,方法是小寫的parameters。
我們?cè)趂orward()函數(shù)中寫好我們的邏輯,再return,一定要返回。
八、數(shù)據(jù)增強(qiáng)(Data Argumentation)
Big Data
很大的數(shù)據(jù)集是train的好壞的前提。有了Big Data神經(jīng)網(wǎng)絡(luò)就能train的很好,有了Big Data這個(gè)網(wǎng)絡(luò)就不容易o(hù)ver fitting,這個(gè)是防止過擬合的關(guān)鍵The key to prevent Overfitting。
Limited Data 有限的數(shù)據(jù)進(jìn)行優(yōu)化
? Small network capacity
首先如果數(shù)據(jù)比較少的話,就要減少網(wǎng)絡(luò)的參數(shù)量,這樣就不容易o(hù)verfitting。
? Regularization
如果網(wǎng)絡(luò)結(jié)構(gòu)是固定的話,我們就正則化Regularization,迫使網(wǎng)絡(luò)的一部分權(quán)值w接近于0,這樣也減少了網(wǎng)絡(luò)的參數(shù)量。
? Data argumentation
對(duì)原來的數(shù)據(jù)進(jìn)行增強(qiáng),比如對(duì)原來的圖像進(jìn)行調(diào)色,旋轉(zhuǎn)角度等,獲得新的圖片,將這些圖片和原先的圖片一起用于網(wǎng)絡(luò)的訓(xùn)練。
Data argumentation
? Flip 翻轉(zhuǎn)
? Rotate 旋轉(zhuǎn)
? Scale 縮放
? Crop Part 裁剪部分
? Noise 白噪聲
? Random Move 隨機(jī)移動(dòng)
? GAN 生成對(duì)抗網(wǎng)絡(luò)
Flip翻轉(zhuǎn)
水平翻轉(zhuǎn)和豎直翻轉(zhuǎn):
.RandomHorizontalFlip()水平翻轉(zhuǎn)、.RandomVerticalFlip()豎直翻轉(zhuǎn),這兩個(gè)都是隨機(jī)的,有可能做,也有可能不做。
Rotate旋轉(zhuǎn)
.RandomRotation(15)會(huì)在-15°<0<15°會(huì)在個(gè)區(qū)間內(nèi)隨機(jī)的進(jìn)行選擇;如果我們要固定這個(gè)角度,.RandomRotation([0, 90, 180, 270])會(huì)隨機(jī)的在0°、90°、180°和270°三個(gè)角度中選取進(jìn)行旋轉(zhuǎn),如果要不旋轉(zhuǎn)的那就在加一個(gè)0°,這樣會(huì)將原來的照片數(shù)量變?yōu)樵瓉淼?被。
Scale縮放
Crop Part裁剪部分
隨機(jī)裁剪一部分,裁切的那部分用0來填補(bǔ)。
Noise白噪聲
pytorch沒有提供這個(gè)Noise接口,需要人為在Numpy基礎(chǔ)上添加這個(gè)。
這里需要總結(jié)一下數(shù)據(jù)增強(qiáng),由于增強(qiáng)后的照片與原圖片是比較接近的,所有數(shù)據(jù)增強(qiáng)是有一定的幫助的,但是幫助不會(huì)很多。
總結(jié)
以上是生活随笔為你收集整理的PyTorch-07 卷积神经网络(什么是卷积、卷积神经网络、池化层、Batch normalization、经典卷积网络、深度残差网络 ResNet、nn.Module、数据增强)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: AJAX-服务器响应
- 下一篇: 常用的几种卷积神经网络介绍