日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

PyTorch-07 卷积神经网络(什么是卷积、卷积神经网络、池化层、Batch normalization、经典卷积网络、深度残差网络 ResNet、nn.Module、数据增强)

發布時間:2023/12/20 88 豆豆

PyTorch-07 卷積神經網絡(什么是卷積、卷積神經網絡、池化層、Batch normalization、經典卷積網絡(LeNet-5、AlexNet、VGG、GoogLeNet)、深度殘差網絡 ResNet、nn.Module(使用nn.Module的好處)、數據增強(Data Argumentation))

一、什么是卷積


deep learning 一般使用0-1這個范圍,但是數據存儲是0-255,所以我們加載進來時除以255這個分數,使其達到0-1這個區間內。


What’s wrong with Linear

全連接層,在pytorch中也叫線性層,線性層僅僅表達了全連接網絡的線性部分,線性層后面會增加一個非線性單元,叫做激活函數,因此不論是叫線性層還是叫全連接層,都指的是整個網絡。我們實現的時候linear是沒有包含激活函數這部分的。

對于下圖,這個神經網絡一共有4層,3個隱藏層和1個輸出層,input layer輸入層是不把它計算在內的,僅僅作為數據的輸入。對于某一層,是指這一層的權值w和這一層的輸出加在一起叫做一層。

輸入的本來是28*28的matrix,這里為了方便全連接層的處理,因此打平為784維的向量。中間節點全部去256,一共有4個hidden layer,每個hidden layer是256,和一個輸出層10類。

這個網絡一共有多少參數量呢?多少參數量就意味著有多少條線,784256 + 256256 + 256256 + 25610 = 390k parameter參數量。每一個參數是用一個四字節的浮點數來表示的。390k *4 = 1.6MB memory的顯存

Receptive Field 感受領域(局部相關性)


模仿人眼感受局部相關性的一個機制,提出了卷積神經網絡,這里卷積指的就是局部相關性,一次感受的就是一個小方塊,而不是一個大區間,一次感受的是一個小的視野,。

Weight sharing 權值共享

這樣一個小窗口,會掃過一個大的圖片,在掃合格大的圖片過程中使用了同一個權值w,也就是w這個參數是不變的,掃完一次之后,只有在back propagate反向傳播之后才更新一次w。因此小窗口掃描大圖片的過程中,通過局部相關性促成這種共享的機制存在,使用相同的w掃描大圖的過程叫做權值共享。以后會在RNN中使用權值共享這個概念在里面。使用權值共享會讓參數量下降。

用于郵編的識別:LeNet-5,會讓參數量下降了原來的1/6。

Convolution Operation卷積操作

卷積操作就是小窗口33不斷的與大窗體對應位置進行計算,獲得一個輸出點,通過不斷的循環,完成對所有點的計算后生成新的窗體,該新窗體大小和原來的窗體大小是相同的。

之前的連接是2828整個大窗體的連接,每個點都通過整個大窗體來進行計算,每個節點都有784條連線;而卷積計算只會和每個節點相關的9個節點相連線,這個小窗口只有9根線進行連接,其他的775條都是斷開的。


小窗體和小窗體對應大窗體位置的小窗體可以進行矩陣的計算,也可以進行相應元素上的相乘后再累加,從而會的一個點,這種相乘在累加后生成一個點的操作叫做卷積操作。

Why call Convolution?為什么叫卷積?

卷積操作在信號處理鄰域中:
是一個偏移的積分運算過程。





這種信號鄰域的計算如何才能對應到圖片中的卷積運算中呢?
圖像上的積分運行,就是對應窗口位置相乘再累加。

Convolution卷積

對一個圖片進行Sharpen操作,讓圖片變得更銳化,因此我們就使用一個5*5的kernal核,和圖片進行卷積運算。圖像上的積分運行,就是對應窗口位置相乘再累加。

除了上面的Sharpen銳化操作,還有一個是Blur模糊化。

除了上面兩個還有一個是Edge Detect邊緣檢測。

CNN on feature maps CNN專題圖

每次掃描使用的不同kernal,則獲得的圖片是不一樣的,下面可以看到小窗體在開始是紅色框,第二次是綠色框,這兩次計算后生成的結果是不同,因為kernal不同。



二、卷積神經網絡


