动手学习深度学习 06:卷积神经网络

01 从全连接到卷积

先了解一个例子,如果我们想用MLP解决猫狗分类的问题:

	假设我们有一个足够充分的照片数据集,数据集中是拥有标注的照片,每张照片具有百万级像素,这意味着网络的每次输入都有一百万个维度。 即使将隐藏层维度降低到1000,这个全连接层也将有20^6 * 10^3=10^9个参数,想要训练这个模型将不可实现。

	有些读者可能会反对这个观点,认为要求百万像素的分辨率可能不是必要的。 然而,即使分辨率减小为十万像素,使用1000个隐藏单元的隐藏层也可能不足以学习到良好的图像特征,在真实的系统中我们仍然需要数十亿个参数。 此外,拟合如此多的参数还需要收集大量的数据。

如何解决这个问题呢?

​ 图像中本就拥有丰富的结构,而这些结构可以被人类和机器学习模型使用。 **卷积神经网络(convolutional neural networks,CNN)**是机器学习利用自然图像中一些已知结构的创造性方法。

下面讲述两个重要原则:

  1. 平移不变性(translation invariance):不管检测对象出现在图像中的哪个位置,神经网络的前面几层应该对相同的图像区域具有相似的反应,即为“平移不变性”。
  2. 局部性(locality):神经网络的前面几层应该只探索输入图像中的局部区域,而不过度在意图像中相隔较远区域的关系,这就是“局部性”原则。最终,可以聚合这些局部特征,以在整个图像级别进行预测。

1、从全连接层到卷积层

卷积是特殊的全连接层

在这里插入图片描述

1.1 平移不变性

1、在欧几里得几何中,平移是一种几何变换,表示把一幅图像或一个空间中的每一个点在相同方向移动相同距离。

  • 比如对图像分类任务来说,图像中的目标不管被移动到图片的哪个位置,得到的结果(标签)应该是相同的,这就是卷积神经网络中的平移不变性。

2、平移不变性意味着系统产生完全相同的响应(输出),不管它的输入是如何平移的 。平移同变性(translation equivariance)意味着系统在不同位置的工作原理相同,但它的响应随着目标位置的变化而变化

  • 比如,实例分割任务,就需要平移同变性,目标如果被平移了,那么输出的实例掩码也应该相应地变化。

3、为什么卷积神经网络具有平移不变性

  • 简单地说,卷积+最大池化约等于平移不变性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2T7rXI0c-1664529089161)(assets/image-20220929214238019.png)]

1.2 局部性

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bccu14nv-1664529089162)(assets/image-20220929214437649.png)]

1.3 总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ojUX1qXz-1664529089162)(assets/image-20220929213858460.png)]

2、卷积

2.1 二维交叉相关

首先了解一下“二维交叉相关”的概念,了解一下卷积是怎么工作的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2SHseQdf-1664529089163)(assets/image-20220929215035162.png)]

2.2 二维卷积层

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tMhTzoqR-1664529089163)(assets/image-20220929215418950.png)]

例子,不同的卷积操作可以产生不同的效果,如:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WpZpbWjD-1664529089163)(assets/image-20220929215555381.png)]

2.3 交叉相关 VS 卷积

二者唯一的区别在于卷积多了几个“负号”,但由于对称性,在实际使用中没有区别

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OkZzeN2b-1664529089164)(assets/image-20220929220006082.png)]

2.4 一维和三维

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XVAvNAsM-1664529089164)(assets/image-20220929220059395.png)]

2.5 总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Yu2diav-1664529089164)(assets/image-20220929220228325.png)]

3、总结

  • 图像的平移不变性使我们以相同的方式处理局部图像,而不在乎它的位置。
  • 局部性意味着计算相应的隐藏表示只需一小部分局部图像像素。
  • 在图像处理中,卷积层通常比全连接层需要更少的参数,但依旧获得高效用的模型。
  • 卷积神经网络(CNN)是一类特殊的神经网络,它可以包含多个卷积层。
  • 多个输入和输出通道使模型在每个空间位置可以获取图像的多方面特征。

02 图像卷积

1、互相关运算

严格来说,卷积层是个错误的叫法,因为它所表达的运算其实是互相关运算(cross-correlation),而不是卷积运算。

在卷积层中,输入张量和核张量通过互相关运算产生输出张量。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-psc5MDQ0-1664529089164)(assets/image-20220929222013798.png)]

