6-4 填充和步幅

news2024/11/14 14:17:04

请添加图片描述
在前面的例子 图6.2.1中,输入的高度和宽度都为 3 3 3,卷积核的高度和宽度都为 2 2 2,生成的输出表征的维数为 2 × 2 2\times 2 2×2。 正如我们在 6-2节中所概括的那样,假设输入形状为 n h × n w n_{h}\times n_{w} nh×nw,卷积核形状为 k h × k w k_{h}\times k_{w} kh×kw,那么输出形状将是 ( n h − k h + 1 ) × ( n w − k w + 1 ) (n_{h}-k_{h}+1)\times(n_{w}-k_{w}+1) (nhkh+1)×(nwkw+1)。 因此,卷积的输出形状取决于输入形状卷积核的形状

还有什么因素会影响输出的大小呢?本节我们将介绍填充(padding)步幅(stride)。假设以下情景:

  • 有时,在应用了连续的卷积之后,我们最终得到的输出远小于输入大小。这是由于卷积核的宽度和高度通常大于 1 1 1所导致的。比如,一个 240 × 240 240\times 240 240×240像素的图像,经过 10 10 10 5 × 5 5\times 5 5×5的卷积后,将减少到 200 × 200 200\times 200 200×200像素。如此一来,原始图像的边界丢失了许多有用信息。而填充是解决此问题最有效的方法
  • 有时,我们可能希望大幅降低图像的宽度和高度。例如,如果我们发现原始的输入分辨率十分冗余。步幅则可以在这类情况下提供帮助。

填充

