1. 程式人生 > >深度學習之PyTorch實戰(2)——神經網路模型搭建和引數優化

深度學習之PyTorch實戰(2)——神經網路模型搭建和引數優化

  上一篇部落格先搭建了基礎環境,並熟悉了基礎知識,本節基於此,再進行深一步的學習。

  接下來看看如何基於PyTorch深度學習框架用簡單快捷的方式搭建出複雜的神經網路模型,同時讓模型引數的優化方法趨於高效。如同使用PyTorch中的自動梯度方法一樣,在搭建複雜的神經網路模型的時候,我們也可以使用PyTorch中已定義的類和方法,這些類和方法覆蓋了神經網路中的線性變換、啟用函式、卷積層、全連線層、池化層等常用神經網路結構的實現。在完成模型的搭建之後,我們還可以使用PyTorch提供的型別豐富的優化函式來完成對模型引數的優化,除此之外,還有很多防止模型在模型訓練過程中發生過擬合的類。

一:PyTorch之torch.nn

  PyTorch中的 torch.nn包提供了很多與實現神經網路中的具體功能相關的類,這些類涵蓋了深度神經網路模型在搭建和引數優化過程中的常用內容,比如神經網路中的卷積層、池化層、全連線層這類層次構造的方法、防止過擬合的引數歸一化方法、Dropout 方法,還有啟用函式部分的線性啟用函式、非線性啟用函式相關的方法,等等。在學會使用PyTorch的 torch.nn進行神經網路模型的搭建和引數優化後,我們就會發現實現一個神經網路應用並沒有我們想象中那麼難。

1.1 匯入包

  下面使用PyTorch的torch.nn包來簡化我們之前的程式碼,開始部分的程式碼變化不大,如下所示:

#_*_coding:utf-8_*_
import torch
from torch.autograd import Variable

# 批量輸入的資料量
batch_n = 100
# 通過隱藏層後輸出的特徵數
hidden_layer = 100
# 輸入資料的特徵個數
input_data = 1000
# 最後輸出的分類結果數
output_data = 10

x = Variable(torch.randn(batch_n , input_data) , requires_grad = False)
y = Variable(torch.randn(batch_n , output_data) , requires_grad = False)

   和之前一樣,這裡首先匯入必要的包、類並定義了4個變數,不過這裡僅定義了輸入和輸出的變數,之前定義神經網路模型中的權重引數的程式碼被刪減了,這和我們之後在程式碼中使用的torch.nn包中的類有關,因為這個類能夠幫助我們自動生成和初始化對應維度的權重引數。

之前的程式碼如下:

#_*_coding:utf-8_*_
import torch
from torch.autograd import Variable

# 批量輸入的資料量
batch_n = 100
# 通過隱藏層後輸出的特徵數
hidden_layer = 100
# 輸入資料的特徵個數
input_data = 1000
# 最後輸出的分類結果數
output_data = 10

x = Variable(torch.randn(batch_n , input_data) , requires_grad = False)
y = Variable(torch.randn(batch_n , output_data) , requires_grad = False)

w1 = Variable(torch.randn(input_data,hidden_layer),requires_grad = True)
w2 = Variable(torch.randn(hidden_layer,output_data),requires_grad = True)

1.2 模型搭建

models = torch.nn.Sequential(
    # 首先通過其完成從輸入層到隱藏層的線性變換
    torch.nn.Linear(input_data,hidden_layer),
    # 經過啟用函式
    torch.nn.ReLU(),
    # 最後完成從隱藏層到輸出層的線性變換
    torch.nn.Linear(hidden_layer,output_data)
)

   torch.nn.Sequential括號內的內容就是我們搭建的神經網路模型的具體結構,這裡首先通過torch.nn.Linear(input_data, hidden_layer)完成從輸入層到隱藏層的線性變換,然後經過啟用函式及torch.nn.Linear(hidden_layer, output_data)完成從隱藏層到輸出層的線性變換。下面分別對在以上程式碼中使用的torch.nn.Sequential、torch.nn.Linear和torch.nn.RelU這三個類進行詳細介紹