計算結束后,原來的圖片如果是2828,由于kernal是33,在靠近到原圖片的邊緣時,最邊緣的一圈kernal是無法靠近的,因為如果將kernal中心點對準原圖片邊緣的像素,則kernal會有一些超出原圖像,為了不讓kernal不超出原圖片,所以新圖片是26*26,會比原來的小一圈。

如果進行padding填充操作,即讓原來的2828周圍增加一圈0,就可以保證輸出的新函數是2828。

多個kernal代表著有多個不同的觀察角度。
原圖像(1,28,28)
kernal(7,3,3)
新圖像(7,26,26)
卷積運算:

Notation注意

1、Input_channels:表示輸入圖片有幾個通道,黑白圖片通道數是1,彩色圖片通道數3。
2、Kernal_channels:2 ch,這里是指kernal有一個Blur和一個Edge Detect組成的2 ch,(注意,如果輸入圖片是三通道的,則kernal就有三個與原圖片對應,但是kernal的channel不是與原圖片通道數來相互對應的,是與Blur和Edge Detect來對應的,所以kernal有2 ch,可以理解為kernal的類型數量,但是有3個一樣的Blur和3個一樣Edge與彩色圖片相互對應,這里的3表示1個ch的與原彩色圖片對應的三個通道)(這個很容易混淆,需要理解)。
3、Kernal_size:3*3
4、Stride:這里指的是移動的步長,kernal小窗體移動的步長。
5、Padding:這里指填充于原圖片周圍的一圈0,padding=1,表示一圈0,padding=2為二圈。

更加常見的情況 Multi-Kernels


LeNet-5 郵政編碼的識別的網絡:

Pyramid Architecture金字塔結構

representation learning 特征學習的由來:特征不斷的提取的過程。

nn.Conv2d 類接口

stride=2,會有降維的效果,輸出的新圖會比原圖小。
這里盡量不要直接使用layer.forward()。推薦使用實例的方法,out = layer(x),這個其實是調用的是python的魔法 .call 函數。pytorch在__call__函數中封裝了一些hooks,這些hooks有一些高階的特性,如果要使用這些hooks,就必須要使用layer()類的實例來調用,如果使用layer.forward()這種方法,就沒辦法使用pytorch提供的一些hooks方法。不要直接調用.forward()。

Inner weight & bias

F.conv2d 函數式接口

三、池化層 (Pooling層,即下采樣)Down/up sample

Outline
? upsample 上采樣,和圖片放大非常類似。
? Pooling 下采樣,就意味著是將feature map變小的操作。
卷積神經網絡配套的一個網絡層叫:Pooling層
? ReLU

1、Downsample 下采樣

對于圖片數據而言,Downsample下采樣類似于降維的這種操作。

Max pooling

在卷積神經網絡中,是使用max pooling來進行類似于上面那種降維操作的。

Avg pooling

就是獲取小窗口所有像元的均值。

使用pytorch來完成pooling這樣的操作

這里可以注意,convolution的kernal會改變原圖像的channel,而這里的pooling是不會改變原圖像channel的數量的。下面的代碼可以看出輸入和輸出的channel是沒有改變的,依然是16。

2、Upsample 上采樣

在pytorch中如何使用 F.interpolate

interpolate表示插值的意思。
這里依然是不會改變原圖像的channel數量。下面的代碼是pytorch自帶的實現擴大方法,的對于tensor這種數據類型的可以直接使用的。

ReLU

是將圖片feature map中負的像元給去掉的過程,那些像元響應太低了,就把那些小于0的像元給去掉。下圖中黑色部分就是響應太小了,去掉后就變成了右圖的樣子,像素值變為了0。

在pytorch中如何使用 F.relu()

inplace = True 表示可以使用原先變量的內存空間,這樣可以減少內存的使用。可以發現使用relu后,輸入和輸出的shape是沒有變化的,但是像元值發生了改變,最小值不在是負值,而變為了0。

四、Batch normalization(數據歸一化方法)

重要概念,Batch normalization,這個概念會在卷積神經網絡中、循環神經網絡中等都會用到。
Batch Normalization是2015年一篇論文中提出的數據歸一化方法,往往用在深度神經網絡中激活層之前。. 其作用可以加快模型訓練時的收斂速度,使得模型訓練過程更加穩定,避免梯度爆炸或者梯度消失。. 并且起到一定的正則化作用,幾乎代替了Dropout(dropout是指在深度學習網絡的訓練過程中,對于神經網絡單元,按照一定的概率將其暫時從網絡中丟棄)。