如上所述,在应用多层卷积时,我们常常丢失边缘像素。 由于我们通常使用小卷积核,因此对于任何单个卷积,我们可能只会丢失几个像素。 但随着我们应用许多连续卷积层,累积丢失的像素数就多了。
解决这个问题的简单方法即为填充(padding)在输入图像的边界填充元素(通常填充元素是 0 0 0。 例如,在 图6.3.1中,我们将 3 × 3 3\times 3 3×3输入填充到 5 × 5 5\times 5 5×5,那么它的输出就增加为 4 × 4 4\times 4 4×4。阴影部分是第一个输出元素以及用于输出计算的输入和核张量元素: 0 × 0 + 0 × 1 + 0 × 2 + 0 × 3 = 0 0\times 0+0\times 1+0\times 2+ 0\times 3=0 0×0+0×1+0×2+0×3=0
请添加图片描述
通常,如果我们添加 p h p_{h} ph行填充(大约一半在顶部,一半在底部)和 p w p_{w} pw列填充(左侧大约一半,右侧一半),则输出形状将为:
( n h − k h + p h + 1 ) × ( n w − k w + p w + 1 ) (n_{h}-k_{h}+p_{h}+1)\times(n_{w}-k_{w}+p_{w}+1) (nhkh+ph+1)×(nwkw+pw+1)
这意味着输出的高度宽度将分别增加 p h p_{h} ph p w p_{w} pw
在许多情况下,我们需要设置 p h = k h − 1 p_{h}=k_{h}-1 ph=kh1 p w = k w − 1 p_{w}=k_{w}-1 pw=kw1使输入和输出具有相同的高度和宽度。 这样可以在构建网络时更容易地预测每个图层的输出形状。假设 k h k_{h} kh是奇数,我们将在高度的两侧填充 p h / 2 p_{h}/2 ph/2行。 如果 k h k_{h} kh是偶数,则一种可能性是在输入顶部填充 ⌈ p h / 2 ⌉ \lceil p_{h}/2 \rceil ph/2行,在底部填充 ⌊ p h / 2 ⌋ \lfloor p_{h}/2 \rfloor ph/2行。同理,我们填充宽度的两侧。

卷积神经网络中卷积核的高度和宽度通常为奇数,例如1、3、5或7选择奇数的好处是,保持空间维度的同时,我们可以在顶部和底部填充相同数量的行,在左侧和右侧填充相同数量的列

此外,使用奇数的核大小和填充大小也提供了书写上的便利。
对于任何二维张量 X X X,当满足:

  1. 卷积核的大小是奇数;
  2. 所有边的填充行数和列数相同;
  3. 输出与输入具有相同高度和宽度

则可以得出:输出 Y [ i , j ] Y[i, j] Y[i,j]是通过以输入 X [ i , j ] X[i, j] X[i,j]为中心,与卷积核进行互相关计算得到的

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

在卷积神经网络中,如果你创建了一个高度和宽度都为 3 3 3的二维卷积层,并在所有侧边填充 1 1 1个像素,然后用这个卷积层处理一个高度和宽度都为 8 8 8的输入,输出的高度和宽度仍然是 8 8 8。下面是为什么这样设置可以达到这个效果的解释:

卷积核尺寸:在这个例子中,卷积核(filter)的高度和宽度 k h = k w = 3 k_h = k_w = 3 kh=kw=3。这意味着每个卷积核覆盖了输入数据的 3 × 3 3 \times 3 3×3区域。

填充大小:为了确保输出尺寸与输入尺寸相同,我们使用了填充。填充是在输入张量的边界添加额外的行和列,这里填充的尺寸 p h = p w = 2 p_h = p_w = 2 ph=pw=2。这意味着在输入的每一边(顶部、底部、左侧和右侧)都加上了一个像素的填充。

输出尺寸的计算: 通常输出尺寸的公式是 ( n h − k h + p h + 1 ) × ( n w − k w + p w + 1 ) (n_{h} - k_{h} + p_{h} + 1) \times (n_{w} - k_{w} + p_{w} + 1) (nhkh+ph+1)×(nwkw+pw+1)。 在这个例子中,输入尺寸 n h = n w = 8 n_h = n_w = 8 nh=nw=8,卷积核尺寸 k h = k w = 3 k_h = k_w = 3 kh=kw=3,填充 p h = p w = 2 p_h = p_w = 2 ph=pw=2。 代入公式得到输出尺寸为 $(8 - 3 + 2 + 1) \times (8 - 3 + 2 +

  1. = 8 \times 8$。
    保持尺寸相同通过添加等量的填充,卷积操作可以在不改变数据的高度和宽度的情况下进行。这样做的好处是简化了网络结构的设计,因为你可以确保无论添加多少这种类型的层,输入与输出的尺寸都保持一致,这使得每层的输出都可以明确计算和预见

因此,通过在高度和宽度为 3 3 3的卷积层周围加上 1 1 1像素的填充(这里说的填充,是加在输入张量上的,周围加1,即为共加了 2 2 2行,加了 2 2 2),并处理高度和宽度为 8 8 8的输入,可以确保输出仍然保持 8 × 8 8 \times 8 8×8的尺寸,这有助于在设计深层网络时维持结构的一致性

import torch
from torch import nn

# 为了方便起见,我们定义了一个计算卷积层的函数。
# 此函数初始化卷积层权重,并对输入和输出提高和缩减相应的维数
# 接受两个参数:conv2d(一个卷积层对象)和 X(输入数据张量)
def comp_conv2d(conv2d, X):
    # 这里的(1,1)表示批量大小和通道数都是1
    X = X.reshape((1, 1) + X.shape)
    # 将输入张量 X 的形状重塑为 (1, 1, height, width)
    # 这里的 (1, 1) 分别表示批量大小和通道数都是 1
    # PyTorch 的卷积层通常期望输入的形状为 (batch_size, channels, height, width)。
    Y = conv2d(X)
    # conv2d 对象被应用于已调整形状的输入 X
    # conv2d 是一个卷积层,它执行卷积运算并输出结果 Y。
    
    return Y.reshape(Y.shape[2:]) # 省略前两个维度:批量大小和通道
    # 去除 Y 的前两个维度(批量大小和通道数),只保留高度和宽度,这样输出就是卷积结果的空间维度。

# 请注意,这里每边都填充了1行或1列,因此总共添加了2行或2列
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1)
# 创建了一个 Conv2d 卷积层,具有 1 个输入通道和 1 个输出通道,卷积核大小为 3(3x3),并且在所有边界上填充了 1 个像素(这样做可以保持输入和输出的空间尺寸相同)。
# padding=1意思是在上下左右各填充一行!

X = torch.rand(size=(8, 8))
# 生成一个形状为 (8, 8) 的张量,填充了 0 到 1 之间的随机数,代表一个 8x8 的图像数据。
comp_conv2d(conv2d, X).shape
# 使用定义好的 comp_conv2d 函数来计算卷积,并输出处理后的数据的形状。
# 由于卷积层使用了填充,输入和输出的形状应该是相同的,即 (8, 8)。

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

conv2d = nn.Conv2d(1, 1, kernel_size=(5, 3), padding=(2, 1))
comp_conv2d(conv2d, X).shape

请添加图片描述
这个例子其实也说明了, p h p_{h} ph p w p_{w} pw在实际使用的时候可以分别设置不一样的

巧记

  • p h = k h − 1 p_{h}=k_{h}-1 ph=kh1 p w = k w − 1 p_{w}=k_{w}-1 pw=kw1
    p h p_{h} ph是指在高度上,在原始输入的上部和下部,一共添加了多少行
    p w p_{w} pw是指在宽度上,在原始输入的左边和右边,一共添加了多少行

  • nn.Conv2d中的padding=(x,y),这里的(x,y)指的是,在上部和下部分别添加x行,在左边和右边分别添加y列,padding参数的值是 p h p_{h} ph p w p_{w} pw的一半。

  • 输出形状计算公式: ( n h − k h + p h + 1 ) × ( n w − k w + p w + 1 ) (n_{h}-k_{h}+p_{h}+1)\times(n_{w}-k_{w}+p_{w}+1) (nhkh+ph+1)×(nwkw+pw+1)

  • 卷积神经网络中卷积核的高度和宽度通常为奇数


步幅

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

我们将每次滑动元素的数量称为步幅(stride)。到目前为止,我们只使用过高度或宽度为 1 1 1的步幅,那么如何使用较大的步幅呢? 图6.3.2是垂直步幅为 3 3 3,水平步幅为 2 2 2的二维互相关运算。 着色部分是输出元素以及用于输出计算的输入和内核张量元素: 0 × 0 + 0 × 1 + 1 × 2 + 2 × 3 = 8 0\times 0+0\times 1+1\times2+2\times3=8 0×0+0×1+1×2+2×3=8 0 × 0 + 6 × 1 + 0 × 2 + 0 × 3 = 6 0\times0+6\times1+0\times2+0\times3=6 0×0+6×1+0×2+0×3=6

可以看到,为了计算输出中第一列的第二个元素和第一行的第二个元素,卷积窗口分别向下滑动三行和向右滑动两列。但是,当卷积窗口继续向右滑动两列时,没有输出,因为输入元素无法填充窗口(除非我们添加另一列填充)。
请添加图片描述
通常,当垂直步幅为 s h s_{h} sh、水平步幅为 s w s_{w} sw时,输出形状为:
请添加图片描述
请添加图片描述
下面,我们将高度和宽度的步幅设置为 2 2 2,从而将输出的高度和宽度减半。

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

请添加图片描述

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

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

请添加图片描述
为了简洁起见,当输入高度和宽度两侧的填充数量分别为 p h p_{h} ph p w p_{w} pw时,我们称之为填充 ( p h , p w ) (p_{h},p_{w}) (ph,pw)。当 p h = p w = p p_{h}=p_{w}=p ph=pw=p时,填充是 p p p
同理,当高度和宽度上的步幅分别为 s h s_{h} sh s w s_{w} sw时,我们称之为步幅 ( s h , s w ) (s_{h},s_{w}) (sh,sw)。特别地,当 s h = s w = s s_{h}=s_{w}=s sh=sw=s时,我们称步幅为 s s s
默认情况下,填充为 0 0 0,步幅为 1 1 1
在实践中,我们很少使用不一致的步幅或填充,也就是说,我们通常有 p h = p w p_{h}=p_{w} ph=pw s h = s w s_{h}=s_{w} sh=sw

小结

  • 填充可以增加输出的高度和宽度。这常用来使输出与输入具有相同的高和宽。

  • 步幅可以减小输出的高和宽,例如输出的高和宽仅为输入的高和宽的 1 / n 1/n 1/n
    n n n是一个大于 1 1 1的整数)。

  • 填充和步幅可用于有效地调整数据的维度

