深度之眼 PyTorch 训练营第 4 期 (10):CNN

15 篇文章 1 订阅
15 篇文章 3 订阅


本文尽量不涉及 CNN(卷积神经网络)的原理,仅讨论 CNN 的 PyTorch 实现。CNN 独有的层包括卷积层(convolution layer),池化层(pooling layer),转置卷积层(transposed convolution layer),反池化层(unpooling layer)。卷积层与池化层在 CNN 中最常用,而转置卷积层与反池化层通常用于计算机视觉应用里的图像再生,对于 NLP 来说应用不多,不再赘述。

1. 卷积神经网络工作原理

从工程实现的角度来说,一个 CNN 网络可以分成两部分:特征学习阶段与分类阶段。
A Comprehensive Guide to Convolutional Neural Networks — the ELI5 way
特征学习层由多层卷积层与池化层叠加,之间使用 relu 作为激活函数。卷积层的作用是使信息变深(层数增加),通常会使层的长宽减小;池化层的作用是使信息变窄,提取主要信息。之后进入分类层,将信息变成一维向量,经过 1-3 层全连接层与 relu 之后,经过最终的 softmax 层进行分类;若目标为二分类,则也可以经过 sigmoid 层。

2. convolution layer 卷积层

卷积层有三个类,分别是:

  • torch.nn.Conv1d
  • torch.nn.Conv2d
  • torch.nn.Conv3d
    这三个类分别对应了文本(一维数据)、图片(二维数据)和视频(三维数据)。它们的维度如下:
  • 一维数据是一个 3 维张量:batch * channel * feature;
  • 二维数据是一个 4 维张量:batch * channel * weight * height;
  • 三维数据是一个 5 维张量:batch * channel * frame * weight * height。

可见,三个类处理的数据的前两维是完全一致的。此外,三个类的参数也完全一致,以 torch.nn.Conv2d 为例:

torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')
  • in_channels:输入张量的层数;
  • out_channels:输出张量的层数;
  • kernel_size:卷积核的大小,整数或元组;
  • stride:卷积的步长,整数或元组;
  • padding:填充的宽度,整数或元组;
  • dilation:稀释的跨度,整数或元组;
  • groups:卷积的分组;
  • bias:偏置项;
  • padding_mode:填充的方法。

当所有尺寸均为矩形的时候,输出张量的长和宽的数值为:
d i m e n s i o n = H i n + 2 × p a d d i n g − d i l u t i o n × ( k e r n e l _ s i z e − 1 ) − 1 s t r i d e dimension=\frac{H_{in}+2\times padding-dilution\times (kernel\_size-1)-1}{stride} dimension=strideHin+2×paddingdilution×(kernel_size1)1

  • 一个 trick:当 k e r n e l _ s i z e = 3 kernel\_size=3 kernel_size=3 p a d d i n g = 1 padding=1 padding=1 s t r i d e = 1 stride=1 stride=1 的时候,输入张量和输出张量的长宽是不变的。

池化层的权重是随机初始化的,不过我们也可以手动设定。

>>> conv = torch.nn.Conv2d(1, 1, 3, bias=0.) # 定义一个 3x3 的卷积核
>>> nn.init.constant_(conv.weight.data, 1.) # 卷积核的权重设为 1.
>>> print(Convolutional.weight.data)
tensor([[[[1., 1., 1.],
          [1., 1., 1.],
          [1., 1., 1.]]]])
>>> tensor = torch.linspace(16., 1., 16).reshape(1, 1, 4, 4) # 定义一个张量
>>> print(tensor)
tensor([[[[16., 15., 14., 13.],
          [12., 11., 10.,  9.],
          [ 8.,  7.,  6.,  5.],
          [ 4.,  3.,  2.,  1.]]]])
>>> conv(tensor) # 卷积操作
tensor([[[[99., 90.],
          [63., 54.]]]], grad_fn=<MkldnnConvolutionBackward>)

上例中,卷积核是一个 3 × 3 3\times3 3×3 的全 1 张量;在卷积运算中,卷积核先与张量中前三排中的前三个元素进行 elementwise 的乘法,然后相加,得到输出张量中的第一个元素。然后向右滑动一个元素(因为 stride 默认是 1),重复卷积运算;既然达到末尾,返回左侧向下滑动一个单位,继续运算,直到到达末尾。

