0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

PyTorch教程-7.3. 填充和步幅

jf_pJlTbmA9 来源:PyTorch 作者:PyTorch 2023-06-05 15:44 次阅读

回忆一下图 7.2.1中的卷积示例。输入的高度和宽度均为 3,卷积核的高度和宽度均为 2,从而产生具有维度的输出表示2×2. 假设输入形状是 nh×nw卷积核形状为 kh×kw,输出形状将是 (nh−kh+1)×(nw−kw+1):我们只能将卷积核移动到它用完像素以应用卷积为止。

在下文中,我们将探索许多技术,包括填充和跨步卷积,它们可以更好地控制输出的大小。作为动机,请注意,由于内核的宽度和高度通常大于1,在应用许多连续的卷积之后,我们往往会得到比输入小得多的输出。如果我们从一个240×240像素图像,10层层5×5卷积将图像缩小为200×200像素,切片30%的图像,并用它抹掉原始图像边界上的任何有趣信息。填充是处理此问题的最流行的工具。在其他情况下,我们可能希望大幅降低维数,例如,如果我们发现原始输入分辨率很笨重。跨步卷积是一种流行的技术,可以在这些情况下提供帮助。

import torch
from torch import nn

from mxnet import np, npx
from mxnet.gluon import nn

npx.set_np()

import jax
from flax import linen as nn
from jax import numpy as jnp
from d2l import jax as d2l

No GPU/TPU found, falling back to CPU. (Set TF_CPP_MIN_LOG_LEVEL=0 and rerun for more info.)

import tensorflow as tf

7.3.1. 填充

如上所述,应用卷积层时的一个棘手问题是我们往往会丢失图像周边的像素。考虑 图 7.3.1,该图将像素利用率描述为卷积核大小和图像内位置的函数。角落里的像素几乎没有被使用。

pYYBAGR9NUyAe85CAAEEPZIO2Q0993.svg

图 7.3.1尺寸卷积的像素利用1×1, 2×2, 和3×3分别。

由于我们通常使用小内核,对于任何给定的卷积,我们可能只会丢失几个像素,但是当我们应用许多连续的卷积层时,这可能会累加起来。这个问题的一个直接解决方案是在输入图像的边界周围添加额外的填充像素,从而增加图像的有效尺寸。通常,我们将额外像素的值设置为零。在 图 7.3.2中,我们填充一个3×3输入,将其大小增加到5×5. 相应的输出然后增加到4×4矩阵。阴影部分是第一个输出元素以及用于输出计算的输入和内核张量元素:0×0+0×1+0×2+0×3=0.

poYBAGR9NU-AUDCLAAGdaRvKYPc237.svg

图 7.3.2带填充的二维互相关。

一般来说,如果我们总共添加ph填充行(大约一半在顶部,一半在底部)和总共pw填充列(大约一半在左边,一半在右边),输出形状将是

(7.3.1)(nh−kh+ph+1)×(nw−kw+pw+1).

这意味着输出的高度和宽度将增加 ph和pw, 分别。

在许多情况下,我们会想要设置ph=kh−1和 pw=kw−1给输入和输出相同的高度和宽度。这样在构建网络时更容易预测每一层的输出形状。假如说kh这里很奇怪,我们会垫ph/2高度两侧的行。如果 kh是偶数,一种可能是填充 ⌈ph/2⌉输入顶部的行和 ⌊ph/2⌋底部的行。我们将以相同的方式填充宽度的两侧。

CNN 通常使用具有奇数高度和宽度值的卷积核,例如 1、3、5 或 7。选择奇数核大小的好处是我们可以保留维度,同时在顶部和底部填充相同数量的行,并且左右的列数相同。

此外,这种使用奇数内核和填充来精确保持维度的做法提供了文书上的好处。对于任意一个二维张量X,当核的大小为奇数,且各边的padding行数和列数相同时,产生与输入等高等宽的输出,我们知道输出是通过cross计算的-输入和卷积核与以 为中心的窗口的相关性。Y[i, j]X[i, j]

在下面的示例中,我们创建了一个二维卷积层,其高度和宽度均为 3,并在所有边上应用 1 个像素的填充。给定一个高度和宽度为 8 的输入,我们发现输出的高度和宽度也为 8。