请添加图片描述
可以通过统筹选择设置填充和步幅,来将矩形的输入张量,转换为指定高宽比的输出张量


小trick

  • 一般来说,填充的目的是让输入张量和输出张量的形状一致
  • 通常来讲,步幅等于 1 1 1是最好的,如果不选步幅为 1 1 1,一般是我们觉得计算量太大了,需要很多很多层才会使得我这个图片变得越来越小,我不想真的用很多层去做这个事情。通常来说步幅是取 2 2 2,每一次减半。步幅取多少,一般看我想把模型复杂度控制在什么程度
  • 步幅、填充、核大小和通道数,都是一个神经网络架构的一部分,是我整个网络怎么设计的一部分。
  • 在实际情况中,我们一定是参照经典的网络结构做设计

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1972810.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大象机器人水星MercuryX1轮式人形机器人基于物体标记建模的键盘点按操作!

引言 在现代科技的推动下,机器人在日常生活和工作场景中的应用越来越广泛。本文将介绍MercuryX1,这款先进的机器人如何通过其手臂末端的摄像头识别并确定键盘的键位,从而进行精确的打字操作。通过这一案例,我们将展示MercuryX1在自…

xcode使用

1. 界面 1.1. Build Settings,Build Phases和Build Rules三个设置项 Build Settings(编译设置): 每个选项由标题(Title)和定义(Definition)组成。这里主要定义了Xcode在编译项目时的一些具体配置 Build Phases(编译资源):用于指定编译过程中项目所链接的原文件,依赖对象,库…

