【Pytorch】18. CNN的不同层

卷积层

过滤器的重要性

当你深入学习这门课程时,将发现刚刚学习的不同类型的过滤器非常重要,尤其是当你学习卷积神经网络 (CNN) 时。CNN 是一种深度学习模型,可以学习完成图像分类和对象识别等任务。它们可以使用卷积层跟踪空间信息并学习提取特征,例如对象边缘。下面是一个简单的 CNN 结构示例,由多个层级组成,包括这个“卷积层”。

在这里插入图片描述

卷积层

我们通过向输入图像应用一系列不同的图像过滤器(也称为卷积核),生成了卷积层。

在这里插入图片描述

4 个核 = 4 个过滤的图像。在上面的示例中,4 个不同的过滤器生成了 4 个不同的过滤图像。当我们堆叠这些图像时,就形成了深度为 4 的完整卷积层!

在这里插入图片描述

通常,我们在 PyTorch 中使用 nn.Conv2d 定义卷积层,并指定以下参数:

nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0)

in_channels 是指输入深度。对于灰阶图像来说,深度 = 1
out_channels 是指输出深度,或你希望获得的过滤图像数量
kernel_size 是卷积核的大小(通常为 3,表示 3x3 核)
stridepadding 具有默认值,但是应该根据你希望输出在空间维度 x, y 里具有的大小设置它们的值

代码演示①:卷积可视化

https://github.com/udacity/deep-learning-v2-pytorch.git
请转到 convolutional-neural-networks/conv-visualization/ 文件夹并打开 conv_visualization.ipynb。

可以看到不同的filter过滤后的图片

在这里插入图片描述

代码演示②:卷积神经网络训练CIFAR-10

https://github.com/udacity/deep-learning-v2-pytorch.git
请转到 convolutional-neural-networks/cifar-cnn/ 文件夹并打开 cifar10_cnn_exercise.ipynb。

池化层

池化层的输入通常是卷积层,卷积层是特征图层,每个过滤器对应一个特征图,每个负责发现图像中的一种pattern。但是过多的图层可能导致过拟合,这个时候就需要池化层来帮助来降低维度。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-My5kkdUV-1618221085697)(en-resource://database/16854:0)]

池化层的操作方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Foa3EBRD-1618221085699)(en-resource://database/16856:0)]

这样原本的卷积层就变小了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oj2Akg1u-1618221085700)(en-resource://database/16858:0)]

代码演示:

从 Github 克隆代码库,再打开convolutional-neural-networks > conv-visualization 文件夹里的 notebook conv_visualization.ipynb 和 maxpooling_visualization.ipynb。你可以使用 git clone https://github.com/udacity/deep-learning-v2-pytorch.git

池化的替代方案

注意,池化运算会丢失一些图像信息。这是因为为了获得更小的特征级图像表示,池化会丢弃像素信息。对于图像分类等任务来说,这没关系,但是也会造成一些问题。

以人脸识别为例。在识别人脸时,我们会注意显著特征,例如两只眼睛,一个鼻子,一张嘴。这些特征共同形成了完整的一张脸。经过训练的典型人脸识别 CNN 也应学习识别这些特征。但是将图像压缩成特征级表示法可能会出现奇怪的结果:

  • 假设有一张经过 PS 的人脸图像,脸上有三只眼睛或眼睛上方有一个鼻子,特征级表示法将能够识别这些特征并依然识别出人脸!虽然这张脸是 PS 的,包含不合常规的太多特征。

与池化层相比,有一些分类方法不会丢弃空间信息,而是学习各个部分之间的关系(例如眼睛、鼻子和嘴之间的空间关系)。学习各部分之间空间关系的一种方法是胶囊网络

胶囊网络

胶囊网络能够从图像中检测出对象的各个部分,并表示这些部分之间的空间关系。如果对象(例如人脸)具有不同的形态和典型数量的特征(眼睛、鼻子、嘴),胶囊网络便能够识别同一对象,即使没有在训练数据里见过这些形态。

胶囊网络由父节点和子节点组成,这些节点构成了对象的完整样貌。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2AxlVlFu-1618221085701)(en-resource://database/16860:0)]

在上述示例中,模型能够识别叶节点中的人脸部分(眼睛、鼻子、嘴等),然后在父节点中形成更完整的人脸部分。

什么是胶囊?

胶囊实际上是节点集合,每个节点都包含关于特定部分的信息,例如宽度、方向、颜色等特性。注意,每个胶囊都输出一个向量,该向量由大小和方向组成。

  • 大小 (m) = 某个部分存在的概率;值在 0 到 1 之间。
  • 方向 (theta) = 某个部分的状态。

我们可以对这些输出向量执行数学运算并构建一个解析树,最终识别出由多个小部分组成的完整对象。大小是一个特殊的属性,即使对象朝着不同的方向,大小也应该很大,如下图所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sMwo6Bh1-1618221085702)(en-resource://database/16862:0)]

脸朝不同方向的猫脸孔,数据来源:这篇博文

要在 PyTorch 中尝试实现胶囊网络,请访问此 github 代码库

增加深度

目前我们已经学习了卷积层,池化层,全连接层。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V3z4oxFh-1618221085703)(en-resource://database/16864:0)]

如果我们要处理现实中的杂乱图像,还有一个问题没有讨论,就是如果我们要从网络上搜集各种图片,这些图片很可能有不同的大小,和MLP相同,CNN也要求输入的固定的大小,最常见的是resize到正方形,

另外,当网络深度越来越深,网络也能提取到更复杂的pattern,帮助我们识别图像中的内容,而且也会丢弃掉一些无用的空间信息,例如平滑的背景
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6uVjPepc-1618221085703)(en-resource://database/16866:0)]

在pytorch中设计一个卷积层

数字分别是:输入的channel,期望输出的channel,卷积核的大小(通常式正方形),步长和填充

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ZRfJZXB-1618221085704)(en-resource://database/16868:0)]

填充是指在图像周围添加像素边界。

在 PyTorch 中,你可以指定边界大小。为何需要填充?

在创建卷积层时,我们使用中心像素作为锚点,并在图像上移动一个方形过滤器。这种卷积核无法完美地覆盖图像的边边角角。填充功能使我们能够控制输出的空间大小(最常见的用途是保留输入的空间大小,使输入和输出的宽度及高度一样)。

最常见的填充方法是用 0 像素填充图像(称为零填充),或者使用最近的像素值填充。要详细了解在给定核大小的情况下如何计算填充量,请参阅此页面

按照上面设置卷积层的方法我们可以设计好几层,但是有一个问题就是channel越来越多了,但是每张feature的大小还是不变的,我们希望它越来越小,这个时候就需要池化层了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0qPFHVAy-1618221085705)(en-resource://database/16870:0)]

Pytorch实现

PyTorch 中的卷积层

要在 PyTorch 中创建卷积层,必须首先导入必要的模块:

import torch.nn as nn

然后定义卷积层和模型的前馈行为(输入如何经过网络层级)。首先必须定义一个 Model 类并填写两个函数。

init

你可以通过以下格式在__init__, 函数里定义卷积层:

self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0)

