STEP-4:Pytorch-卷积神经网络,LeNet

2020-02-18  本文已影响0人  可恶小林子
感谢伯禹学习平台,本次学习将记录记录如何使用Pytorch高效实现网络,熟练掌握Pytorch的基础知识,记录不包含理论知识的细节展开。

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

填充 (pad)

填充(padding)是指在输入高和宽的两侧填充元素(通常是0元素

pad=1 卷积操作
如果原输入的高和宽是和,卷积核的高和宽是和,在高的两侧一共填充行,在宽的两侧一共填充列,则输出形状为:

我们在卷积神经网络中使用奇数高宽的核,比如,的卷积核,对于高度(或宽度)为大小为的核,令步幅为1,在高(或宽)两侧选择大小为的填充,便可保持输入与输出尺寸相同。
步幅 (stride)

在互相关运算中,卷积核在输入数组上滑动,每次滑动的行数与列数即是步幅(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-1p_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 卷积层实现
# 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))

二:卷积网络中的池化层(pooling)

二维池化层

池化层主要用于缓解卷积层对位置的过度敏感性。同卷积层一样,池化层每次对输入数据的一个固定形状窗口(又称池化窗口)中的元素计算输出,池化层直接计算池化窗口内元素的最大值或者平均值,该运算也分别叫做最大池化或平均池化。图6展示了池化窗口形状为2\times 2的最大池化。

池化窗口形状为 2 x 2 的最大池化

二维平均池化的工作原理与二维最大池化类似,但将最大运算符替换成平均运算符。池化窗口形状为p \times q的池化层称为p \times q池化层,其中的池化运算叫作p \times q池化。

池化层也可以在输入的高和宽两侧填充并调整窗口的移动步幅来改变输出形状。池化层填充和步幅与卷积层填充和步幅的工作机制一样。

在处理多通道输入数据时,池化层对每个输入通道分别池化,但不会像卷积层那样将各通道的结果按通道相加。这意味着池化层的输出通道数与输入通道数相等。

Pytorch中的pooling实现
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

仍旧采用类继承的方式实现这个网络,所有的激活使用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>)
上一篇 下一篇

猜你喜欢

热点阅读