接下来,我们在corr2d函数中实现如上过程,该函数接受输入张量X和卷积核张量K,并返回输出张量Y

import torch
from torch import nn
from d2l import torch as d2l

def corr2d(X, K):  #@save
    """计算二维互相关运算"""
    h, w = K.shape
    Y = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))
    for i in range(Y.shape[0]):
        for j in range(Y.shape[1]):
            Y[i, j] = (X[i:i + h, j:j + w] * K).sum()
    return Y
X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
K = torch.tensor([[0.0, 1.0], [2.0, 3.0]])
corr2d(X, K)

结果展示:

tensor([[19., 25.],
        [37., 43.]])

2、卷积层

卷积层对输入和卷积核权重进行互相关运算,并在添加标量偏置之后产生输出。 所以,卷积层中的两个被训练的参数是卷积核权重和标量偏置。

就像我们之前随机初始化全连接层一样,在训练基于卷积层的模型时,我们也随机初始化卷积核权重。

基于上面定义的corr2d函数实现二维卷积层。

  • __init__构造函数中,将weightbias声明为两个模型参数。
  • 前向传播函数调用corr2d函数并添加偏置。
class Conv2D(nn.Module):
    def __init__(self, kernel_size):
        super().__init__()
        self.weight = nn.Parameter(torch.rand(kernel_size))
        self.bias = nn.Parameter(torch.zeros(1))

    def forward(self, x):
        return corr2d(x, self.weight) + self.bias

PS:高度和宽度分别为h和w的卷积核可以被称为h×w卷积或h×w卷积核。 我们也将带有h×w卷积核的卷积层称为h×w卷积层。

3、卷积层的简单应用:图像的边缘检测

检测图像中不同颜色的边缘:通过找到像素变化的位置,来检测图像中不同颜色的边缘。

1、首先,我们构造一个6×8像素的黑白图像。中间四列为黑色(0),其余像素为白色(1)。

X = torch.ones((6, 8))
X[:, 2:6] = 0
X
tensor([[1., 1., 0., 0., 0., 0., 1., 1.],
        [1., 1., 0., 0., 0., 0., 1., 1.],
        [1., 1., 0., 0., 0., 0., 1., 1.],
        [1., 1., 0., 0., 0., 0., 1., 1.],
        [1., 1., 0., 0., 0., 0., 1., 1.],
        [1., 1., 0., 0., 0., 0., 1., 1.]])

2、接下来,我们构造一个高度为1、宽度为2的卷积核K

当进行互相关运算时,如果水平相邻的两元素相同,则输出为零,否则输出为非零。

K = torch.tensor([[1.0, -1.0]])

3、现在,我们对参数X(输入)和K(卷积核)执行互相关运算。 如下所示,输出Y中的1代表从白色到黑色的边缘,-1代表从黑色到白色的边缘,其他情况的输出为0。

Y = corr2d(X, K)
Y
tensor([[ 0.,  1.,  0.,  0.,  0., -1.,  0.],
        [ 0.,  1.,  0.,  0.,  0., -1.,  0.],
        [ 0.,  1.,  0.,  0.,  0., -1.,  0.],
        [ 0.,  1.,  0.,  0.,  0., -1.,  0.],
        [ 0.,  1.,  0.,  0.,  0., -1.,  0.],
        [ 0.,  1.,  0.,  0.,  0., -1.,  0.]])

4、现在我们将输入的二维图像转置,再进行如上的互相关运算。

corr2d(X.t(), K)
tensor([[0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0.]])

结论: 其输出如下,之前检测到的垂直边缘消失了。 不出所料,这个卷积核K只可以检测垂直边缘,无法检测水平边缘。

4、学习卷积核

当有了更复杂数值的卷积核,或者连续的卷积层时,我们不可能手动设计滤波器。那么我们是否可以学习由X生成Y的卷积核呢?

现在让我们看看是否可以通过仅查看“输入-输出”对来学习由X生成Y的卷积核。为了简单起见,我们在此使用内置的二维卷积层,并忽略偏置。

# 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核
conv2d = nn.Conv2d(1,1, kernel_size=(1, 2), bias=False)

# 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度),
# 其中批量大小和通道数都为1
X = X.reshape((1, 1, 6, 8))
Y = Y.reshape((1, 1, 6, 7))
lr = 3e-2  # 学习率