直觀的解釋Intuitive explanation

對于使用sigmoid函數來說,當輸入大于某個區間范圍后,sigmoid的導數會接近于0,這樣就容易出現梯度離散的情況。因此很有必要對輸入做一定的限制。normalization就是用來最這個限制的,讓輸入數據等效變換,使其輸入的數據滿足一定的正太分布N(0,σ^2),希望這個值能夠均勻落在0的附近,使其在小的范圍內變動,這樣再做下一層的時候,優化起來會更加方便。

可以發現下圖中,左側的圖,x1是在一個比較小的值,x2是在一個比較大的值,因此當w1進行改變時,x1的變化會比較小,當w2進行改變時,x2會急劇的變化,這樣在搜索全局最小值的過程會比較曲折一些,左圖的箭頭所示。
如果x1和x2的范圍是一樣的,w1和w2對最終loss是一樣的,會形成右圖圓形的路徑,在進行搜索的時候,不管從哪個點出發,梯度所指的方向都是全局最小解的方向。這樣搜索過程會比較快捷方便穩定。

Feature scaling特征縮放

Batch Normalization

我們這里只看Batch normalization。

這里需要注意的是,μ和σ是統計出來的,是不需要參與反向傳播(back propagation)的,是根據當前的batch里面的統計數據統計出來的;但是γ和β是學出來的,是需要梯度信息的,剛開始(γ=1, β=0)是沒有影響,慢慢會學到一個N(β為均值, γ為方差)的偏置。
此外μ和σ會有一個歷史記錄,記錄每一個batch的總的均值和總的方差,總的均值和總的方差在命名上μ和σ前面增加了一個running,running-μ和running-σ2,這樣命名就是表示運行時候的統計數據,表示所有training出來的總的均值和總的方差。

利用pytorch進行這樣的一個計算:這里是針對1維數據進行操作的。

import torchfrom torch import nn#這里是針對一個1維的feature,原來是(28,28),只不過拉平了,變成了784。 x = torch.rand(100,16,784) #均勻分布在0到1之間 # print(x) #均勻分布的均值肯定是0.5#這里的參數是給定的channel數量 #這里是有多少個channel,就統計出多少個數值 #這就意味著會生成16個統計信息,這些統計信息記錄了每個channel的均值和方差。 layer = nn.BatchNorm1d(16) print(layer) #做一次forward運行后,會生成均值和方差,為當前x的。 #做完這個后,會自動更新一個全局的running-μ和running-σ2。 #對于只做一次運算的來收,μ和σ2就直接賦值為全局的了。 out = layer(x)# print(out) print(layer.running_mean) print(layer.running_mean.shape)print(layer.running_var) print(layer.running_var.shape)

batch normalize規范化的寫法

下面的步驟就是batch normalize train的過程。
1、第一步統計當前batch的μв均值和方差σ2в,這里還自動更新running-μ和running-σ2。
2、第二步進行normalize的操作。注意在進行normalize的時候分母會加一個很小的值10^-8,這樣做是為了避免除零錯誤。計算完成后再進行一個平移β和縮放γ,使其達到N(均值β,方差γ)的正太分布。在反向傳播(back propagation) 的時候,β和γ需要梯度信息,因此這兩個參數會自動更新。

利用pytorch進行這樣的一個計算:這里是針對2維數據進行操作的。

import torch from torch import nnx = torch.rand(1,16,7,7) # print(x) print(x.shape)layer = nn.BatchNorm2d(16) #這里的參數16一定要和輸入數據的channel匹配起來out = layer(x) #這里進行normalize,將數據范圍很大的數據壓縮到(0,1)的范圍內。 # print(out) print(out.shape)#(γ=1, β=0)是不產生任何影響的 γ = layer.weight print(γ) β = layer.bias print(β)#這里需要注意一下:μ和σ2是沒辦法直接獲取到的,只能查詢總的均值和方差 running_μ = layer.running_mean #總的均值 print(running_μ) running_σ = layer.running_var #總的方差 print(running_σ)print('===========================') #將當前layer的所有參數都打印出來 print(vars(layer)) #這四個參數(running_mean,running_var,weight,bias)都是維度為1,長度為16的 #這些參數中,'affine': True 表示γ(weight)和β(weight)需不需要自動衰減,如果是False,則weight就會自動設置為1,bias自動設置為0,且不自動更新和變換。 #'training' = True 表示當前的模式是train還是test。 #一般情況下affine和training都會設置為True。