1.2.1 torch.nn.Sequential

  torch.nn.Sequential類是torch.nn中的一種序列容器,通過在容器中巢狀各種實現神經網路中具體功能相關的類,來完成對神經網路模型的搭建,最主要的是,引數會按照我們定義好的序列自動傳遞下去。我們可以將巢狀在容器中的各個部分看作各種不同的模組,這些模組可以自由組合。模組的加入一般有兩種方式,一種是在以上程式碼中使用的直接巢狀,另一種是以orderdict有序字典的方式進行傳入,這兩種方式的唯一區別是,使用後者搭建的模型的每個模組都有我們自定義的名字,而前者預設使用從零開始的數字序列作為每個模組的名字。下面通過示例來直觀地看一下使用這兩種方式搭建的模型之間的區別。

  首先,使用直接巢狀搭建的模型程式碼如下:

#_*_coding:utf-8_*_
import torch
from torch.autograd import Variable

# 批量輸入的資料量
batch_n = 100
# 通過隱藏層後輸出的特徵數
hidden_layer = 100
# 輸入資料的特徵個數
input_data = 1000
# 最後輸出的分類結果數
output_data = 10

x = Variable(torch.randn(batch_n , input_data) , requires_grad = False)
y = Variable(torch.randn(batch_n , output_data) , requires_grad = False)

models = torch.nn.Sequential(
    # 首先通過其完成從輸入層到隱藏層的線性變換
    torch.nn.Linear(input_data,hidden_layer),
    # 經過啟用函式
    torch.nn.ReLU(),
    # 最後完成從隱藏層到輸出層的線性變換
    torch.nn.Linear(hidden_layer,output_data)
)
print(models)

   這裡對模型的結構進行列印輸出,結果如下:

Sequential(
  (0): Linear(in_features=1000, out_features=100, bias=True)
  (1): ReLU()
  (2): Linear(in_features=100, out_features=10, bias=True)
)

   使用orderdict有序字典進行傳入來搭建的模型程式碼如下:

#_*_coding:utf-8_*_
import torch
from torch.autograd import Variable
from collections import OrderedDict

# 批量輸入的資料量
batch_n = 100
# 通過隱藏層後輸出的特徵數
hidden_layer = 100
# 輸入資料的特徵個數
input_data = 1000
# 最後輸出的分類結果數
output_data = 10

models = torch.nn.Sequential(OrderedDict([
    ("Linel",torch.nn.Linear(input_data,hidden_layer)),
    ("ReLU1",torch.nn.ReLU()),
    ("Line2",torch.nn.Linear(hidden_layer,output_data))
])
)
print(models)


   這裡對該模型的結構進行列印輸出,結果如下:

Sequential(
  (Linel): Linear(in_features=1000, out_features=100, bias=True)
  (ReLU1): ReLU()
  (Line2): Linear(in_features=100, out_features=10, bias=True)
)

   通過對這兩種方式進行比較,我們會發現,對模組使用自定義的名稱可讓我們更便捷地找到模型中相應的模組並進行操作。

1.2.2 torch.nn.Linear

  torch.nn.Linear類用於定義模型的線性層,即完成前面提到的不同的層之間的線性變換。torch.nn.Linear類接收的引數有三個,分別是輸入特徵數、輸出特徵數和是否使用偏置,設定是否使用偏置的引數是一個布林值,預設為True,即使用偏置。在實際使用的過程中,我們只需將輸入的特徵數和輸出的特徵數傳遞給torch.nn.Linear類,就會自動生成對應維度的權重引數和偏置,對於生成的權重引數和偏置,我們的模型預設使用了一種比之前的簡單隨機方式更好的引數初始化方法。  根據我們搭建模型的輸入、輸出和層次結構需求,它的輸入是在一個批次中包含100個特徵數為1000的資料,最後得到100個特徵數為10的輸出資料,中間需要經過兩次線性變換,所以要使用兩個線性層,兩個線性層的程式碼分別是torch.nn.Linear(input_data,hidden_layer)和torch.nn.Linear(hidden_layer, output_data)。可看到,其代替了之前使用矩陣乘法方式的實現,程式碼更精煉、簡潔。