for i in range(10):
    Y_hat = conv2d(X)
    l = (Y_hat - Y) ** 2
    conv2d.zero_grad()
    l.sum().backward()
    # 迭代卷积核
    conv2d.weight.data[:] -= lr * conv2d.weight.grad
    if (i + 1) % 2 == 0:
        print(f'epoch {i+1}, loss {l.sum():.3f}')
epoch 2, loss 10.749
epoch 4, loss 1.871
epoch 6, loss 0.341
epoch 8, loss 0.069
epoch 10, loss 0.016

在10次迭代之后,误差已经降到足够低。现在我们来看看我们所学的卷积核的权重张量。

conv2d.weight.data.reshape((1, 2))

输出:

tensor([[ 0.9923, -0.9740]])

细心的你一定会发现,我们学习到的卷积核权重非常接近我们之前定义的卷积核K

5、互相关VS卷积

回想一下我们在 6.1节中观察到的互相关和卷积运算之间的对应关系。 为了得到正式的卷积运算输出,我们需要执行 (6.1.6)中定义的严格卷积运算,而不是互相关运算。 幸运的是,它们差别不大,我们只需水平和垂直翻转二维卷积核张量,然后对输入张量执行互相关运算。

值得注意的是,由于卷积核是从数据中学习到的,因此无论这些层执行严格的卷积运算还是互相关运算,卷积层的输出都不会受到影响。 为了说明这一点,假设卷积层执行互相关运算并学习 图6.2.1中的卷积核,该卷积核在这里由矩阵K表示。 假设其他条件不变,当这个层执行严格的卷积时,学习的卷积核K′在水平和垂直翻转之后将与K相同。 也就是说,当卷积层对 图6.2.1中的输入和K′执行严格卷积运算时,将得到与互相关运算 图6.2.1中相同的输出。

为了与深度学习文献中的标准术语保持一致,我们将继续把“互相关运算”称为卷积运算,尽管严格地说,它们略有不同。 此外,对于卷积核张量上的权重,我们称其为元素

6、特征映射和感受野

如在 6.1.4.1节中所述, 图6.2.1中输出的卷积层有时被称为特征映射(feature map),因为它可以被视为一个输入映射到下一层的空间维度的转换器。 在卷积神经网络中,对于某一层的任意元素x,其感受野(receptive field)是指在前向传播期间可能影响x计算的所有元素(来自所有先前层)。

请注意,感受野可能大于输入的实际大小。让我们用 图6.2.1为例来解释感受野: 给定2×2卷积核,阴影输出元素值19的感受野是输入阴影部分的四个元素。 假设之前输出为Y,其大小为2×2,现在我们在其后附加一个卷积层,该卷积层以Y为输入,输出单个元素z。 在这种情况下,Y上的z的感受野包括Y的所有四个元素,而输入的感受野包括最初所有九个输入元素。 因此,当一个特征图中的任意元素需要检测更广区域的输入特征时,我们可以构建一个更深的网络。

7、小结

  • 二维卷积层的核心计算是二维互相关运算。最简单的形式是,对二维输入数据和卷积核执行互相关操作,然后添加一个偏置。
  • 我们可以设计一个卷积核来检测图像的边缘。
  • 我们可以从数据中学习卷积核的参数。
  • 学习卷积核时,无论用严格卷积运算或互相关运算,卷积层的输出不会受太大影响。
  • 当需要检测输入特征中更广区域时,我们可以构建一个更深的卷积网络。

8、QA

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HDRUskye-1664529089165)(assets/image-20220929224142015.png)]

03 填充与步幅

卷积层中控制输出大小的两个超参数:填充和步幅

1、填充

在应用多层卷积时,我们常常丢失边缘像素。 由于我们通常使用小卷积核,因此对于任何单个卷积,我们可能只会丢失几个像素。 但随着我们应用许多连续卷积层,累积丢失的像素数就多了。 解决这个问题的简单方法即为填充(padding):在输入图像的边界填充元素(通常填充元素是0)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VigLszs0-1664529089165)(assets/image-20220929230951871.png)]

公式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yVrbXDG2-1664529089165)(assets/image-20220929231543815.png)]

Q:卷积神经网络中卷积核的高度和宽度通常为奇数,例如1、3、5或7。

A:选择奇数的好处是:

  • 保持空间维度的同时,我们可以在顶部和底部填充相同数量的行,在左侧和右侧填充相同数量的列。
  • 此外,使用奇数的核大小和填充大小也提供了书写上的便利。对于任何二维张量X,当满足: 1. 卷积核的大小是奇数; 2. 所有边的填充行数和列数相同; 3. 输出与输入具有相同高度和宽度 则可以得出:输出Y[i, j]是通过以输入X[i, j]为中心,与卷积核进行互相关计算得到的。

