PyTorch 入門實戰(四)——利用Torch.nn構建卷積神經網絡

承接上一篇:PyTorch 入門實戰(三)——Dataset和DataLoader


PyTorch入門實戰

1.博客:PyTorch 入門實戰(一)——Tensor

2.博客:PyTorch 入門實戰(二)——Variable

3.博客:PyTorch 入門實戰(三)——Dataset和DataLoader

4.博客:PyTorch 入門實戰(四)——利用Torch.nn構建卷積神經網絡

5.博客:待更新...


目錄

一、概念

二、使用nn.Module創建一個網絡框架

三、利用PyTorch卷積模塊填充網絡框架

四、自己寫一個VGG-16帶有BatchNorm層的網絡

五、總結


一、概念

1.需要聲明的是構建卷積神經網絡需要有一定的面向對象基礎,因爲所有建立的模型結構都是繼承自nn.Module這個基類所完成的

2.我們需要新建一個子類,並且構造函數前向傳播等方法需要被重寫才能實現自己編寫的網絡

3.我們還需要知道torch中卷積層池化層全連接層等部分的編寫方法和拼接方式

二、使用nn.Module創建一個網絡框架

1.聲明一個類,並繼承自nn.Module:

class testNet(nn.Module):

2.定義構造函數,例如我們建立一個用於分類的網絡,類別數是10

class testNet(nn.Module):
    def __init__(self, num_classes=10):

3.初始化方法使用父類的方法即可,super這裏指的就是nn.Module這個基類,第一個參數是自己創建的類名:

class testNet(nn.Module):
    def __init__(self, num_classes=10):
        super(testNet, self).__init__()
        #定義自己的網絡

4.我們還需要定義自己的前向傳播函數,注意forward函數需要有返回值

class testNet(nn.Module):
    def __init__(self, num_classes=10):
        super(testNet, self).__init__()
        #定義自己的網絡

    def forward(self,x):
        #定義自己的前向傳播方式
        return x

5.到目前爲止,我們基本上算是搭了一個具體的框架,但是裏面沒有“肉”,接下來就講解如何填“肉”~

三、利用PyTorch卷積模塊填充網絡框架

1.卷積層nn.Con2d()

常用參數:

  • in_channels:輸入通道數(深度)
  • out_channels:輸出通道數(深度)
  • kernel_size:濾波器(卷積核)大小,寬和高相等的卷積核可以用一個數字表示,例如kernel_size=3;否則用不同數字表示,例如kernel_size=(5,3)
  • stride:表示濾波器滑動的步長
  • padding:是否進行零填充,padding=0表示四周不進行零填充,padding=1表示四周進行1個像素點的零填充
  • bias:默認爲True,表示使用偏置
  • groups:groups=1表示所有輸入輸出是相關聯的;groups=n表示輸入輸出通道數(深度)被分割爲n份,並分別對應,且需要被groups整除
  • (dilation:卷積對輸入的空間間隔,默認爲dilation=1)

舉個例子,構建一個輸入通道爲3,輸出通道爲64,卷積核大小爲3x3,四周進行1個像素點的零填充conv1層:

class testNet(nn.Module):
    def __init__(self, num_classes=10):
        super(testNet, self).__init__()
        #定義自己的網絡
        self.conv1 = nn.Conv2d(3,64,kernel_size=3,padding=1)

    def forward(self,x):
        #定義自己的前向傳播方式
        return x

除此之外,我們還需要在forward函數裏使用conv1構建傳播方法:

class testNet(nn.Module):
    def __init__(self, num_classes=10):
        super(testNet, self).__init__()
        #定義自己的網絡
        self.conv1 = nn.Conv2d(3,64,kernel_size=3,padding=1)

    def forward(self,x):
        #定義自己的前向傳播方式
        out = self.conv1(x)
        return out

這樣一個只含有一個卷積層的網絡就搭建好了。

2.池化層——最大值池化nn.MaxPool2d()均值池化nn.AvgPool2d()

常用參數:

  • kernel_size、stride、padding、dilation在卷積層部分定義和這裏一致
  • return_indices:表示是否返回最大值的下標,默認爲False,即不返回
  • ceil_mode:默認爲False,即不使用方格代替層結構
  • (均值池化函數中)count_include_pad:默認爲True,表示包含零填充

舉個例子,構建一個卷積核大小爲2x2步長爲2pool1層,並且加入到forward中:

class testNet(nn.Module):
    def __init__(self, num_classes=10):
        super(testNet, self).__init__()
        #定義自己的網絡
        self.conv1 = nn.Conv2d(3,64,kernel_size=3,padding=1)
        self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2)

    def forward(self,x):
        #定義自己的前向傳播方式
        out = self.conv1(x)
        out = self.pool1(out)
        return out

事實上,池化層可以不必緊跟在卷積層之後,中間可以加入激活層BatchNorm層,甚至可以在多個卷積操作後添加池化操作

3.加快收斂速度一一批標準化層nn.BatchNorm2d()

常用參數爲:

  • num_features:輸入通道數(深度)
  • eps:爲數值穩定性而添加到分母的值, 默認值爲1e-5
  • momentum:用於running_mean和running_var計算的值;對於累積移動平均值(即簡單平均值),可以設置爲“無”。 默認值爲0.1
  • affine:當設置爲True時,該模塊具有可學習的仿射參數。 默認爲True
  • track_running_stats:當設置爲True時,該模塊跟蹤運行的均值和方差,當設置爲False時,該模塊不跟蹤這樣的統計數據,並且總是在訓練和評估模式。 默認爲True

舉個例子,構建一個輸入通道爲64BN1層,與卷積層輸出通道數64對應,並加入到forward中

class testNet(nn.Module):
    def __init__(self, num_classes=10):
        super(testNet, self).__init__()
        #定義自己的網絡
        self.conv1 = nn.Conv2d(3,64,kernel_size=3,padding=1)
        self.BN1 = nn.BatchNorm2d(64)
        self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2)

    def forward(self,x):
        #定義自己的前向傳播方式
        out = self.conv1(x)
        out = self.BN1(out)
        out = self.pool1(out)
        return out

4.增加網絡的非線性——激活函數nn.ReLU(True)

參數:

  • inplace:可以選擇就地進行操作。 默認值爲False

因此設置爲True的意義就是改變當前的原始對象

ReLU,線性整流函數(Rectified Linear Unit),定義如下:

                                                                                     

                                    

舉個例子,在卷積層(或BN層)之後,池化層之前,添加激活函數並加入到forward中:

class testNet(nn.Module):
    def __init__(self, num_classes=10):
        super(testNet, self).__init__()
        #定義自己的網絡
        self.conv1 = nn.Conv2d(3,64,kernel_size=3,padding=1)
        self.BN1 = nn.BatchNorm2d(64)
        self.relu1 = nn.ReLU(True)
        self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2)

    def forward(self,x):
        #定義自己的前向傳播方式
        out = self.conv1(x)
        out = self.BN1(out)
        out = self.relu1(out)
        out = self.pool1(out)
        return out

5.利用nn.Sequential()按順序構建網絡

(1)利用add_module()函數添加層,第一個參數是層名,第二個參數是實現方法

        layer2 = nn.Sequential()
        layer2.add_module('conv2', nn.Conv2d(64,64,kernel_size=3,padding=1))
        layer2.add_module('BN2',nn.BatchNorm2d(64))
        layer2.add_module('relu2',nn.ReLU(True))
        layer2.add_module('pool2',nn.MaxPool2d(kernel_size=2,stride=2))
        self.layer2 = layer2

可以看到layer2裏有個部分:卷積層conv2批標準化層BN2激活函數relu2最大池化層pool2

不要忘記forward函數裏添加,只不過變得簡單了,其實博主也建議利用Sequential()的方式

class testNet(nn.Module):
    def __init__(self, num_classes=10):
        super(testNet, self).__init__()
        #定義自己的網絡
        self.conv1 = nn.Conv2d(3,64,kernel_size=3,padding=1)
        self.BN1 = nn.BatchNorm2d(64)
        self.relu1 = nn.ReLU(True)
        self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2)

        layer2 = nn.Sequential()
        layer2.add_module('conv2', nn.Conv2d(64,64,kernel_size=3,padding=1))
        layer2.add_module('BN2',nn.BatchNorm2d(64))
        layer2.add_module('relu2',nn.ReLU(True))
        layer2.add_module('pool2',nn.MaxPool2d(kernel_size=2,stride=2))
        self.layer2 = layer2

    def forward(self,x):
        #定義自己的前向傳播方式
        out = self.conv1(x)
        out = self.BN1(out)
        out = self.relu1(out)
        out = self.pool1(out)

        out = self.layer2(out)
        return out