Test

這里需要注意的是,和drop out一樣,batch normalization這個layer在training和test情況下行為是不太一樣的。

在training的時候,μ和σ會統計出來,并自動更新一下running,之后再反向傳播(back propagation),更新一下γ和β。

在test的時候,一般我們只會test一個sample,因此μ和σ2是沒法統計的,因為只有一個sample,所以就沒有必要進行統計。因此在test的時候,μ和σ取的不是當前batch的μ和σ2,而在test的時候將全值running-μ和running-σ賦值為μ和σ2。此外在test的時候,是沒有backward的,γ和β是不需要更新的。要實現test,就需要提前切換到test這個模式下,必須調用.eval()這個函數,設置為test模式,再調用BatchNorm1d()進行變換。

可視化Visualization

曲線圖中的虛線是使用batch normalize的,實線的是沒有使用的。
可以發現使用了batch normalize,收斂速度更快,精度也有所提升。我們可以查看每一層參數分布的情況,使用了后,其分布情況是圍繞均值為0附近,總的來說會往0附近來靠近,并不一定是0,方差為1來進行分布,這樣操作后更加利于training。這里需要注意一下,為什么不完完全全在以N(均值=0, 方差=1)這樣進行分布呢?這是因為存在γ和β對其分布的調整,如果γ=1且β=0,那么其分布情況就不會是以N(均值=0, 方差=1)這樣進行分布,其分布就是N(β為均值, γ為方差)這樣的分布。

總結一下使用batch normalize的好處Advantage

? Converge faster
收斂速度更快,在使用sigmoid激活函數的時候,如果把數據限制到0均值單位方差,那么相當于只使用了激活函數中近似線性的部分,這顯然會降低模型表達能力,但是收斂速度更快。

? Better performance
更加容易搜索出最優解

? Robust
? stable
變得更穩定了
? larger learning rate
更大的設置學習率的范圍

五、經典卷積網絡(CNN)

下圖中是最近10年內的網絡變化,其中柱狀圖上面的數字表示錯誤率(準確率=1-錯誤率)。

LeNet-5

在1989年,Yann LeCun提出了一種用反向傳導進行更新的卷積神經網絡,稱為LeNet。
最開始的LeNet-5是用于一個手寫數字的識別,手寫數字的圖片只有28*28,當時的網絡結構并不深,而且每一層的參數量也不大,即使這樣這個網絡結構也是沒有辦法在GPU上跑的,因為當時GPU還沒有,是386的年代。
這個準確度達到了99.2%,就可以很成熟的使用起來了。

所以在當時美國的支票票號識別,郵編識別就已經廣泛使用了。



AlexNet

LeNet-5是80年代出來的,那個時候deep learning還沒有很熱,在2012年,出來了AlexNet,準確率有了很高的提升。當時是使用兩個GTX580(3GB)顯卡來進行該網絡的運算,該網絡是5個卷積層,總共是8個layers,AlexNet的小窗口是11*11的,此外第一次使用ReLU函數、Max pooling、Dropout。

VGG

VGGNet是來自于牛津大學的視覺研究組。VGG一共有6個版本(比如VGG11、VGG16、VGG19)。VGG探索出了,小的kernal窗口不但可以減少計算量,并且沒有損害精度,而且計算速度很快,這也是VGG最核心的價值。探索出了更小型的kernal更好的效果。

這里我們先了解一下1*1卷積核:
1*1 Convolution
可以注意到的是,輸入的channel是32,而到了輸出channel就是16了。這里的輸出的channel取決于kernal的channel。此外1*1的kernal可以保證輸入和輸出的圖片大小是一樣的。

GoogLeNet