比如,在下面的例子中,我们创建一个高度和宽度为3的二维卷积层,并在所有侧边填充1个像素。给定高度和宽度为8的输入,则输出的高度和宽度也是8。

import torch
from torch import nn


# 为了方便起见,我们定义了一个计算卷积层的函数。
# 此函数初始化卷积层权重,并对输入和输出提高和缩减相应的维数
def comp_conv2d(conv2d, X):
    # 这里的(1,1)表示批量大小和通道数都是1
    X = X.reshape((1, 1) + X.shape)
    Y = conv2d(X)
    # 省略前两个维度:批量大小和通道
    return Y.reshape(Y.shape[2:])

# 请注意,这里每边都填充了1行或1列,因此总共添加了2行或2列
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1)
X = torch.rand(size=(8, 8))
comp_conv2d(conv2d, X).shape
torch.Size([8, 8])

当卷积核的高度和宽度不同时,我们可以填充不同的高度和宽度,使输出和输入具有相同的高度和宽度。在如下示例中,我们使用高度为5,宽度为3的卷积核,高度和宽度两边的填充分别为2和1。

conv2d = nn.Conv2d(1, 1, kernel_size=(5, 3), padding=(2, 1))
comp_conv2d(conv2d, X).shape
torch.Size([8, 8])

2、步幅

在计算互相关时,卷积窗口从输入张量的左上角开始,向下、向右滑动。 在前面的例子中,我们默认每次滑动一个元素。 但是,有时候为了高效计算或是缩减采样次数,卷积窗口可以跳过中间位置,每次滑动多个元素。

我们将每次滑动元素的数量称为步幅(stride)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z6mWQSj9-1664529089166)(assets/image-20220929231005444.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A5EUor4a-1664529089166)(assets/image-20220929231745047.png)]

下面,我们将高度和宽度的步幅设置为2,从而将输入的高度和宽度减半。

conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2)
comp_conv2d(conv2d, X).shape
torch.Size([4, 4])

接下来,看一个稍微复杂的例子。

conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4))
comp_conv2d(conv2d, X).shape
torch.Size([2, 2])

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l8FetHCC-1664529089167)(assets/image-20220929231833597.png)]

3、总结

  • 填充和幅度是卷积层的超参数
    • 填充和步幅可用于有效地调整数据的维度。
  • 填充:在输入周围添加额外的行/列,来控制输出形状和减少量
    • 填充可以增加输出的高度和宽度。这常用来使输出与输入具有相同的高和宽。
  • 步幅:是每个滑动核窗口时行/列的步长,可以成倍的减少输出形状
    • 步幅可以减小输出的高和宽,例如输出的高和宽仅为输入的高和宽的1/n(n是一个大于1的整数)

04 多输入多输出通道

卷积层另外一个重要的超参数:通道数

1、多输入通道

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aAsmhDiH-1664529089167)(assets/image-20220930150601290.png)]

多输入通道的计算规则:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AdHrzil7-1664529089167)(assets/image-20220930150800575.png)]

为了加深理解,我们实现一下多输入通道互相关运算。 简而言之,我们所做的就是对每个通道执行互相关操作,然后将结果相加。

import torch
from d2l import torch as d2l

def corr2d_multi_in(X, K):
    # 先遍历“X”和“K”的第0个维度(通道维度),再把它们加在一起
    return sum(d2l.corr2d(x, k) for x, k in zip(X, K))

构造与上图中的值相对应的输入张量X和核张量K,以验证互相关运算的输出

X = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],
               [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])

corr2d_multi_in(X, K)

结果展示:

tensor([[ 56.,  72.],
        [104., 120.]])

2、多输出通道

​ 到目前为止,不论有多少输入通道,我们还只有一个输出通道。在最流行的神经网络架构中,随着神经网络层数的加深,我们常会增加输出通道的维数,通过减少空间分辨率以获得更大的通道深度。

​ 直观地说,我们可以将每个通道看作是对不同特征的响应。而现实可能更为复杂一些,因为每个通道不是独立学习的,而是为了共同使用而优化的。因此,多输出通道并不仅是学习多个单通道的检测器。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VmnETPi2-1664529089167)(assets/image-20220930151010930.png)]