1.2.3 torch.nn.RelU

   torch.nn.ReLU類屬於非線性啟用分類,在定義時預設不需要傳入引數。當然,在 torch.nn包中還有許多非線性啟用函式類可供選擇,比如之前講到的PReLU、LeakyReLU、Tanh、Sigmoid、Softmax等。

  在掌握torch.nn.Sequential、torch.nn.Linear和torch.nn.RelU的使用方法後,快速搭建更復雜的多層神經網路模型變為可能,而且在整個模型的搭建過程中不需要對在模型中使用到的權重引數和偏置進行任何定義和初始化說明,因為引數已經完成了自動生成。

1.3 優化模型

epoch_n = 10000
learning_rate = 1e-4
loss_fn = torch.nn.MSELoss()

   前兩句程式碼和之前的程式碼沒有多大區別,只是單純地增加了學習速率和訓練次數,學習速率現在是0.0001,訓練次數增加到了10000次,這樣做是為了讓最終得到的結果更好。不過計算損失函式的程式碼發生了改變,現在使用的是在torch.nn包中已經定義好的均方誤差函式類torch.nn.MSELoss來計算損失值,而之前的程式碼是根據損失函式的計算公式來編寫的。

  下面簡單介紹在torch.nn包中常用的損失函式的具體用法,如下所述:

1.3.1 torch.nn.MSELoss

  torch.nn.MSELoss類使用均方誤差函式對損失值進行計算,在定義類的物件時不用傳入任何引數,但在使用例項時需要輸入兩個維度一樣的引數方可進行計算。示例如下

import torch
from torch.autograd import Variable

loss_f = torch.nn.MSELoss()
x = Variable(torch.randn(100,100))
y = Variable(torch.randn(100,100))
loss = loss_f(x,y)
print(loss)

   以上程式碼首先通過隨機方式生成了兩個維度都是(100,100)的引數,然後使用均方誤差函式來計算兩組引數的損失值,列印輸出的結果如下:

tensor(2.0121)

1.3.2 torch.nn.L1Loss

  torch.nn.L1Loss類使用平均絕對誤差函式對損失值進行計算,同樣,在定義類的物件時不用傳入任何引數,但在使用例項時需要輸入兩個維度一樣的引數進行計算。示例如下:

import torch
from torch.autograd import Variable

loss_f = torch.nn.L1Loss()
x = Variable(torch.randn(100,100))
y = Variable(torch.randn(100,100))
loss = loss_f(x,y)
print(loss)

   以上程式碼也是通過隨機方式生成了兩個維度都是(100,100)的引數,然後使用平均絕對誤差函式來計算兩組引數的損失值,列印輸出的結果如下:

tensor(1.1294)

1.3.3 torch.nn.CrossEntropyLoss

 torch.nn.CrossEntropyLoss類用於計算交叉熵,在定義類的物件時不用傳入任何引數,在使用例項時需要輸入兩個滿足交叉熵的計算條件的引數,程式碼如下:

import torch
from torch.autograd import Variable

loss_f = torch.nn.CrossEntropyLoss()
x = Variable(torch.randn(3,5))
y = Variable(torch.LongTensor(3).random_(5))
loss = loss_f(x,y)
print(loss)

   這裡生成的第1組引數是一個隨機引數,維度為(3,5);第2組引數是3個範圍為0~4的隨機數字。計算這兩組引數的損失值,列印輸出的結果如下

tensor(1.6983)

   在學會使用PyTorch中的優化函式之後,我們就可以對自己建立的神經網路模型進行訓練並對引數進行優化了,程式碼如下:

for epoch in range(epoch_n):
    y_pred = models(x)
    loss = loss_fn(y_pred,y)
    if epoch%1000 == 0:
        print("Epoch:{},Loss:{:.4f}".format(epoch,loss.data[0]))
    models.zero_grad()

    loss.backward()

    for param in models.parameters():
        param.data -= param.grad.data*learning_rate

   以上程式碼中的絕大部分和之前訓練和優化部分的程式碼是一樣的,但是引數梯度更新的方式發生了改變。因為使用了不同的模型搭建方法,所以訪問模型中的全部引數是通過對“models.parameters()”進行遍歷完成的,然後才對每個遍歷的引數進行梯度更新。其列印輸入結果的方式是每完成1000次訓練,就列印輸出當前的loss值.

