1. 程式人生 > 實用技巧 >Pytorch入門之CNN和五大CNN網路

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個子網路來加深網路深度,然後子網路相互串聯繼續加深網路