安装 electron 报错解决

1. 报错 大概率由镜像问题导致 2. 解决 2.1 打开 npm 配置 npm config edit 2.2 添加配置 registryhttps://registry.npmmirror.comelectron_mirrorhttps://cdn.npmmirror.com/binaries/electron/electron_builder_binaries_mirrorhttps://npmmirror.com/mirrors/electron…

Tensor安装和测试

1: 打开git官方 https://github.com/NVIDIA/TensorRT 2: 下载得到:TensorRT-10.2.0.19.Linux.x86_64-gnu.cuda-11.8.tar.gz 3: 下载后配置环境变量,上面地址记得改成真实地址。 4: 如果想python使用tensorrt,那么 解压后目录&#xff0c…

深入理解单元测试与JUnit:从基础概念到实践操作

文章目录 前言一、单元测试是什么?单元测试的特点单元测试的好处 二、junit是什么?三、操作步骤1.junit安装2.maven新建项目3. 新建java文件4. 生成测试类5. 编写测试方法6. 测试结果 总结 前言 随着软件开发行业的不断发展,测试的重要性日益…

清华和字节联合推出的视频理解大模型video-SALMONN(ICML 2024)

video-SALMONN: Speech-Enhanced Audio-Visual Large Language Models 论文信息 paper:https://arxiv.org/abs/2406.15704 code:https://github.com/bytedance/SALMONN/ AI也会「刷抖音」!清华领衔发布短视频全模态理解新模型 | ICML 2024 …

Python数值计算(10)——PPoly对象

在scipy中,scipy.interpolate下还有一个PPoly的类,用于表示插值多项式,很多插值算法的结果,都以该类的实例返回,因此有必要了解该类的使用方法。要使用该类,首先要引入相应的模块: from scipy.…

基于docker的 nacos安装部署

一、拉取镜像 拉取nacos官方镜像,这里使用默认命令 docker pull nacos/nacos-server二、创建挂载目录 创建本地的映射文件application.properties mkdir -p /home/docker/nacos/conf /home/docker/nacos/logstouch /home/docker/nacos/conf/application.propert…