forward

然后在 forward 函数里引用该层级!在此例中,我传入了输入图像x,并向此层的输出应用了 ReLU 函数。

x = F.relu(self.conv1(x))

注意:可以将kernel_sizestride表示为数字或元组。你还可以设置很多其他可调参数,从而更改卷积层的行为。

PyTorch 中的池化层

池化层的参数是核大小和步长。通常和下采样因子的值一样。例如,以下代码将使输入的 x-y 维度下采样到一半大小:

self.pool = nn.MaxPool2d(2,2)

forward

以下代码将池化层应用到了 forward 函数。

x = F.relu(self.conv1(x))
x = self.pool(x)

卷积示例 1

假设我要构建一个 CNN,输入层接受的是 200 x 200 像素(对应于高 200、宽 200、深 1 的三维数组)的灰阶图像。然后,假设下一层是一个卷积层,包含 16 个过滤器,每个过滤器的宽和高都是 2。在进行卷积运算时,我希望过滤器一次跳过 2 个像素。但是我不希望过滤器越过图像边界;换句话说,我不想用 0 填充图像。

要构建此卷积层,我会使用以下代码:

self.conv1 = nn.Conv2d(1, 16, 2, stride=2)

卷积示例 2

假设我希望下一层是一个卷积层,它将在示例 1 中构建的层级作为输入。假设新层级有 32 个过滤器,每个的高和宽是 3。在进行卷积运算时,我希望过滤器一次跳过 1 个像素。为了使此层的宽和高与输入层的一样,我将填充图像。要构建此卷积层,我会使用以下代码:

self.conv2 = nn.Conv2d(16, 32, 3, padding=1)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qkzJTDcv-1618221085707)(en-resource://database/16872:0)]

用 3x3 窗口和步长 1 进行卷积运算图像来源:http://iamaaditya.github.io/2016/03/one-by-one-convolution/

序列模型

我们还可以在__init__函数里使用Sequential封装容器,这样就能在 PyTorch 中创建 CNN 模型。序列模型使我们能够堆叠不同的层级,并在层级之间指定激活函数。

def __init__(self):
        super(ModelName, self).__init__()
        self.features = nn.Sequential(
              nn.Conv2d(1, 16, 2, stride=2),
              nn.MaxPool2d(2, 2),
              nn.ReLU(True),

              nn.Conv2d(16, 32, 3, padding=1),
              nn.MaxPool2d(2, 2),
              nn.ReLU(True) 
         )
         

公式:卷积层中的参数数量

卷积层中的参数数量取决于为filters/out_channelskernel_sizeinput_shape设定的值。

需要定义以下变量:

  • K- 卷积层中的过滤器数量
  • F- 卷积过滤器的高和宽
  • D_in- 上一层的深度

注意K=out_channels,以及F=;kernel_size。同样,D_ininput_shape元组中的最后一个值,通常是 1 或 3(分别表示 RGB 和灰阶图像)。因为每个过滤器有F*F*D_in个权重,并且卷积层由K个过滤器组成,所以卷积层中的权重总数是K*F*F*D_in。由于每个过滤器有一个偏差项,所以卷积层有K个偏差。卷积层的参数数量是K*F*F*D_in + K

扁平化

要完成 CNN 结构,有一个步骤是扁平化一系列卷积层和池化层的最终输出,这样才能作为向量参数输入线性分类层中。在此步骤,你必须知道层级输出的确切参数数量。

本系列笔记来自Udacity课程《Intro to Deep Learning with Pytorch》

全部笔记请关注微信公众号【阿肉爱学习】,在菜单栏点击“利其器”,并选择“pytorch”查看

在这里插入图片描述

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值