STEP-4:Pytorch-卷積神經(jīng)網(wǎng)絡,LeNet

感謝伯禹學習平臺,本次學習將記錄記錄如何使用Pytorch高效實現(xiàn)網(wǎng)絡,熟練掌握Pytorch的基礎知識,記錄不包含理論知識的細節(jié)展開。

一:卷積操作中常用的pad和stride

填充 (pad)

填充(padding)是指在輸入高和寬的兩側填充元素(通常是0元素

pad=1 卷積操作

如果原輸入的高和寬是和,卷積核的高和寬是和,在高的兩側一共填充行,在寬的兩側一共填充列,則輸出形狀為:

我們在卷積神經(jīng)網(wǎng)絡中使用奇數(shù)高寬的核,比如,的卷積核,對于高度(或寬度)為大小為的核,令步幅為1,在高(或寬)兩側選擇大小為的填充,便可保持輸入與輸出尺寸相同。

步幅 (stride)

在互相關運算中,卷積核在輸入數(shù)組上滑動,每次滑動的行數(shù)與列數(shù)即是步幅(stride)。此前我們使用的步幅都是1,圖3展示了在高上步幅為3、在寬上步幅為2的二維互相關運算。

高和寬上步幅分別為3和2的二維互相關運算

一般來說,當高上步幅為s_h,寬上步幅為s_w時,輸出形狀為:

\lfloor(n_h+p_h-k_h+s_h)/s_h\rfloor \times \lfloor(n_w+p_w-k_w+s_w)/s_w\rfloor

如果p_h=k_h-1,p_w=k_w-1,那么輸出形狀將簡化為\lfloor(n_h+s_h-1)/s_h\rfloor \times \lfloor(n_w+s_w-1)/s_w\rfloor。更進一步,如果輸入的高和寬能分別被高和寬上的步幅整除,那么輸出形狀將是(n_h / s_h) \times (n_w/s_w)。

p_h = p_w = p時,我們稱填充為p;當s_h = s_w = s時,我們稱步幅為s。

Pytorch 卷積層實現(xiàn)
# in_channels,out_channels表示輸入輸出的channel維度大小
import torch.nn as nn
conv2d = nn.Conv2d(in_channels=2, out_channels=3, kernel_size=(3, 5), stride=1, padding=(1, 2))

二:卷積網(wǎng)絡中的池化層(pooling)

二維池化層

池化層主要用于緩解卷積層對位置的過度敏感性。同卷積層一樣,池化層每次對輸入數(shù)據(jù)的一個固定形狀窗口(又稱池化窗口)中的元素計算輸出,池化層直接計算池化窗口內元素的最大值或者平均值,該運算也分別叫做最大池化或平均池化。圖6展示了池化窗口形狀為2\times 2的最大池化。

池化窗口形狀為 2 x 2 的最大池化

二維平均池化的工作原理與二維最大池化類似,但將最大運算符替換成平均運算符。池化窗口形狀為p \times q的池化層稱為p \times q池化層,其中的池化運算叫作p \times q池化。

池化層也可以在輸入的高和寬兩側填充并調整窗口的移動步幅來改變輸出形狀。池化層填充和步幅與卷積層填充和步幅的工作機制一樣。

在處理多通道輸入數(shù)據(jù)時,池化層對每個輸入通道分別池化,但不會像卷積層那樣將各通道的結果按通道相加。這意味著池化層的輸出通道數(shù)與輸入通道數(shù)相等。

Pytorch中的pooling實現(xiàn)
import torch.nn as nn
# 最大池化
max_pool2d = nn.MaxPool2d(kernel_size=2, padding=1, stride=(2, 1))
# 平均池化
max_pool2d =nn.AvgPool2d(kernel_size=2, padding=1)
# 目前十分流行的全局平均池化,通常用在分類器前一層
AdaptiveAvg_pool2d = nn.AdaptiveAvgPool2d(1)

三:Pytorch LeNet

模型結構
LeNet

仍舊采用類繼承的方式實現(xiàn)這個網(wǎng)絡,所有的激活使用sigmoid

class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=6, kernel_size=5, padding=2)
        self.pool1 = nn.AvgPool2d(kernel_size=2, stride=2)
        self.conv2 = nn.Conv2d(in_channels=6, out_channels=16, kernel_size=5)
        self.pool2 = nn.AvgPool2d(kernel_size=2, stride=2)
        self.fc1 = nn.Linear(in_features=16 * 5 * 5, out_features=120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)

    def forward(self, x):
        # 輸入為 [b,1,28,28]
        x = x.view(-1, 1, 28, 28)
        # print(x.shape)
        x = self.conv1(x)  # [b,1,28,28]->[b,6,28,28]
        # print(x.shape)
        x = torch.sigmoid(x)
        x = self.pool1(x)  # [b,6,28,28]->[b,6,14,14]
        # print(x.shape)

        x = self.conv2(x)  # [b,6,14,14]->[b,16,10,10]
        # print(x.shape)
        x = torch.sigmoid(x)
        x = self.pool2(x)  # [b,16,10,10]->[b,16,5,5]
        # print(x.shape)

        x = self.fc1(x.view(x.shape[0], -1))  # [b,16*5*5]->[b,120]
        # print(x.shape)
        x = torch.sigmoid(x)
        x = self.fc2(x)  # [b,120]->[b,84]
        # print(x.shape)
        x = torch.sigmoid(x)
        x = self.fc3(x)  # [b,84]->[b,10]
        # print(x.shape)
        return x

測試LeNet

cnn = LeNet()
x= torch.randn(size=(1,28*28), dtype = torch.float32)
print(cnn(x))
# 結果如下
torch.Size([1, 1, 28, 28])
torch.Size([1, 6, 28, 28])
torch.Size([1, 6, 14, 14])
torch.Size([1, 16, 10, 10])
torch.Size([1, 16, 5, 5])
torch.Size([1, 120])
torch.Size([1, 84])
torch.Size([1, 10])
tensor([[-0.3434,  0.0098,  0.0862, -0.2367, -0.2388, -0.6209, -0.3104,  0.1159,
          0.1260, -0.2928]], grad_fn=<AddmmBackward>)
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容