# We define a helper function to calculate convolutions. It initializes the
# convolutional layer weights and performs corresponding dimensionality
# elevations and reductions on the input and output
def comp_conv2d(conv2d, X):
  # (1, 1) indicates that batch size and the number of channels are both 1
  X = X.reshape((1, 1) + X.shape)
  Y = conv2d(X)
  # Strip the first two dimensions: examples and channels
  return Y.reshape(Y.shape[2:])

# 1 row and column is padded on either side, so a total of 2 rows or columns
# are added
conv2d = nn.LazyConv2d(1, kernel_size=3, padding=1)
X = torch.rand(size=(8, 8))
comp_conv2d(conv2d, X).shape

torch.Size([8, 8])

# We define a helper function to calculate convolutions. It initializes
# the convolutional layer weights and performs corresponding dimensionality
# elevations and reductions on the input and output
def comp_conv2d(conv2d, X):
  conv2d.initialize()
  # (1, 1) indicates that batch size and the number of channels are both 1
  X = X.reshape((1, 1) + X.shape)
  Y = conv2d(X)
  # Strip the first two dimensions: examples and channels
  return Y.reshape(Y.shape[2:])

# 1 row and column is padded on either side, so a total of 2 rows or columns are added
conv2d = nn.Conv2D(1, kernel_size=3, padding=1)
X = np.random.uniform(size=(8, 8))
comp_conv2d(conv2d, X).shape

(8, 8)

# We define a helper function to calculate convolutions. It initializes
# the convolutional layer weights and performs corresponding dimensionality
# elevations and reductions on the input and output
def comp_conv2d(conv2d, X):
  # (1, X.shape, 1) indicates that batch size and the number of channels are both 1
  key = jax.random.PRNGKey(d2l.get_seed())
  X = X.reshape((1,) + X.shape + (1,))
  Y, _ = conv2d.init_with_output(key, X)
  # Strip the dimensions: examples and channels
  return Y.reshape(Y.shape[1:3])
# 1 row and column is padded on either side, so a total of 2 rows or columns are added
conv2d = nn.Conv(1, kernel_size=(3, 3), padding='SAME')
X = jax.random.uniform(jax.random.PRNGKey(d2l.get_seed()), shape=(8, 8))
comp_conv2d(conv2d, X).shape

(8, 8)

# We define a helper function to calculate convolutions. It initializes
# the convolutional layer weights and performs corresponding dimensionality
# elevations and reductions on the input and output
def comp_conv2d(conv2d, X):
  # (1, 1) indicates that batch size and the number of channels are both 1
  X = tf.reshape(X, (1, ) + X.shape + (1, ))
  Y = conv2d(X)
  # Strip the first two dimensions: examples and channels
  return tf.reshape(Y, Y.shape[1:3])
# 1 row and column is padded on either side, so a total of 2 rows or columns
# are added
conv2d = tf.keras.layers.Conv2D(1, kernel_size=3, padding='same')
X = tf.random.uniform(shape=(8, 8))
comp_conv2d(conv2d, X).shape

TensorShape([8, 8])

当卷积核的高和宽不同时,我们可以通过为高和宽设置不同的填充数,使输出和输入具有相同的高和宽。

# We use a convolution kernel with height 5 and width 3. The padding on either
# side of the height and width are 2 and 1, respectively
conv2d = nn.LazyConv2d(1, kernel_size=(5, 3), padding=(2, 1))
comp_conv2d(conv2d, X).shape

torch.Size([8, 8])

# We use a convolution kernel with height 5 and width 3. The padding on
# either side of the height and width are 2 and 1, respectively
conv2d = nn.Conv2D(1, kernel_size=(5, 3), padding=(2, 1))
comp_conv2d(conv2d, X).shape

(8, 8)

# We use a convolution kernel with height 5 and width 3. The padding on
# either side of the height and width are 2 and 1, respectively
conv2d = nn.Conv(1, kernel_size=(5, 3), padding=(2, 1))
comp_conv2d(conv2d, X).shape

(8, 8)

# We use a convolution kernel with height 5 and width 3. The padding on
# either side of the height and width are 2 and 1, respectively
conv2d = tf.keras.layers.Conv2D(1, kernel_size=(5, 3), padding='same')
comp_conv2d(conv2d, X).shape

TensorShape([8, 8])

7.3.2. 步幅