(2)直接在Sequential()裏寫:

        self.layer3 = nn.Sequential(
            nn.Conv2d(64,128,kernel_size=3,padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(True),
            )

forward函數不要忘記加上

class testNet(nn.Module):
    def __init__(self, num_classes=10):
        super(testNet, self).__init__()
        #定義自己的網絡
        self.conv1 = nn.Conv2d(3,64,kernel_size=3,padding=1)
        self.BN1 = nn.BatchNorm2d(64)
        self.relu1 = nn.ReLU(True)
        self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2)

        layer2 = nn.Sequential()
        layer2.add_module('conv2', nn.Conv2d(64,64,kernel_size=3,padding=1))
        layer2.add_module('BN2',nn.BatchNorm2d(64))
        layer2.add_module('relu2',nn.ReLU(True))
        layer2.add_module('pool2',nn.MaxPool2d(kernel_size=2,stride=2))
        self.layer2 = layer2

        self.layer3 = nn.Sequential(
            nn.Conv2d(64,128,kernel_size=3,padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(True),
            )
    def forward(self,x):
        #定義自己的前向傳播方式
        out = self.conv1(x)
        out = self.BN1(out)
        out = self.relu1(out)
        out = self.pool1(out)

        out = self.layer2(out)
        out = self.layer3(out)
        return out

6.線性分類器(全連接層)——線性迴歸函數nn.Linear()

                                                                                      y=wx+b

四個屬性

  • in_features: 上層神經元個數
  • out_features:本層神經元個數
  • weight:權重, 形狀[out_features,in_features]
  • bias:偏置, 形狀[out_features]

三個參數

  • in_features:上層網絡神經元的個數
  • out_features: 該網絡層神經元的個數
  • bias: 網絡層是否有偏置,默認爲True,且維度爲[out_features]

其實Linear是一個類,在linear.py中有定義,裏面的構造函數就告訴了我們一樣的信息:

def __init__(self, in_features, out_features, bias=True):
        super(Linear, self).__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.weight = Parameter(torch.Tensor(out_features, in_features))
        if bias:
            self.bias = Parameter(torch.Tensor(out_features))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

舉個例子,我們設計一個分類器,in_features爲128out_features可以爲我們的類別數num_classes=10

        self.classifier = nn.Sequential(
            nn.Linear(128,num_classes),
            )

注意在forward中一般需要將之前的結果,即多維度的Tensor展平成一維以便分類,可參看博主博客:

torch x = x.view(x.size(0),-1)的理解

代碼爲:

        out = out.view(out.size(0), -1)
        out = self.classifier(out)

最終一個簡單的分類網絡如下:

class testNet(nn.Module):
    def __init__(self, num_classes=10):
        super(testNet, self).__init__()
        #定義自己的網絡
        self.conv1 = nn.Conv2d(3,64,kernel_size=3,padding=1)
        self.BN1 = nn.BatchNorm2d(64)
        self.relu1 = nn.ReLU(True)
        self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2)

        layer2 = nn.Sequential()
        layer2.add_module('conv2', nn.Conv2d(64,64,kernel_size=3,padding=1))
        layer2.add_module('BN2',nn.BatchNorm2d(64))
        layer2.add_module('relu2',nn.ReLU(True))
        layer2.add_module('pool2',nn.MaxPool2d(kernel_size=2,stride=2))
        self.layer2 = layer2

        self.layer3 = nn.Sequential(
            nn.Conv2d(64,128,kernel_size=3,padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(True),
            )
        self.classifier = nn.Sequential(
            nn.Linear(128,num_classes),
            )
    def forward(self,x):
        #定義自己的前向傳播方式
        out = self.conv1(x)
        out = self.BN1(out)
        out = self.relu1(out)
        out = self.pool1(out)

        out = self.layer2(out)
        out = self.layer3(out)

        out = out.view(out.size(0), -1)
        out = self.classifier(out)
        return out

7.防止過擬合——nn.Dropout()

參數:

  • p:元素歸零的概率。 默認值爲0.5
  • inplace:如果設置爲True,將就地執行此操作。 默認值爲False

函數功能的解釋爲:

在訓練期間,使用伯努利分佈的樣本隨機地將輸入張量的一些元素歸零:p使得在每個前向呼叫中隨機化零元素。

事實證明,這是一種有效的正則化技術,可以防止神經元的共同適應,如“通過阻止特徵檢測器的共同適應改善神經網絡”所述。

通俗易懂地,就是使神經元部分失活,即在不同的訓練過程中隨機扔掉一部分神經元。也就是讓某個神經元的激活值以一定的概率p,讓其停止工作,本次訓練過程中不更新權值,也不參與神經網絡的計算,但是它的權重得保留下來

實際上,分類器如果想達到好的效果,可以加上多個激活層和Dropout的組合操作,再得出結果,例如:

        self.classifier = nn.Sequential(
            nn.Linear(128,256),
            nn.ReLU(True),
            nn.Dropout(),

            nn.Linear(256, 256),
            nn.ReLU(True),
            nn.Dropout(),

            nn.Linear(256,num_classes),
            )

最終的網絡爲:

class testNet(nn.Module):
    def __init__(self, num_classes=10):
        super(testNet, self).__init__()
        #定義自己的網絡
        self.conv1 = nn.Conv2d(3,64,kernel_size=3,padding=1)
        self.BN1 = nn.BatchNorm2d(64)
        self.relu1 = nn.ReLU(True)
        self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2)

        layer2 = nn.Sequential()
        layer2.add_module('conv2', nn.Conv2d(64,64,kernel_size=3,padding=1))
        layer2.add_module('BN2',nn.BatchNorm2d(64))
        layer2.add_module('relu2',nn.ReLU(True))
        layer2.add_module('pool2',nn.MaxPool2d(kernel_size=2,stride=2))
        self.layer2 = layer2

        self.layer3 = nn.Sequential(
            nn.Conv2d(64,128,kernel_size=3,padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(True),
            )
        self.classifier = nn.Sequential(
            nn.Linear(128,256),
            nn.ReLU(True),
            nn.Dropout(),

            nn.Linear(256, 256),
            nn.ReLU(True),
            nn.Dropout(),

            nn.Linear(256,num_classes),
            )
    def forward(self,x):
        #定義自己的前向傳播方式
        out = self.conv1(x)
        out = self.BN1(out)
        out = self.relu1(out)
        out = self.pool1(out)

        out = self.layer2(out)
        out = self.layer3(out)

        out = out.view(out.size(0), -1)
        out = self.classifier(out)
        return out

四、自己寫一個VGG-16帶有BatchNorm層的網絡

1.VGG-16(含有BN層)的網絡架構:

VGG16(
  (features): Sequential(
    (0): Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (2): ReLU(inplace)
    (3): Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (4): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (5): ReLU(inplace)
    (6): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (7): Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (8): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (9): ReLU(inplace)
    (10): Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (11): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (12): ReLU(inplace)
    (13): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (14): Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (15): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (16): ReLU(inplace)
    (17): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (18): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (19): ReLU(inplace)
    (20): Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (21): BatchNorm2d(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (22): ReLU(inplace)
    (23): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (24): Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (25): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (26): ReLU(inplace)
    (27): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (28): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (29): ReLU(inplace)
    (30): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (31): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (32): ReLU(inplace)
    (33): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (34): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (35): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (36): ReLU(inplace)
    (37): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (38): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (39): ReLU(inplace)
    (40): Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (41): BatchNorm2d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (42): ReLU(inplace)
    (43): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (44): AvgPool2d(kernel_size=1, stride=1, padding=0)
  )
  (classifier): Sequential(
    (0): Linear(in_features=512, out_features=4096, bias=True)
    (1): ReLU(inplace)
    (2): Dropout(p=0.5)
    (3): Linear(in_features=4096, out_features=4096, bias=True)
    (4): ReLU(inplace)
    (5): Dropout(p=0.5)
    (6): Linear(in_features=4096, out_features=10, bias=True)
  )
)

2.其實這個是print出來的。那麼我們想得到這樣的結構,需要寫出這樣一個網絡結構,博主的代碼如下

import torch.nn as nn

class VGG16(nn.Module):
    def __init__(self, num_classes=10):
        super(VGG16, self).__init__()
        self.features = nn.Sequential(
            #1
            nn.Conv2d(3,64,kernel_size=3,padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(True),
            #2
            nn.Conv2d(64,64,kernel_size=3,padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            #3
            nn.Conv2d(64,128,kernel_size=3,padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(True),
            #4
            nn.Conv2d(128,128,kernel_size=3,padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            #5
            nn.Conv2d(128,256,kernel_size=3,padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(True),
            #6
            nn.Conv2d(256,256,kernel_size=3,padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(True),
            #7
            nn.Conv2d(256,256,kernel_size=3,padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            #8
            nn.Conv2d(256,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            #9
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            #10
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            #11
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            #12
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            #13
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            nn.AvgPool2d(kernel_size=1,stride=1),
            )
        self.classifier = nn.Sequential(
            #14
            nn.Linear(512,4096),
            nn.ReLU(True),
            nn.Dropout(),
            #15
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            #16
            nn.Linear(4096,num_classes),
            )
        #self.classifier = nn.Linear(512, 10)

    def forward(self, x):
        out = self.features(x) 
        out = out.view(out.size(0), -1)
        out = self.classifier(out)
        return out

3.創建VGG16類對象再打印即可:

if __name__ == '__main__':
    import torch
    #使用gpu
    use_cuda = torch.cuda.is_available()
    device = torch.device("cuda" if use_cuda else "cpu")
    
    net = VGG16().to(device)
    print(net)

代碼整體爲:

import torch.nn as nn

class VGG16(nn.Module):
    def __init__(self, num_classes=10):
        super(VGG16, self).__init__()
        self.features = nn.Sequential(
            #1
            nn.Conv2d(3,64,kernel_size=3,padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(True),
            #2
            nn.Conv2d(64,64,kernel_size=3,padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            #3
            nn.Conv2d(64,128,kernel_size=3,padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(True),
            #4
            nn.Conv2d(128,128,kernel_size=3,padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            #5
            nn.Conv2d(128,256,kernel_size=3,padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(True),
            #6
            nn.Conv2d(256,256,kernel_size=3,padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(True),
            #7
            nn.Conv2d(256,256,kernel_size=3,padding=1),
            nn.BatchNorm2d(256),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            #8
            nn.Conv2d(256,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            #9
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            #10
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            #11
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            #12
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            #13
            nn.Conv2d(512,512,kernel_size=3,padding=1),
            nn.BatchNorm2d(512),
            nn.ReLU(True),
            nn.MaxPool2d(kernel_size=2,stride=2),
            nn.AvgPool2d(kernel_size=1,stride=1),
            )
        self.classifier = nn.Sequential(
            #14
            nn.Linear(512,4096),
            nn.ReLU(True),
            nn.Dropout(),
            #15
            nn.Linear(4096, 4096),
            nn.ReLU(True),
            nn.Dropout(),
            #16
            nn.Linear(4096,num_classes),
            )
        #self.classifier = nn.Linear(512, 10)

    def forward(self, x):
        out = self.features(x) 
        out = out.view(out.size(0), -1)
        out = self.classifier(out)
        return out


class testNet(nn.Module):
    def __init__(self, num_classes=10):
        super(testNet, self).__init__()
        #定義自己的網絡
        self.conv1 = nn.Conv2d(3,64,kernel_size=3,padding=1)
        self.BN1 = nn.BatchNorm2d(64)
        self.relu1 = nn.ReLU(True)
        self.pool1 = nn.MaxPool2d(kernel_size=2,stride=2)

        layer2 = nn.Sequential()
        layer2.add_module('conv2', nn.Conv2d(64,64,kernel_size=3,padding=1))
        layer2.add_module('BN2',nn.BatchNorm2d(64))
        layer2.add_module('relu2',nn.ReLU(True))
        layer2.add_module('pool2',nn.MaxPool2d(kernel_size=2,stride=2))
        self.layer2 = layer2

        self.layer3 = nn.Sequential(
            nn.Conv2d(64,128,kernel_size=3,padding=1),
            nn.BatchNorm2d(128),
            nn.ReLU(True),
            )
        self.classifier = nn.Sequential(
            nn.Linear(128,256),
            nn.ReLU(True),
            nn.Dropout(),

            nn.Linear(256, 256),
            nn.ReLU(True),
            nn.Dropout(),

            nn.Linear(256,num_classes),
            )
    def forward(self,x):
        #定義自己的前向傳播方式
        out = self.conv1(x)
        out = self.BN1(out)
        out = self.relu1(out)
        out = self.pool1(out)

        out = self.layer2(out)
        out = self.layer3(out)

        out = out.view(out.size(0), -1)
        out = self.classifier(out)
        return out

if __name__ == '__main__':
    import torch
    #使用gpu
    use_cuda = torch.cuda.is_available()
    device = torch.device("cuda" if use_cuda else "cpu")
    
    net = VGG16().to(device)
    print(net)

結果爲:

                  

五、總結

1.nn.Module是一個基類,需要派生一個子類構造自己的網絡,需要改寫的方法有__init__forward等,nn.Sequential()函數按照定義順序構建網絡

2.nn中各種模塊的使用需要注意輸入輸出的銜接以及順序

3.forward函數中的層層之間輸入輸出的大小也要注意匹配

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章