PS:输入通道书核输出通道数是随意的,没啥相关性

Q:为什么要采用多个输入和输出通道?

A:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LlTsacVS-1664529089168)(assets/image-20220930152325367.png)]

  • 每个输入通道可以识别特定的模式
  • 输入通道核识别并组会输入的模式

代码实现:

实现一个计算多个通道的输出的互相关函数

def corr2d_multi_in_out(X, K):
    # 迭代“K”的第0个维度,每次都对输入“X”执行互相关运算。
    # 最后将所有结果都叠加在一起
    return torch.stack([corr2d_multi_in(X, k) for k in K], 0)

通过将核张量KK+1K中每个元素加1)和K+2连接起来,构造了一个具有3个输出通道的卷积核。

K = torch.stack((K, K + 1, K + 2), 0)
K.shape
torch.Size([3, 2, 2, 2])

下面,我们对输入张量X与卷积核张量K执行互相关运算。现在的输出包含3个通道,第一个通道的结果与先前输入张量X和多输入单输出通道的结果一致。

corr2d_multi_in_out(X, K)
tensor([[[ 56.,  72.],
         [104., 120.]],

        [[ 76., 100.],
         [148., 172.]],

        [[ 96., 128.],
         [192., 224.]]])

3、1×1 卷积层

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VMYnasK1-1664529089168)(assets/image-20220930155149587.png)]

们使用全连接层实现1×1卷积。 请注意,我们需要对输入和输出的数据形状进行调整。

def corr2d_multi_in_out_1x1(X, K):
    c_i, h, w = X.shape
    c_o = K.shape[0]
    X = X.reshape((c_i, h * w))
    K = K.reshape((c_o, c_i))
    # 全连接层中的矩阵乘法
    Y = torch.matmul(K, X)
    return Y.reshape((c_o, h, w))

当执行1×1卷积运算时,上述函数相当于先前实现的互相关函数corr2d_multi_in_out。让我们用一些样本数据来验证这一点。

X = torch.normal(0, 1, (3, 3, 3))
K = torch.normal(0, 1, (2, 3, 1, 1))

Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
assert float(torch.abs(Y1 - Y2).sum()) < 1e-6

拓展:二维卷积层

下图描述了二维卷积层的输入与输出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hLeLvoLv-1664529089168)(assets/image-20220930152831972.png)]

总结:

  • 输出通道数是卷积层的超参数(输入不是。输入是前一个的超参数)
  • 每个输入通道有独立的二维卷积核,所有通道结果相加得到一个输入通道结果
  • 每个输入通道有独立的三维卷积核
  • 多输入多输出通道可以用来扩展卷积层的模型。
  • 当以每像素为基础应用时,1×1卷积层相当于全连接层。
  • 1×1卷积层通常用于调整网络层的通道数量和控制模型复杂性。

05 池化层(汇聚层)

通常当我们处理图像时,我们希望逐渐降低隐藏表示的空间分辨率、聚集信息,这样随着我们在神经网络中层叠的上升,每个神经元对其敏感的感受野(输入)就越大。

本节将介绍汇聚(pooling)层,它具有双重目的:降低卷积层对位置的敏感性,同时降低对空间降采样表示的敏感性。

1、最大池化层

不同于卷积层中的输入与卷积核之间的互相关计算,汇聚层不包含参数。 相反,池运算是确定性的,我们通常计算汇聚窗口中所有元素的最大值或平均值。这些操作分别称为最大汇聚层(maximum pooling)和平均汇聚层(average pooling)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MLlqG1D1-1664529089168)(assets/image-20220930162901524.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uW022Fwk-1664529089169)(assets/image-20220930163049418.png)]

池化窗口形状为 2×2 的最大池化层。着色部分是第一个输出元素,以及用于计算这个输出的输入元素: max(0,1,3,4)=4.

池化窗口形状为p×q的池化层称为p×q池化层,池化操作称为p×q池化。

2、填充、步幅和多通道

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kmWqK3ia-1664529089169)(assets/image-20220930163118441.png)]

与卷积层一样,池化层也可以改变输出形状。和以前一样,我们可以通过填充和步幅以获得所需的输出形状。

3、平均池化层

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cgPiBFQm-1664529089169)(assets/image-20220930161533374.png)]

总结:

  • 池化层返回窗口中最大或平均值
  • 池化层的主要作用:缓解卷积层对位置的敏感性
  • 同样有窗口大小、填充、和步幅作为超参数