在计算互相关时,我们从输入张量左上角的卷积窗口开始,然后将其滑过所有位置,包括向下和向右。在前面的示例中,我们默认一次滑动一个元素。然而,有时,无论是为了提高计算效率还是因为我们希望下采样,我们一次将窗口移动一个以上的元素,跳过中间位置。如果卷积核很大,这是特别有用的,因为它捕获了大面积的底层图像。

我们将每张幻灯片遍历的行数和列数称为 步幅。到目前为止,我们对高度和宽度都使用了 1 的步幅。有时,我们可能想使用更大的步幅。 图 7.3.3显示了垂直步长为 3,水平步长为 2 的二维互相关运算。阴影部分是输出元素以及用于输出计算的输入和内核张量元素: 0×0+0×1+1×2+2×3=8, 0×0+6×1+0×2+0×3=6. 我们可以看到,当第一列的第二个元素生成时,卷积窗口向下滑动了三行。当生成第一行的第二个元素时,卷积窗口向右滑动两列。当卷积窗口在输入上继续向右滑动两列时,就没有输出了,因为输入元素无法填满窗口(除非我们再添加一列padding)。

pYYBAGR9NVGADh_aAAE0DSKqWFg595.svg

图 7.3.3高度和宽度的步长分别为 3 和 2 的互相关。

一般来说,当高度的步幅为sh宽度的步幅是sw,输出形状为

(7.3.2)⌊(nh−kh+ph+sh)/sh⌋×⌊(nw−kw+pw+sw)/sw⌋.

如果我们设置ph=kh−1和pw=kw−1, 那么输出形状可以简化为 ⌊(nh+sh−1)/sh⌋×⌊(nw+sw−1)/sw⌋. 更进一步,如果输入的高度和宽度可以被高度和宽度的步幅整除,那么输出形状将是 (nh/sh)×(nw/sw).

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

conv2d = nn.LazyConv2d(1, kernel_size=3, padding=1, stride=2)
comp_conv2d(conv2d, X).shape

torch.Size([4, 4])

conv2d = nn.Conv2D(1, kernel_size=3, padding=1, strides=2)
comp_conv2d(conv2d, X).shape

(4, 4)

conv2d = nn.Conv(1, kernel_size=(3, 3), padding=1, strides=2)
comp_conv2d(conv2d, X).shape

(4, 4)

conv2d = tf.keras.layers.Conv2D(1, kernel_size=3, padding='same', strides=2)
comp_conv2d(conv2d, X).shape

TensorShape([4, 4])

让我们看一个稍微复杂一点的例子。

conv2d = nn.LazyConv2d(1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4))
comp_conv2d(conv2d, X).shape

torch.Size([2, 2])

conv2d = nn.Conv2D(1, kernel_size=(3, 5), padding=(0, 1), strides=(3, 4))
comp_conv2d(conv2d, X).shape

(2, 2)

conv2d = nn.Conv(1, kernel_size=(3, 5), padding=(0, 1), strides=(3, 4))
comp_conv2d(conv2d, X).shape

(2, 2)

conv2d = tf.keras.layers.Conv2D(1, kernel_size=(3,5), padding='valid',
                strides=(3, 4))
comp_conv2d(conv2d, X).shape

TensorShape([2, 1])

7.3.3. 总结与讨论

填充可以增加输出的高度和宽度。这通常用于为输出提供与输入相同的高度和宽度,以避免不希望的输出收缩。此外,它确保所有像素的使用频率相同。通常我们在输入高度和宽度的两侧选择对称填充。在这种情况下,我们指的是 (ph,pw)填充。最常见的是我们设置ph=pw,在这种情况下,我们只是声明我们选择填充p.

类似的约定适用于步幅。横步时 sh和垂直步幅swmatch,我们简单说说strides. 步幅可以降低输出的分辨率,例如将输出的高度和宽度降低到仅 1/n输入的高度和宽度n>1. 默认情况下,填充为 0,步幅为 1。

到目前为止,我们讨论的所有填充都只是用零扩展图像。这具有显着的计算优势,因为它很容易实现。此外,可以将运算符设计为隐式利用此填充,而无需分配额外的内存。同时,它允许 CNN 对图像中的隐式位置信息进行编码,只需了解“空白”的位置即可。零填充有很多替代方法。 Alsallakh等人。( 2020 )提供了替代方案的广泛概述(尽管没有明确的案例使用非零填充,除非出现伪影)。

7.3.4. 练习