這里有不同大小的kernal,此外還有一個合并的操作Filter concatenation,為滿足這個操作的要求,需要確保所有不同kernal輸出來的圖像大小相同,因為有一個33 max pooling,所有輸出來的圖像大小要與max pooling輸出來的相同即可。假設max pooling輸出的是77的圖像,則kernal輸出的也必須是77(為了保證輸出的是77,可以使用stride步長來控制),這樣可以獲得很多個(32,7,7),之后通過concatenation操作后,可以獲得(32,4,7,7)的feature map。

之前使用的都是統一的kernal,而這里為什么要使用不同類型的kernal呢?這是因為每一個不同大小的kernal所感受的視野是不同,11 kernal感受的視野是一個點,33 kernal感受的視野是一個小窗體,通過綜合不同大小的視野(綜合局部和全局的信息),可以感受到一個跟好的信息量。


具體的結構是:

堆疊更多層更好嗎?Stack more layers?

通過堆疊更多層數的時候,層數更多的時候,反而準確度并沒有提升,反而training比較困難,training的error比較高,這告訴我們并不能簡單的堆疊更多的層數(這里需要拋開4層到22層,這范圍內是堆疊更多層數效果會有提升,但是超過20層以上之后,僅是簡單的堆疊的話,training的難度非常大,training找到最優解的難度很大)。

現在我們的網絡結構以及是1000多層了,那如何解決這個問題的呢?也就是ResNet。

六、深度殘差網絡 ResNet

殘差神經網絡(ResNet)是由微軟研究院的何愷明、張祥雨、任少卿、孫劍等人提出的。ResNet 在2015 年的ILSVRC(ImageNet Large Scale Visual Recognition Challenge)中取得了冠軍。

殘差神經網絡的主要貢獻是發現了“退化現象(Degradation)”,并針對退化現象發明了 “快捷連接(Shortcut connection)”,極大的消除了深度過大的神經網絡訓練困難問題。神經網絡的“深度”首次突破了100層、最大的神經網絡甚至超過了1000層。

ResNet構建塊Unit,選擇什么樣的結構合適

這個Unit選擇一個2-3層的卷積層,再加一個short。為了能夠shortcut,就意味著經過shortcut輸出的結果要與輸入時的維度相同且channel也不能衰減,即輸入時256層,則shortcut時也要是256層。

我們來計算一下參數量,輸出256,輸入256,每一個kernal是33,則參數量大概有25625633 = 589,824 大約有600k左右。如果有三個這樣的Convolution的話,還需要再乘以3。

所以為了減少這個參數量,將unit中的kernal的size減小。比如說輸入時是(256,224,224),通過第一個11 kernal時的結果是(64,224,224);再通過第二個33 kernal時,進行padding處理后保持shape不變,依然是(64,224,224);最后通過第三個1*1 kernal后,將輸出結果恢復到原來的(256,224,224)。

輸入時一個(256,224,224),和shortcut的(256,224,224)進行對應元素位置的相加,同樣大小的矩陣相加,是同位置直接相加,不改變其維度,最后結果也是(256,224,224),是沒有改變的。

再來計算一下參數量,2566411 ~ 16k;646433 ~ 36k;642561*1 ~ 16k;這樣總共 Total是大約70k。和原來的600k參數量相比,大概少了9倍。

查看一下34層的ResNet的基本結構:

ResNet的爆炸性的效果

Why call Residual? 為什么叫殘差呢?

F(x)表示unit卷積層學習的內容,原來學習的是H(x),F(x) + x = H(x) 原來學習的情況。
對于unit中的卷積層來說,F(x) = H(x) - x。這個減號就是殘差的由來。

跨時間維度上不同網絡的比較

對于一個ResNet是如何實現的呢?

這里關鍵是如何實現一個Unit基本單元。

這里需要注意的是,我們可以將輸入數據的channel和輸出數據的channel設置成為不同的,我們只需在shortcut路徑上增加一個1*1 kernal 將輸入的channel通過這個kernal的channel進行調整,從而將輸入的channel和輸出channel有相同的數量。在shortcut的路徑上增加一個調整channel的kernal,就可以不用擔心輸入和輸出channel不同的問題了。

ResNet例子:

import torch from torch import nn from torch.nn import functional as F from torch.utils.data import DataLoader from torchvision import datasets from torchvision import transforms from torch import nn, optim# from torchvision.models import resnet18class ResBlk(nn.Module):"""resnet block"""def __init__(self, ch_in, ch_out):""":param ch_in::param ch_out:"""super(ResBlk, self).__init__()self.conv1 = nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=1, padding=1)self.bn1 = nn.BatchNorm2d(ch_out)self.conv2 = nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1)self.bn2 = nn.BatchNorm2d(ch_out)self.extra = nn.Sequential()if ch_out != ch_in:# [b, ch_in, h, w] => [b, ch_out, h, w]self.extra = nn.Sequential(nn.Conv2d(ch_in, ch_out, kernel_size=1, stride=1),nn.BatchNorm2d(ch_out))def forward(self, x):""":param x: [b, ch, h, w]:return:"""out = F.relu(self.bn1(self.conv1(x)))out = self.bn2(self.conv2(out))# short cut.# extra module: [b, ch_in, h, w] => [b, ch_out, h, w]# element-wise add:out = self.extra(x) + outreturn outclass ResNet18(nn.Module):def __init__(self):super(ResNet18, self).__init__()self.conv1 = nn.Sequential(nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1),nn.BatchNorm2d(16))# followed 4 blocks# [b, 64, h, w] => [b, 128, h ,w]self.blk1 = ResBlk(16, 16)# [b, 128, h, w] => [b, 256, h, w]self.blk2 = ResBlk(16, 32)# # [b, 256, h, w] => [b, 512, h, w]# self.blk3 = ResBlk(128, 256)# # [b, 512, h, w] => [b, 1024, h, w]# self.blk4 = ResBlk(256, 512)self.outlayer = nn.Linear(32*32*32, 10)def forward(self, x):""":param x::return:"""x = F.relu(self.conv1(x))# [b, 64, h, w] => [b, 1024, h, w]x = self.blk1(x)x = self.blk2(x)# x = self.blk3(x)# x = self.blk4(x)# print(x.shape)x = x.view(x.size(0), -1)x = self.outlayer(x)return xdef main():batchsz = 32cifar_train = datasets.CIFAR10('cifar', True, transform=transforms.Compose([transforms.Resize((32, 32)),transforms.ToTensor()]), download=True)cifar_train = DataLoader(cifar_train, batch_size=batchsz, shuffle=True)cifar_test = datasets.CIFAR10('cifar', False, transform=transforms.Compose([transforms.Resize((32, 32)),transforms.ToTensor()]), download=True)cifar_test = DataLoader(cifar_test, batch_size=batchsz, shuffle=True)x, label = iter(cifar_train).next()print('x:', x.shape, 'label:', label.shape)device = torch.device('cuda')# model = Lenet5().to(device)model = ResNet18().to(device)criteon = nn.CrossEntropyLoss().to(device)optimizer = optim.Adam(model.parameters(), lr=1e-3)print(model)for epoch in range(1000):model.train()for batchidx, (x, label) in enumerate(cifar_train):# [b, 3, 32, 32]# [b]x, label = x.to(device), label.to(device)logits = model(x)# logits: [b, 10]# label: [b]# loss: tensor scalarloss = criteon(logits, label)# backpropoptimizer.zero_grad()loss.backward()optimizer.step()#print(epoch, 'loss:', loss.item())model.eval()with torch.no_grad():# testtotal_correct = 0total_num = 0for x, label in cifar_test:# [b, 3, 32, 32]# [b]x, label = x.to(device), label.to(device)# [b, 10]logits = model(x)# [b]pred = logits.argmax(dim=1)# [b] vs [b] => scalar tensorcorrect = torch.eq(pred, label).float().sum().item()total_correct += correcttotal_num += x.size(0)# print(correct)acc = total_correct / total_numprint(epoch, 'acc:', acc)if __name__ == '__main__':main()


DenseNet

基于ResNet的短接層思路,可以推廣到,每一個unit都可以和前面所有層都有一個短接,這樣就有了DenseNet。

這里需要注意的是,DenseNet的每一次短接過程,其實是一個concat過程,是將之前的所有信息進行綜合,不是element wise的相加操作,這樣會使得最后的channel越來越大,所以DenseNet對于channel的選擇要設計的很精妙,舍得后面的channel不至于過大。

七、nn.Module(使用nn.Module的好處)

