Pytorch入門之CNN和五大CNN網路
CNN的介紹我就不寫了,基本的都知道,用於處理影象語音的一種神經網路。在全連線層的基礎上,增加了卷積層和池化層。
關於卷積層和池化層填充係數padding的計算公式,兩者都是都是用一個核(視窗)去處理,卷積核是為了取得影象的資訊,包含了網路的學習引數,池化層是為了突出影象重要資訊和縮小影象規模(分為最大池化和平均池化),不含學習引數,但兩者的計算模式都是一個視窗計算得到1個值,故計算模式是相同的。
需要注意的是,Pytorch中的nn.Conv2d和nn.Maxpool中的padding係數是指填充一邊的值,計算公式如下:
而其他引數值:輸入通道、輸出通道、視窗大小、步長都是超引數,(雖然padding也是)。
CNN中的張量都是四維的
對卷積來說
輸入(N,C,H,W)->卷積核(FN,C,Hk,Wk)->輸出(N,FN,Hp,Wp)
bias:(FN,1,1)
對於卷積的內部細節,如下圖,不細說了
另外補充一點:池化層不該輸入輸出的通道數。
CNN比傳統利用FC處理影象分類的優勢
五大網路
1、LeNet
class LeNet(nn.Module): def __init__(self): super().__init__() self.conv1 = nn.Sequential( nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, stride=1, padding=0), # (1, 32 32)->(6, 28 28) nn.Sigmoid(), # (6, 28 28)->(6 28 28) nn.MaxPool2d(kernel_size=2) # (6, 28 28)->(6 14 14) ) self.conv2 = nn.Sequential( nn.Conv2d(6, 16, 5, 1, 0), # (6, 14 14)->(16 10 10) nn.Sigmoid(), # (6, 10 10)->(16 10 10) nn.MaxPool2d(2) # (16, 10 10)->(16 5 5) ) self.fc = nn.Sequential( nn.Linear(16*5*5, 120), nn.Sigmoid(), nn.Linear(120, 84), nn.Sigmoid(), nn.Linear(84, 10) ) def forward(self, x): x = self.conv1(x) x = self.conv2(x) out = self.fc(x.view(x.shape[0], -1)) return out net = LeNet() print(net)
做CNN要有耐心,資料的擬合要慢慢來。
2、AlexNet
Alexnet是淺層網路與深層網路的分界線。
Alexnet類
class AlexNet(nn.Module): def __init__(self): super().__init__() self.conv1 = nn.Sequential( nn.Conv2d(in_channels=1, out_channels=96, kernel_size=11, stride=4, padding=0), #(1 227 227)->(96 55 55) nn.ReLU(), # (96 55 55)->(96 55 55) nn.MaxPool2d(kernel_size=3, stride=2) # (96 55 55)->(96 27 27) ) self.conv2 = nn.Sequential( nn.Conv2d(96, 256, 5, 1, 2), # (96, 27 27)->(256 27 27) nn.ReLU(), # (256 27 27)->(256 27 27) nn.MaxPool2d(3, 2) # (256 27 27)->(256 13 13) ) self.conv3 = nn.Sequential( nn.Conv2d(256, 384, 3, 1, 1), # (256 13 13)->(384 13 13) nn.ReLU(), # (384 13 13)->(384 13 13) ) self.conv4 = nn.Sequential( nn.Conv2d(384, 384, 3, 1, 1), # (384 13 13)->(384 13 13) nn.ReLU(), # (384 13 13)->(384 13 13) ) self.conv5 = nn.Sequential( nn.Conv2d(384, 256, 3, 1, 1), # (384 13 13)->(256 13 13) nn.ReLU(), # (256 13 13)->(256 13 13) nn.MaxPool2d(3, 2) # (256 13 13)->(256 6 6) ) self.fc = nn.Sequential( nn.Linear(256*6*6, 4096), nn.Dropout(0.2), nn.ReLU(), nn.Linear(4096, 4096), nn.Dropout(0.5), nn.ReLU(), nn.Linear(4096, 10) ) def forward(self, x): x = self.conv1(x) x = self.conv2(x) x = self.conv3(x) x = self.conv4(x) x = self.conv5(x) out = self.fc(x.view(x.shape[0], -1)) return out net = AlexNet() print(net)
3、VGG(主要介紹VGG11)
使用簡單的基礎塊來實現網路
每一個vgg塊都返回一個容器。
class Vgg11(nn.Module): def __init__(self): super().__init__() self.vgg = nn.Sequential() for i, (num_convs, in_channels, out_channels) in enumerate(Vgg11block_size): self.vgg.add_module('vggblock'+str(i), d2l.vgg_block(num_convs, in_channels, out_channels)) self.vgg.add_module('fc', nn.Sequential( d2l.FlattenLayer(), nn.Linear(512*7*7, 4096), nn.Dropout(0.3), nn.ReLU(), nn.Linear(4096, 4096), nn.Dropout(0.5), nn.ReLU(), nn.Linear(4096, 10)) ) def forward(self, x): out = self.vgg(x) return out net = Vgg11() print(net)
4、NiN
NiN是網路中的網路,即每個nin塊都是一個卷積層+全連線層(用1*1卷積層替代)的小網路
也是和VGG一樣,採用塊的思想,使用nin塊
net = nn.Sequential( d2l.nin_block(1, 96, 11, 4, 0), # (1, 224, 224)->(96, 55, 55) nn.MaxPool2d(3, 2), # (96, 55, 55)->(96, 27, 27) d2l.nin_block(96, 256, 5, 1, 2), # (96, 27, 27)->(256, 27, 27) nn.MaxPool2d(3, 2), # (256, 27, 27)->(256, 13, 13) d2l.nin_block(256, 384, 3, 1, 1), # (256, 13, 13)->(384, 13, 13) nn.MaxPool2d(3, 2), # (384, 13, 13)->(384, 6, 6) nn.Dropout(0.5), d2l.nin_block(384, 10, 3, 1, 1), # (384, 6, 6)->(10, 6, 6) d2l.GlobalAvgPool2d(), # (10, 6, 6)->(10, 1, 1) d2l.FlattenLayer() # (10, 1, 1)->(10) ) print(net)
5、Googlenet
採用了NIN中網路串聯網路的思想
但不是nin塊,而是inception塊
採用5個模組
class Googlenet(nn.Module): def __init__(self): super(Googlenet, self).__init__() self.b1 = nn.Sequential( nn.Conv2d(in_channels=1, out_channels=64, kernel_size=7, stride=2, padding=3), # (1,227,227)->(64,114,114) nn.ReLU(), nn.MaxPool2d(2) # (64,114,114)->(64,57,57) ) self.b2 = nn.Sequential( nn.Conv2d(64, 64, kernel_size=1), # (64,114,114)->(64,114,114) nn.ReLU(), nn.Conv2d(64, 64*3, kernel_size=3, padding=1), # (64,114,114)->(192,114,114) nn.ReLU(), nn.MaxPool2d(3, 2, 1) # (192,57,57)->(192,29,29) ) self.b3 = nn.Sequential( d2l.Inception(192, 64, (96, 128), (16, 32), 32), # (192,29,29)->(256,29,29) d2l.Inception(256, 128, (128, 192), (32, 96), 64), # (256,29,29)->(480,29,29) nn.MaxPool2d(3, 2, 1) # (480,29,29)->(480,15,15) ) self.b4 = nn.Sequential( d2l.Inception(480, 192, (96, 208), (16, 48), 64), # (480,15,15)->(512,15,15) d2l.Inception(512, 160, (112, 224), (24, 64), 64), # (512,15,15)->(512,15,15) d2l.Inception(512, 128, (128, 256), (24, 64), 64), # (512,15,15)->(512,15,15) d2l.Inception(512, 112, (144, 288), (32, 64), 64), # (512,15,15)->(528,15,15) d2l.Inception(528, 256, (160, 320), (32, 128), 128), # (528,15,15)->(832,15,15) nn.MaxPool2d(3, 2, 1) # (832,15,15)->(832,8,8) ) self.b5 = nn.Sequential( d2l.Inception(832, 256, (160, 320), (32, 128), 128), # (832,8,8)->(832,8,8) d2l.Inception(832, 384, (192, 384), (48, 128), 128), # (832,8,8)->(1024,8,8) d2l.GlobalAvgPool2d(), # (1024,8,8)->(1024,1,1) d2l.FlattenLayer(), nn.Linear(1024*1*1, 10) ) def forward(self, x): x = self.b1(x) x = self.b2(x) x = self.b3(x) x = self.b4(x) out = self.b5(x) return out net = Googlenet() print(net)
總結:
各個網路的內部細節已經在程式碼中,接下來總結優缺點
Lenet
優點:
交替使用2層卷積層和2層池化層,最後接3層FC來分類。
速度很快
缺點:
使用了sigmoid作為啟用函式,會導致梯度消失
網路深度低
Alexnet
優點:
使用5層卷積層抽取影象特徵,3層池化層,2層FC的複雜網路更大引數空間,比Lenet有更強的分類能力。
使用Relu啟用函式。使用Dropout層。引入影象增廣
使用多層卷積池化層來加深網路
缺點:
速度慢。沒有提供簡單的規則指導如何設計網路
Vggnet
優點:使用簡單的基礎塊來加深網路,塊採用堆積的小卷積核比Alexnet大的卷積核可以有更大的深度,包括網路深度(因為卷積通常會縮小圖片大小,導致網路深度有限,而小的卷積核可以有更大的深度空間去繼續操作)和通道深度。比如3個3*3的堆積核和1個7*7卷積核,在相同感知野情況下,堆積核引數更少,深度更深。
也是卷積層後接FC。
可通過重複使用的塊來構建網路,可自由指定塊每塊中卷積層個數和小卷積和的堆積數(輸出通道數)
缺點:
速度也慢
Ninnet
優點:
通過串聯卷積層和1*1卷積層(效果類似FC)的小網路塊來加深網路。每個塊後接一個池化層。卷積層的引數尺寸(11*11 5*5 3*3)、通道數和Alexnet一樣。
但採用輸出通道數等於標籤類別數的nin塊取代Alexnet的3個FC,然後用全域性平均池化來直接輸出給softmax。顯著減小模型引數,比如塊最後的輸出(batch,10, 6, 6)轉(batch,10*6*6)接FC,則需要10*6*6*10個W、10個b。而NIN的做法做全域性平均池化(10,6,6)->(10,1,1)轉(batch,10),不需要引數。
Googlenet
優點:
吸收了nin對於網路塊串聯網路塊的思想,在一個inception網路塊中使用4路並行,包括1*1卷積層(減少通道數降低模型複雜度)、卷積池化層(提取資訊),最後在通道維推擠合併來擴大網路寬度(通道)。
利用inception網路塊串聯inception網路塊形成1個子網路來加深網路深度,然後子網路相互串聯繼續加深網路