1.4 結果及分析

Epoch:0,Loss:1.0140
Epoch:1000,Loss:0.9409
Epoch:2000,Loss:0.8776
Epoch:3000,Loss:0.8216
Epoch:4000,Loss:0.7716
Epoch:5000,Loss:0.7263
Epoch:6000,Loss:0.6850
Epoch:7000,Loss:0.6468
Epoch:8000,Loss:0.6109
Epoch:9000,Loss:0.5773

Process finished with exit code 0

  從結果可以看出,引數的優化效果比較理想,loss值被控制在相對較小的範圍之內,這和我們增強了訓練次數有很大關係。

完整程式碼如下:

#_*_coding:utf-8_*_
import torch
from torch.autograd import Variable

# 批量輸入的資料量
batch_n = 100
# 通過隱藏層後輸出的特徵數
hidden_layer = 100
# 輸入資料的特徵個數
input_data = 1000
# 最後輸出的分類結果數
output_data = 10

x = Variable(torch.randn(batch_n , input_data) , requires_grad = False)
y = Variable(torch.randn(batch_n , output_data) , requires_grad = False)

models = torch.nn.Sequential(
    # 首先通過其完成從輸入層到隱藏層的線性變換
    torch.nn.Linear(input_data,hidden_layer),
    # 經過啟用函式
    torch.nn.ReLU(),
    # 最後完成從隱藏層到輸出層的線性變換
    torch.nn.Linear(hidden_layer,output_data)
)
# print(models)

epoch_n = 10000
learning_rate = 1e-4
loss_fn = torch.nn.MSELoss()

for epoch in range(epoch_n):
    y_pred = models(x)
    loss = loss_fn(y_pred,y)
    if epoch%1000 == 0:
        print("Epoch:{},Loss:{:.4f}".format(epoch,loss.data[0]))
    models.zero_grad()

    loss.backward()

    for param in models.parameters():
        param.data -= param.grad.data*learning_rate

二:PyTorch之torch.optim

  到目前為止,程式碼中的神經網路權重的引數優化和更新還沒有實現自動化,並且目前使用的優化方法都有固定的學習速率,所以優化函式相對簡單,如果我們自己實現一些高階的引數優化演算法,則優化函式部分的程式碼會變得較為複雜。在PyTorch的torch.optim包中提供了非常多的可實現引數自動優化的類,比如SGD、AdaGrad、RMSProp、Adam等,這些類都可以被直接呼叫,使用起來也非常方便。

2.1 優化模型

我們使用自動化的優化函式實現方法對之前的程式碼進行替換,新的程式碼如下:

#_*_coding:utf-8_*_
import torch
from torch.autograd import Variable

# 批量輸入的資料量
batch_n = 100
# 通過隱藏層後輸出的特徵數
hidden_layer = 100
# 輸入資料的特徵個數
input_data = 1000
# 最後輸出的分類結果數
output_data = 10

x = Variable(torch.randn(batch_n , input_data) , requires_grad = False)
y = Variable(torch.randn(batch_n , output_data) , requires_grad = False)

models = torch.nn.Sequential(
    # 首先通過其完成從輸入層到隱藏層的線性變換
    torch.nn.Linear(input_data,hidden_layer),
    # 經過啟用函式
    torch.nn.ReLU(),
    # 最後完成從隱藏層到輸出層的線性變換
    torch.nn.Linear(hidden_layer,output_data)
)
# print(models)

epoch_n = 20
learning_rate = 1e-4
loss_fn = torch.nn.MSELoss()

optimzer = torch.optim.Adam(models.parameters(),lr = learning_rate)

   這裡使用了torch.optim包中的torch.optim.Adam類作為我們的模型引數的優化函式,在torch.optim.Adam類中輸入的是被優化的引數和學習速率的初始值,如果沒有輸入學習速率的初始值,那麼預設使用0.001這個值。因為我們需要優化的是模型中的全部引數,所以傳遞給torch.optim.Adam類的引數是models.parameters。另外,Adam優化函式還有一個強大的功能,就是可以對梯度更新使用到的學習速率進行自適應調節,所以最後得到的結果自然會比之前的程式碼更理想。