举个栗子!Tableau 技巧(280):创建点象限图( Dot Quadrant Chart )

之前分享过 🌰 :四象限图 和 葡萄干布丁图。今天,我们将两者的呈现方式结合起来,创建如下的点象限图( Dot Quadrant Chart ),可以帮助数据粉在有限的看板区域内展示更多的数据信息。 那么,如何在 Tableau 中…

一文弄清Java的四大引用及其两大传递

开场白 Hello大家好呀,我是CodeCodeBond✊最近在复习很多很多的基础知识,有了很多新的感悟~ 话不多说,直接发车✈ 四大引用 问题切入点 在学习 Thread线程利用ThreadLocalMap实现线程的本地内存(变量副本)的时候&…

简单的docker学习 第1章 docker 概述

Docker 学习笔记 本文是b站动力节点docker学习视频的笔记整理,主要用于自己学习复习使用,视频具体地址为 : 动力节点docker 第一章 docker 概述 1.1 课程引人入 1.1.1 开发/运维互掐 ​ 开发与测试和运维间的矛盾,主要是由于环境的不同而…

flutter 做代码混淆

第一种、手动混淆 修改代码中出现次数多的 类目 方法 。修改静态资源的名字,转静态资源为webp 第二种、使用flutter 自带的命令行工具进行混淆 混淆 Dart 代码 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 使用pragma(vm:entry-point) 装饰器修改方…

【界面开发实战】使用DevEco Studio编写支付宝首页

效果展示 知识点 层叠布局 上一篇文章已经介绍了,这篇文章中不再赘述,如果想了解的话可以去看上一篇文章,链接如下: http://t.csdnimg.cn/CnBZMhttp://t.csdnimg.cn/CnBZM 弹性布局 作用:提供更加有效的方式对容器…

YOLOV5 改进:替换backbone为MobileVIT

1. 介绍 yolov5替换主干网络的步骤如下,依旧和之前的一样 2. 更改common文件 将下面代码加入common最下面即可: from einops import rearrange import torch import torch.nn as nn# Transformer Attention模块定义 class TAttention(nn.Module):def __init__(self, dim, …

string的底层简单实现(造轮子)

文件:String.h ----- 头文件 String.cpp ----- 源文件 Test.cpp ----- 源文件 实现细节: 实现带参构造: 在实现带参构造建议不使用初始化列表,初始化去写不太好: :_str(new char[strlen(str)1]) 用初始化列表要在…

如何在 Jupyter Notebook 中直接设置全局随机种子的方法及易错地方、notebook和pycharm中设置随机种子的区别

结论: 在 Jupyter Notebook 中直接设置全局随机种子的方法是确保每个单独的代码块中都调用相同的 set_seed 函数。这是最简单且有效的方法。在每个代码块开头设置随机种子,确保代码在每次执行时具有相同的随机数生成顺序。 易错地方: …

mac配置git的sshkey

在MAC中配置Git的SSH Key: 1.打开终端 2.生成SSH密钥,输入以下命令: ssh-keygen -t rsa -b 4096 -C “你自己的账号电子邮件地址” 按回车键后,系统会提示你输入文件保存路径,默认为~/.ssh/id_rsa直接按回车键使用默…

数据结构初阶之排序(上)

排序的概念及其应用 排序的概念 排序:所谓排序,就是使⼀串记录,按照其中的某个或某些关键字的⼤⼩,递增或递减的排列起来的操作。 排序的应用 如下图: 样例数组 下面我们给出一组乱序的数组,接下来的算…

程序员进阶架构知识体系、开发运维工具使用、Java体系知识扩展、前后端分离流程详解、设计模式开发实例汇总专栏分享

场景 作为一名开发者,势必经历过从入门到自学、从基础到进阶、从学习到强化的过程。 当经历过几年企业级开发的磨炼,再回头看之前的开发过程、成长阶段发现确实是走了好多的弯路。 作为一名终身学习的信奉者,秉承持续学习、持续优化的信念…