给定本节中最后一个具有内核大小的代码示例 (3,5), 填充(0,1), 和大步(3,4), 计算输出形状以检查它是否与实验结果一致。

对于音频信号,stride为2对应什么?

实施镜像填充,即边界值被简单地镜像以扩展张量的填充。

步幅大于 1 的计算优势是什么?

大于 1 的步幅在统计上有什么好处?

你将如何实现一大步12?它对应什么?这什么时候有用?

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • pytorch
    +关注

    关注

    2

    文章

    802

    浏览量

    13105
收藏 人收藏

    评论

    相关推荐

    Pytorch模型训练实用PDF教程【中文】

    ?模型部分?还是优化器?只有这样不断的通过可视化诊断你的模型,不断的对症下药,才能训练出一个较满意的模型。本教程内容及结构:本教程内容主要为在 PyTorch 中训练一个模型所可能涉及到的方法及函数,并且
    发表于 12-21 09:18

    Pytorch自动求导示例

    Pytorch自动微分的几个例子
    发表于 08-09 11:56

    Pytorch入门之的基本操作

    Pytorch入门之基本操作
    发表于 05-22 17:15

    PyTorch如何入门

    PyTorch 入门实战(一)——Tensor
    发表于 06-01 09:58

    PyTorch10的基础教程

    PyTorch 10 基础教程(4):训练分类器
    发表于 06-05 17:42

    Pytorch AI语音助手

    想做一个Pytorch AI语音助手,有没有好的思路呀?
    发表于 03-06 13:00

    如何安装TensorFlow2 Pytorch

    如何安装TensorFlow2 Pytorch
    发表于 03-07 07:32

    如何往星光2板子里装pytorch

    如题,想先gpu版本的pytorch只安装cpu版本的pytorch,pytorch官网提供了基于conda和pip两种安装方式。因为咱是risc架构没对应的conda,而使用pip安装提示也没有
    发表于 09-12 06:30

    pytorch模型转换需要注意的事项有哪些?

    什么是JIT(torch.jit)? 答:JIT(Just-In-Time)是一组编译工具,用于弥合PyTorch研究与生产之间的差距。它允许创建可以在不依赖Python解释器的情况下运行的模型
    发表于 09-18 08:05

    CEA R7.3 标准,CEA R7.3 标准是什么意思

    CEA R7.3 标准,CEA R7.3 标准是什么意思 电力线通信在家庭中应用最早的是家庭组网。在家庭中,各种信息家电例如空调、照明系统、视频
    发表于 04-09 11:15 1468次阅读

    基于步数步幅统计的测距方法

    结构构建是室内地图构建的基础,而室内测距是结构构建中的核心问题。为克服现有测距方法中成本高或精度低的不足,在融合了多种智能手机传感器数据的基础上,重新设计了基于步数步幅统计的测距方法。在步数统计阶段
    发表于 11-30 17:33 1次下载
    基于步数<b class='flag-5'>步幅</b>统计的测距方法

    基于PyTorch的深度学习入门教程之PyTorch简单知识

    本文参考PyTorch官网的教程,分为五个基本模块来介绍PyTorch。为了避免文章过长,这五个模块分别在五篇博文中介绍。 Part1:PyTorch简单知识 Part2:PyTorch
    的头像 发表于 02-16 15:20 2202次阅读

    底部填充胶胶水如何填充芯片

    什么是底部填充胶?底部填充胶简单来说就是底部填充用的胶水,主要是以主要成份为环氧树脂的胶水对BGA 封装模式的芯片进行底部填充,利用加热的固化形式,将BGA芯片底部空隙大面积 (一般覆
    发表于 07-19 09:30 8184次阅读

    PyTorch教程7.3填充步幅

    电子发烧友网站提供《PyTorch教程7.3填充步幅.pdf》资料免费下载
    发表于 06-05 10:15 0次下载
    <b class='flag-5'>PyTorch</b>教程<b class='flag-5'>7.3</b>之<b class='flag-5'>填充</b>和<b class='flag-5'>步幅</b>

    pytorch怎么在pycharm中运行

    第一部分:PyTorch和PyCharm的安装 1.1 安装PyTorch PyTorch是一个开源的机器学习库,用于构建和训练神经网络。要在PyCharm中使用PyTorch,首先需
    的头像 发表于 08-01 16:22 1090次阅读