2.2 訓練模型

進行模型訓練的程式碼如下:

#進行模型訓練
for epoch in range(epoch_n):
    y_pred = models(x)
    loss = loss_fn(y_pred,y)
    print("Epoch:{}, Loss:{:.4f}".format(epoch, loss.data[0]))
    optimzer.zero_grad()

    loss.backward()

    #進行梯度更新
    optimzer.step()

 在以上程式碼中有幾處程式碼和之前的訓練程式碼不同,這是因為我們引入了優化演算法,所以通過直接呼叫optimzer.zero_grad來完成對模型引數梯度的歸零;並且在以上程式碼中增加了optimzer.step,它的主要功能是使用計算得到的梯度值對各個節點的引數進行梯度更新。

2.3 列印結果

  這裡只進行20次訓練並列印每輪訓練的loss值,結果如下:

Epoch:0, Loss:1.1289
Epoch:1, Loss:1.1073
Epoch:2, Loss:1.0862
Epoch:3, Loss:1.0656
Epoch:4, Loss:1.0455
Epoch:5, Loss:1.0258
Epoch:6, Loss:1.0065
Epoch:7, Loss:0.9877
Epoch:8, Loss:0.9692
Epoch:9, Loss:0.9513
Epoch:10, Loss:0.9338
Epoch:11, Loss:0.9166
Epoch:12, Loss:0.8997
Epoch:13, Loss:0.8833
Epoch:14, Loss:0.8672
Epoch:15, Loss:0.8514
Epoch:16, Loss:0.8360
Epoch:17, Loss:0.8209
Epoch:18, Loss:0.8061
Epoch:19, Loss:0.7917

Process finished with exit code 0

  在看到這個結果後我們會很驚訝,因為使用torch.optim.Adam類進行引數優化 後僅僅進行了20次訓練,得到的loss值就已經遠遠低於之前進行10000次優化訓練的 結果。所以,如果對torch.optim中的優化演算法類使用得當,就更能幫助我們優化好 模型中的引數。

2.4 完整的程式碼如下:

#_*_coding:utf-8_*_
import torch
from torch.autograd import Variable

# 批量輸入的資料量
batch_n = 100
# 通過隱藏層後輸出的特徵數
hidden_layer = 100
# 輸入資料的特徵個數
input_data = 1000
# 最後輸出的分類結果數
output_data = 10

x = Variable(torch.randn(batch_n , input_data) , requires_grad = False)
y = Variable(torch.randn(batch_n , output_data) , requires_grad = False)

models = torch.nn.Sequential(
    # 首先通過其完成從輸入層到隱藏層的線性變換
    torch.nn.Linear(input_data,hidden_layer),
    # 經過啟用函式
    torch.nn.ReLU(),
    # 最後完成從隱藏層到輸出層的線性變換
    torch.nn.Linear(hidden_layer,output_data)
)
# print(models)

epoch_n = 20
learning_rate = 1e-4
loss_fn = torch.nn.MSELoss()

optimzer = torch.optim.Adam(models.parameters(),lr = learning_rate)

#進行模型訓練
for epoch in range(epoch_n):
    y_pred = models(x)
    loss = loss_fn(y_pred,y)
    print("Epoch:{}, Loss:{:.4f}".format(epoch, loss.data[0]))
    optimzer.zero_grad()

    loss.backward()

    #進行梯度更新
    optimzer.step()

 三:torch和torchvision

  在PyTorch中有兩個核心的包,分別是torch和torchvision。我們之前已經接觸了torch包的一部分內容,比如使用了torch.nn中的線性層加啟用函式配合torch.optim完成了神經網路模型的搭建和模型引數的優化,並使用了 torch.autograd實現自動梯度的功能,接下來會介紹如何使用torch.nn中的類來搭建卷積神經網路。  torchvision包的主要功能是實現資料的處理、匯入和預覽等,所以如果需要對計算機視覺的相關問題進行處理,就可以借用在torchvision包中提供的大量的類來完成相應的工作。下面可以看看都匯入了什麼包