4、代码实现

在下面的代码中的pool2d函数,我们实现池化层的前向传播。 这类似于 6.2节中的corr2d函数。 然而,这里我们没有卷积核,输出为输入中每个区域的最大值或平均值。

import torch
from torch import nn
from d2l import torch as d2l

def pool2d(X, pool_size, mode='max'):
    p_h, p_w = pool_size
    Y = torch.zeros((X.shape[0] - p_h + 1, X.shape[1] - p_w + 1))
    for i in range(Y.shape[0]):
        for j in range(Y.shape[1]):
            if mode == 'max':
                Y[i, j] = X[i: i + p_h, j: j + p_w].max()
            elif mode == 'avg':
                Y[i, j] = X[i: i + p_h, j: j + p_w].mean()
    return Y

我们可以构建 图6.5.1中的输入张量X,验证二维最大汇聚层的输出。

X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
pool2d(X, (2, 2))
tensor([[4., 5.],
        [7., 8.]])

此外,我们还可以验证平均汇聚层。

pool2d(X, (2, 2), 'avg')
tensor([[2., 3.],
        [5., 6.]])

小结:

  • 对于给定输入元素,最大汇聚层会输出该窗口内的最大值,平均汇聚层会输出该窗口内的平均值。
  • 汇聚层的主要优点之一是减轻卷积层对位置的过度敏感。
  • 我们可以指定汇聚层的填充和步幅。
  • 使用最大汇聚层以及大于1的步幅,可减少空间维度(如高度和宽度)。
  • 汇聚层的输出通道数与输入通道数相同。

06 LeNet

链接:6.6. 卷积神经网络(LeNet) — 动手学深度学习 2.0.0-beta1 documentation (d2l.ai)

背景知识:

​ LeNet,它是最早发布的卷积神经网络之一,因其在计算机视觉任务中的高效性能而受到广泛关注。 这个模型是由AT&T贝尔实验室的研究员Yann LeCun在1989年提出的(并以其命名),目的是识别图像 [LeCun et al., 1998]中的手写数字。

LeNet被广泛用于自动取款机(ATM)机中,帮助识别处理支票的数字。MNIST手写数字识别数据集就是当时提出的
在这里插入图片描述

概念补充:

Feature Map(特征图)是输入图像经过神经网络卷积产生的结果,表征的是神经空间内一种特征;其分辨率大小取决于先前卷积核的步长 。

网络结构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Hm3FRjAj-1664529089170)(assets/image-20220930164637090.png)]

通过下面的LeNet代码,你会相信用深度学习框架实现此类模型非常简单。我们只需要实例化一个Sequential块并将需要的层连接在一起。

import torch
from torch import nn
from d2l import torch as d2l

net = nn.Sequential(
    nn.Conv2d(1, 6, kernel_size=5, padding=2), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2),
    nn.Conv2d(6, 16, kernel_size=5), nn.Sigmoid(),
    nn.AvgPool2d(kernel_size=2, stride=2),
    nn.Flatten(),
    nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(),
    nn.Linear(120, 84), nn.Sigmoid(),
    nn.Linear(84, 10))

我们对原始模型做了一点小改动,去掉了最后一层的高斯激活。除此之外,这个网络与最初的LeNet-5一致。

下面,我们将一个大小为28×28的单通道(黑白)图像通过LeNet。通过在每一层打印输出的形状,我们可以检查模型,以确保其操作与我们期望的 图6.6.2一致。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bwSuYpaE-1664529089170)(assets/image-20220930165333892.png)]

X = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32)
for layer in net:
    X = layer(X)
    print(layer.__class__.__name__,'output shape: \t',X.shape)
Conv2d output shape:         torch.Size([1, 6, 28, 28])
Sigmoid output shape:        torch.Size([1, 6, 28, 28])
AvgPool2d output shape:      torch.Size([1, 6, 14, 14])
Conv2d output shape:         torch.Size([1, 16, 10, 10])
Sigmoid output shape:        torch.Size([1, 16, 10, 10])
AvgPool2d output shape:      torch.Size([1, 16, 5, 5])
Flatten output shape:        torch.Size([1, 400])
Linear output shape:         torch.Size([1, 120])
Sigmoid output shape:        torch.Size([1, 120])
Linear output shape:         torch.Size([1, 84])
Sigmoid output shape:        torch.Size([1, 84])
Linear output shape:         torch.Size([1, 10])
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DLNovice

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值