pytorch中使用非常廣泛的類叫nn.Module。
nn.Module類是所有網絡層的父類,當需要實現自己的一個層的時候就必須要繼承這個父類。

Magic

? Every Layer is nn.Module
nn.Module在pytorch中是一個基本的父類,當要實現一個前項計算圖的時候,如果繼承了nn.Module就可以非常方便的使用現成的一些類:
比如說: ? nn.Linear
? nn.BatchNorm2d
? nn.Conv2d

? nn.Module nested in nn.Module
除了使用這些類以外,還可以嵌套nested,每個nn.Module后又可以嵌套nn.Module。

使用nn.Module的好處

1、embed current layers

在nn.Module中啟用了大量現成的神經網絡計算的模塊。使用這些現成的模塊非常方便,只需要調用初始函數,再通過.__call__方法調用forward函數,就可以使用這個模塊提供的功能了。
比如: ? Linear
? ReLU
? Sigmoid
? Conv2d
? ConvTransposed2d
? Dropout
? etc.

2、Container容器

Container就是nn.Sequential()這個類。
nn.Sequetial:按順序包裝多個網絡層(使用容器的時候,容器里面的model屬于容器,而這個容器屬于本網絡)注意哦,這個容器后面括號里都是參數所以要用逗號隔開
sequential是一個時序模型,根據每個submodule傳入的順序寫到計算圖里,在forward的時候也會順序執行。
self.net(x)就可以直接自動完成多次的forward的操作。

3、parameters 權值和偏置參數

使用nn.Module可以很好的對模型參數進行管理,named_parameters()/parameters()方法,這兩個方法都會返回一個用于迭代模型參數的迭代器(named_parameters還包括參數名字)。

比如說下圖中使用Container容器,將兩個線性層容在一起,名為net;通過net.parameters()這個方法可以返回一個生成器,其保存著每層的weight權值和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, # 輸入的神經元個數 # out_features, # 輸出神經元個數 # bias=True # 是否包含偏置 # ) print(net.parameters()) print('=======================') print(list(net.parameters())) print('=======================') print(list(net.parameters())[0]) print(list(net.parameters())[0].shape) #這里為什么是(2,4)呢?因為w是輸出維度在前面,所以是(2,4),是layer0的w。 print('=======================') print(list(net.parameters())[3]) #這里是layer1的bias偏置,所以是(2)print('=======================') #通過.parameters可以很好的返回一個迭代器,返回當前這個net的所有參數。 #因此繼承了nn.Module就不需要額外管理這些參數。 print('=======================')#這個參數有兩種形式, # 一個是帶名字的parameters,返回一個dictionary字典,字典第一個是名字,這個名字是pytorch自動生成的。 #這里我們將字典轉換為了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()方法是返回這個字典中的所有屬性 print('******************************') #一個是不帶名字的parameters。 print(list(net.parameters())[0])#我們可以通過.parameter,將所有參數傳入到優化器中。 #之前我們是通過[w1,b1,w2,b2]這種方式自己管理參數,將這些傳入到optimizer中的。 #現在就直接將這個類的.parameters()傳入其中即可,這樣使用時非常非常方便的。


4. modules 查看子類

? modules: all nodes
對于內部的類可以做到很好的管理。所有的子節點可以稱為modules。

? children: direct children
直接的子節點,我們稱其為children。

使用nn.modules()可以查看所有的子類情況。
其返回的是module其本身0號、其直系兒子1號和其它所有子節點。

import torch from torch import nnclass BasicNet(nn.Module):def __init__(self):super(BasicNet, self).__init__()self.net = nn.Linear(4, 3)def forward(self, x):return self.net(x)class Net(nn.Module):def __init__(self):super(Net, self).__init__()self.net = nn.Sequential(BasicNet(), nn.ReLU(), nn.Linear(3, 2))def forward(self, x):return self.net(x)net = Net() print(list(net.named_parameters())) print('************************') print(list(net.modules()))

5、to(device) 切換GPU或CPU

可以非常方便的將一個類所有的成員函數、所有內部的tensor和所有的操作轉移到GPU或CPU上。

這里需要特別注意,當a是tensor類型時,a.to 返回的是 a_GPU,注意這里的a_GPU和原來的a.to的a不是同一個東西,a.to的a是CPU上的。而對于nn.Module來說,net = net.to。