3. pool layer 池化层

与卷积层对应的,池化层分为最大池化和平均池化两种,每种也有三个类:

  • torch.nn.MaxPool1d
  • torch.nn.MaxPool2d
  • torch.nn.MaxPool3d
  • torch.nn.AvgPool1d
  • torch.nn.AvgPool2d
  • torch.nn.AvgPool3d

所谓“池化”,就是按照一定的规则(选取最大值或计算平均值)在输入层的窗口里计算数据,返回计算结果。它们的参数也一致,最大池化层只有三个参数:

  • kernel_size:卷积核的大小,整数或元组;
  • stride:卷积的步长,整数或元组;
  • padding:填充的宽度,整数或元组;

一维平均池化层有额外的两个参数:

  • ceil_mode:对结果进行上取整;
  • count_include_pad:是否将 padding 纳入计算;

二维及三维平均池化层有额外的一个参数:

  • divisor_override:指定一个除数。

  • 一个 trick:当 kernel_size = 2 \text{kernel\_size}=2 kernel_size=2 stride = 2 \text{stride}=2 stride=2 的时候,输出张量的尺寸是输入张量的一半。

>>> pool = torch.nn.MaxPool2d(2) # 定义一个大小为 2x2 的核
>>> pool(tensor) # 池化操作
tensor([[[[16., 14.],
          [ 8.,  6.]]]])

4. CNN 实战

我们还是使用《[DL] PyTotch 实战 1》 里的任务,只不过这一次我们使用 CNN 搭建神经网络。除了第 2、5 步,其它代码都是一样的,所以这里只有这两步的代码,其它代码请看前文。

构建神经网络时唯一要注意的是最后的全连接层的入度。

class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv = nn.Conv2d(1, 4, 3, 1, 1) # 维度不变
        self.pool = nn.MaxPool2d(2, 2) # 维度减半
        self.fc = nn.Linear(28*28, 10)
        self.softmax = nn.LogSoftmax(dim=1)

    def forward(self, x):
        x = F.relu(self.conv(x))
        x = F.relu(self.pool(x))
        x = self.fc(x.view(x.shape[0], -1))
        out = self.softmax(x)
        return out

这个模型里,每一个 batch 经过卷积层以前的维度是 [batch, 1, 28, 28],经过卷积层后长宽不变而通道数变成了 4;通过池化层以后每个 batch 的维度变成了 [batch, 4, 14, 14],所以全连接层的入度不变。还有一点要注意的是因为 CNN 接受一个二维张量。所以打平这个操作要放在模型里面的全连接层之前,而不是训练中。训练 15 个 epoch:

Epoch 0 - Training loss: 0.5372020660528242
Epoch 1 - Training loss: 0.25464658567836795
Epoch 2 - Training loss: 0.19804853362156383
Epoch 3 - Training loss: 0.1687760797144571
Epoch 4 - Training loss: 0.15073536825316675
Epoch 5 - Training loss: 0.13678724837126033
Epoch 6 - Training loss: 0.1266822514833132
Epoch 7 - Training loss: 0.11664468624781985
Epoch 8 - Training loss: 0.10935285677617071
Epoch 9 - Training loss: 0.1023956656144229
Epoch 10 - Training loss: 0.09896873006684535
Epoch 11 - Training loss: 0.09299984435115986
Epoch 12 - Training loss: 0.08871795376762748
Epoch 13 - Training loss: 0.08644302016886662
Epoch 14 - Training loss: 0.08259313310315805

上一次使用全连接神经网络训练 15 轮后的 loss 是 0.27,看来 CNN 网络的效果好很多。测试一下:

>>> correct_count, all_count = 0, 0

>>> with torch.no_grad():
...     for data in valloader:
...         images, labels = data
...         outputs = cnn(images)
...         _, predicted = torch.max(outputs.data, 1)
...         all_count += labels.size(0)
...         correct_count += (predicted == labels).sum().item()

... print("Number Of Images Tested =", all_count)
... print("\nModel Accuracy =", (correct_count/all_count))
Number Of Images Tested = 10000

Model Accuracy = 0.9705

准确率果然超过了 97%。CNN YES!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值