import torch
# torchvision包的主要功能是實現資料的處理,匯入和預覽等
import torchvision
from torchvision import datasets
from torchvision import transforms
from torch.autograd import Variable

3.1 PyTorch中的torch.transforms

  在前面講到過,在torch.transforms中提供了豐富的類對載入的資料進行變換,現在讓我們看看如何進行變換。我們知道,在計算機視覺中處理的資料集有很大一部分是圖片型別的,而在PyTorch中實際進行計算的是Tensor資料型別的變數,所以我們首先需要解決的是資料型別轉換的問題,如果獲取的資料是格式或者大小不一的圖片,則還需要進行歸一化和大小縮放等操作,慶幸的是,這些方法在torch.transforms中都能找到。  在torch.transforms中有大量的資料變換類,其中有很大一部分可以用於實現資料增強(Data Argumentation)。若在我們需要解決的問題上能夠參與到模型訓練中的圖片資料非常有限,則這時就要通過對有限的圖片資料進行各種變換,來生成新的訓練集了,這些變換可以是縮小或者放大圖片的大小、對圖片進行水平或者垂直翻轉等,都是資料增強的方法。不過在手寫數字識別的問題上可以不使用資料增強的方法,因為可用於模型訓練的資料已經足夠了。對資料進行載入及有相應變化的程式碼如下:

transform = transforms.Compose([transforms.ToTensor(),
                                transforms.Normalize(mean=[0.5,0.5,0.5],
                                std=[0.5,0.5,0.5])])

   我們可以將上面程式碼中的torchvision.transforms.Compose類看作是一種容器,它能夠同時對多種資料變換進行組合。傳入的引數是一個列表,列表中的元素就是對載入的資料進行的各種變換操作。

  在以上的程式碼中,在torchvision.transforms.Compose類中只是用了一個型別的轉換變化transfroms.ToTensor和一個數據標準化變換transforms.Normalize。這裡使用的是標準化變換也叫標準差變換法,這種方法需要使用原始資料的均值(Mean)和標準差(Standard Deviation)來進行資料的標準化,在經過標準化變換之後,資料全部符合均值為0,標準差為1的標準正態分佈,計算公式入選:

   不過這裡我們偷了一個懶,均值和標準差的值並非來自原始資料的,而是自行定義了一個,不過仍然能夠達到我們的目的。

  下面看看在torchvision.transforms中常用的資料變換操作。

3.1.1  torchvision.transforms.Resize

  用於對載入的圖片資料按我們需求的大小進行縮放。傳遞給這個類的引數可以是一個整型資料,也可以是一個類似於(h ,w )的序列,其中,h 代表高度,w 代表寬度,但是如果使用的是一個整型資料,那麼表示縮放的寬度和高度都是這個整型資料的值。

3.1.2  torchvision.transforms.Scale

  用於對載入的圖片資料按我們需求的大小進行縮放,用法和torchvision.transforms.Resize類似。

3.1.3  torchvision.transforms.CenterCrop

  用於對載入的圖片以圖片中心為參考點,按我們需要的大小進行裁剪。傳遞給這個類的引數可以是一個整型資料,也可以是一個類似於(h ,w )的序列。

3.1.4  torchvision.transforms.RandomCrop

  用於對載入的圖片按我們需要的大小進行隨機裁剪。傳遞給這個類的引數可以是一個整型資料,也可以是一個類似於(h ,w )的序列。

3.1.5  torchvision.transforms.RandomHorizontalFlip

  用於對載入的圖片按隨機概率進行水平翻轉。我們可以通過傳遞給這個類的引數自定義隨機概率,如果沒有定義,則使用預設的概率值0.5。

3.1.6  torchvision.transforms.RandomVerticalFlip

  用於對載入的圖片按隨機概率進行垂直翻轉。我們可以通過傳遞給這個類的引數自定義隨機概率,如果沒有定義,則使用預設的概率值0.5。

3.1.7  torchvision.transforms.ToTensor

  用於對載入的圖片資料進行型別轉換,將之前構成PIL圖片的資料轉換成Tensor資料型別的變數,讓PyTorch能夠對其進行計算和處理。