6、save and load 保存和加載網絡中間狀態

在使用pytorch進行深度學習訓練的時候,經常會有些內容需要保存下來,保存到硬盤張,不管什么時候我們都可以讀取到,那么這個時候,使用torch.save()就可以將內容存儲器來,使用torch.load()就可以將存取的內容讀取出來。
save and load保存和加載 Checkpoint 用于推理/繼續訓練,Checkpoint是網絡的中間狀態。
保存和加載是可以用于在斷電等特殊情況下,不至于失去之前訓練的結果,避免讓其從頭開始從新訓練。

Save操作:

#save: torch.save(net.state_dict(), 'ckpt.mdl')

Load操作:

net.load_state_dict(torch.load('ckpt.mdl'))

7、train / test 切換

train和test的方便切換狀態。如果每個類都繼承與nn.Module的話,直接通過對根節點net.train(),或.eval(),就可以切換了。

8、implement own layer 實現自己的類

比如說在pytorch中沒有一個展平的功能Flatten,因此我們可以自己實現一個繼承與nn.Module的用于展平功能的類。
此外還有一個是reshape操作,這個功能只有方法,沒有一個類,所以很多情況下我們也需要自己創建一個繼承與nn.Module的用于Reshape的類。

class Flatten(nn.Module):def __init__(self):super(Flatten, self).__init__()def forward(self, input):return input.view(input.size(0), -1)


實現一個自己的MyLinear,這個自己創建的類和nn.Linear功能是一樣的,這里可以發現nn.Parameter()會自動將需要梯度設置為true,requires_grad=True,而且參數也會受到nn.parameters()的管理,注意類是大寫的Parameter,方法是小寫的parameters。
我們在forward()函數中寫好我們的邏輯,再return,一定要返回。

class MyLinear(nn.Module):def __init__(self, inp, outp):super(MyLinear, self).__init__()#requires_grad = Trueself.w = nn.Parameter(torch.randn(outp,inp))self.b = nn.Parameter(torch.randn(outp))def forward(self,x):x = x @ self.w.t() + self.breturn x

八、數據增強(Data Argumentation)

Big Data

很大的數據集是train的好壞的前提。有了Big Data神經網絡就能train的很好,有了Big Data這個網絡就不容易over fitting,這個是防止過擬合的關鍵The key to prevent Overfitting。

Limited Data 有限的數據進行優化

? Small network capacity
首先如果數據比較少的話,就要減少網絡的參數量,這樣就不容易overfitting。

? Regularization
如果網絡結構是固定的話,我們就正則化Regularization,迫使網絡的一部分權值w接近于0,這樣也減少了網絡的參數量。

? Data argumentation
對原來的數據進行增強,比如對原來的圖像進行調色,旋轉角度等,獲得新的圖片,將這些圖片和原先的圖片一起用于網絡的訓練。

Data argumentation

? Flip 翻轉
? Rotate 旋轉
? Scale 縮放
? Crop Part 裁剪部分
? Noise 白噪聲
? Random Move 隨機移動
? GAN 生成對抗網絡

Flip翻轉

水平翻轉和豎直翻轉:

.RandomHorizontalFlip()水平翻轉、.RandomVerticalFlip()豎直翻轉,這兩個都是隨機的,有可能做,也有可能不做。

Rotate旋轉


.RandomRotation(15)會在-15°<0<15°會在個區間內隨機的進行選擇;如果我們要固定這個角度,.RandomRotation([0, 90, 180, 270])會隨機的在0°、90°、180°和270°三個角度中選取進行旋轉,如果要不旋轉的那就在加一個0°,這樣會將原來的照片數量變為原來的4被。

Scale縮放


Crop Part裁剪部分

隨機裁剪一部分,裁切的那部分用0來填補。

Noise白噪聲

pytorch沒有提供這個Noise接口,需要人為在Numpy基礎上添加這個。

這里需要總結一下數據增強,由于增強后的照片與原圖片是比較接近的,所有數據增強是有一定的幫助的,但是幫助不會很多。

總結

以上是生活随笔為你收集整理的PyTorch-07 卷积神经网络(什么是卷积、卷积神经网络、池化层、Batch normalization、经典卷积网络、深度残差网络 ResNet、nn.Module、数据增强)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。