3.1.8  torchvision.transforms.ToPILImage

  用於將Tensor變數的資料轉換成PIL圖片資料,主要是為了方便圖片內容的顯示

3.2 PyTorch中的torch.nn

  首先我們看一個卷積神經網路模型搭建的程式碼:

#模型搭建和引數優化
# 在順利完成資料裝載後,我們可以開始編寫卷積神經網路模型的搭建和引數優化的程式碼
#卷積層使用torch.nn.Conv2d類來搭建
# 啟用層使用torch.nn.ReLU 類方法來搭建
# 池化層使用torch.nn.MaxPool2d類方法來搭建
# 全連線層使用 torch.nn.Linear 類方法來搭建

class Model(torch.nn.Module):
    def __init__(self):
        super(Model,self).__init__()
        self.conv1 = torch.nn.Sequential(
            torch.nn.Conv2d(1,64,kernel_size=3,stride=1,padding=1),
            torch.nn.ReLU(),
            torch.nn.Conv2d(64,128,kernel_size=3,stride=1,padding=1),
            torch.nn.ReLU(),
            torch.nn.MaxPool2d(stride=2,kernel_size=2))

        self.dense = torch.nn.Sequential(
            torch.nn.Linear(14*14*128,1024),
            torch.nn.ReLU(),
            torch.nn.Dropout(p = 0.5),
            torch.nn.Linear(1024,10)
        )

    def forward(self, x):
        x = self.conv1(x)
        x = x.view(-1,14*14*128)
        x = self.dense(x)
        return x

   上面我們選擇搭建了一個在結構層次上有所簡化的卷積神經網路模型,在結構上使用了兩個卷積層:一個最大池化層和兩個全連線層,這裡對具體的使用方法進行補充說明。

3.2.1 torch.nn.Conv2d

  用於搭建卷積神經網路的卷積層,主要的輸入引數有輸入通道數、輸出通道數、卷積核大小、卷積核移動步長和Paddingde值。其中,輸入通道數的資料型別是整型,用於確定輸入資料的層數;輸出通道數的資料型別也是整型,用於確定輸出資料的層數;卷積核大小的資料型別是整型,用於確定卷積核的大小;卷積核移動步長的資料型別是整型,用於確定卷積核每次滑動的步長;Paddingde 的資料型別是整型,值為0時表示不進行邊界畫素的填充,如果值大於0,那麼增加數字所對應的邊界畫素層數。

3.2.2  torch.nn.MaxPool2d

  用於實現卷積神經網路中的最大池化層,主要的輸入引數是池化視窗大小、池化視窗移動步長和Padding的值。同樣,池化視窗大小的資料型別是整型,用於確定池化視窗的大小。池化視窗步長的資料型別也是整型,用於確定池化視窗每次移動的步長。Padding的值和在torch.nn.Conv2d中定義的Paddingde值的用法和意義是一樣的。

3.2.3  torch.nn.Dropout

  torch.nn.Dropout類用於防止卷積神經網路在訓練的過程中發生過擬合,其工作原理簡單來說就是在模型訓練的過程中,以一定的隨機概率將卷積神經網路模型的部分引數歸零,以達到減少相鄰兩層神經連線的目的。下圖顯示了 Dropout方法的效果。

  在上圖中的打叉的神經節點就是被隨機抽中並丟棄的神經連線,正是因為選取的方式的隨機性,所以在模型的每輪訓練中選擇丟棄的神經連線也是不同的,這樣做是為了讓我們最後訓練出來的模型對各部分的權重引數不產生過度依賴,從而防止過擬合,對於torch.nn.Dropout類,我們可以對隨機概率值的大小進行設定,如果不足任何設定,我們就使用預設的概率值0.5。

  最後說一下程式碼中前向傳播forward函式中的內容,首先經過self.conv1進行卷積處理,然後進行x.view(-1,14*14*128),對引數實現扁平化,因為之後緊接著的就是全連線層,所以如果不進行扁平化,則全連線層的實際輸出的引數維度和其定義輸入的維度將不匹配,程式將會報錯,最後通過self.dense定義的全連線層進行最後的分類。