torch.nn.MaxPool2d详解

注意:这里展示的是本篇博文写时的版本最新的实现,但是后续会代码可能会迭代更新,建议对照官方文档进行学习。

先来看源码:

# 这个类是是许多池化类的基类,这里有必要了解一下
class _MaxPoolNd(Module):
    __constants__ = ['kernel_size', 'stride', 'padding', 'dilation',
                     'return_indices', 'ceil_mode']
    return_indices: bool
    ceil_mode: bool
	# 构造函数,这里只需要了解这个初始化函数即可。
    def __init__(self, kernel_size: _size_any_t, stride: Optional[_size_any_t] = None,
                 padding: _size_any_t = 0, dilation: _size_any_t = 1,
                 return_indices: bool = False, ceil_mode: bool = False) -> None:
        super(_MaxPoolNd, self).__init__()
        self.kernel_size = kernel_size
        self.stride = stride if (stride is not None) else kernel_size
        self.padding = padding
        self.dilation = dilation
        self.return_indices = return_indices
        self.ceil_mode = ceil_mode

    def extra_repr(self) -> str:
        return 'kernel_size={kernel_size}, stride={stride}, padding={padding}' \
            ', dilation={dilation}, ceil_mode={ceil_mode}'.format(**self.__dict__)

class MaxPool2d(_MaxPoolNd):
    kernel_size: _size_2_t
    stride: _size_2_t
    padding: _size_2_t
    dilation: _size_2_t

    def forward(self, input: Tensor) -> Tensor:
        return F.max_pool2d(input, self.kernel_size, self.stride,
                            self.padding, self.dilation, self.ceil_mode,
                            self.return_indices)

MaxPool2d 这个类的实现十分简单。

我们先来看一下基本参数,一共六个:

  1. kernel_size :表示做最大池化的窗口大小,可以是单个值,也可以是tuple元组
  2. stride :步长,可以是单个值,也可以是tuple元组
  3. padding :填充,可以是单个值,也可以是tuple元组
  4. dilation :控制窗口中元素步幅
  5. return_indices :布尔类型,返回最大值位置索引
  6. ceil_mode :布尔类型,为True,用向上取整的方法,计算输出形状;默认是向下取整。

关于 kernel_size 的详解

注意这里的 kernel_size 跟卷积核不是一个东西。 kernel_size 可以看做是一个滑动窗口,这个窗口的大小由自己指定,如果输入是单个值,例如

3

3

3 ,那么窗口的大小就是

3

×

3

3 \times 3

3×3 ,还可以输入元组,例如 (3, 2) ,那么窗口大小就是

3

×

2

3 \times 2

3×2

最大池化的方法就是取这个窗口覆盖元素中的最大值。

关于 stride 的详解

上一个参数我们确定了滑动窗口的大小,现在我们来确定这个窗口如何进行滑动。如果不指定这个参数,那么默认步长跟最大池化窗口大小一致。如果指定了参数,那么将按照我们指定的参数进行滑动。例如 stride=(2,3) , 那么窗口将每次向右滑动三个元素位置,或者向下滑动两个元素位置。

关于 padding 的详解

这参数控制如何进行填充,填充值默认为0。如果是单个值,例如 1,那么将在周围填充一圈0。还可以用元组指定如何填充,例如

p

a

d

d

i

n

g

=

(

2

,

1

)

padding=(2, 1)

padding=(2,1) ,表示在上下两个方向个填充两行0,在左右两个方向各填充一列0。

关于 dilation 的详解

不会

关于 return_indices 的详解

这是个布尔类型值,表示返回值中是否包含最大值位置的索引。注意这个最大值指的是在所有窗口中产生的最大值,如果窗口产生的最大值总共有5个,就会有5个返回值。

关于 ceil_mode 的详解

这个也是布尔类型值,它决定的是在计算输出结果形状的时候,是使用向上取整还是向下取整。怎么计算输出形状,下面会讲到。一看就知道了。

——————————————参数解析结束分界线——————————————

最大池化层输出形状计算

H

o

u

t

=

H

i

n

+

2

×

p

a

d

d

i

n

g

0

d

i

l

a

t

i

o

n

0

×

(

k

e

r

n

e

l

_

s

i

z

e

0

1

)

1

s

t

r

i

d

e

0

+

1

H_{out}=\lfloor \frac{H_{in} + 2 \times padding\lfloor 0 \rfloor - dilation \lfloor 0 \rfloor \times (kernel\_size\lfloor 0 \rfloor - 1)-1}{stride\lfloor 0 \rfloor} + 1 \rfloor

Hout=stride0Hin+2×padding0dilation0×(kernel_size01)1+1

W

o

u

t

=

W

i

n

+

2

×

p

a

d

d

i

n

g

1

d

i

l

a

t

i

o

n

1

×

(

k

e

r

n

e

l

_

s

i

z

e

1

1

)

1

s

t

r

i

d

e

1

+

1

W_{out}=\lfloor \frac{W_{in} + 2 \times padding\lfloor 1 \rfloor - dilation \lfloor 1 \rfloor \times (kernel\_size\lfloor 1 \rfloor - 1)-1}{stride\lfloor 1 \rfloor} + 1 \rfloor

Wout=stride1Win+2×padding1dilation1×(kernel_size11)1+1

看到向下取整的符号了吗?这个就是由 ceil_mode 控制的。

——————————————结束分界线——————————————

下面我们写代码验证一下最大池化层是如何计算的:

首先验证 kernel_size 参数

import torch
import torch.nn as nn

# 仅定义一个 3x3 的池化层窗口
m = nn.MaxPool2d(kernel_size=(3, 3))

# 定义输入
# 四个参数分别表示 (batch_size, C_in, H_in, W_in)
# 分别对应,批处理大小,输入通道数,图像高度(像素),图像宽度(像素)
# 为了简化表示,我们只模拟单张图片输入,单通道图片,图片大小是6x6
input = torch.randn(1, 1, 6, 6)

print(input)

output = m(input)

print(output)

结果:
在这里插入图片描述

第一个tensor是我们的输入数据

1

×

1

×

6

×

6

1 \times 1 \times 6 \times 6

1×1×6×6 ,我们画红线的区域就是我们设置的窗口大小

3

×

3

3 \times 3

3×3 ,背景色为红色的值,为该区域的最大值。

第二个tensor就是我们最大池化后的结果,跟我们标注的一模一样。

这个就是最基本的最大池化。

之后我们验证一下 stride 参数

import torch
import torch.nn as nn

# 仅定义一个 3x3 的池化层窗口
m = nn.MaxPool2d(kernel_size=(3, 3), stride=(2, 2))

# 定义输入
# 四个参数分别表示 (batch_size, C_in, H_in, W_in)
# 分别对应,批处理大小,输入通道数,图像高度(像素),图像宽度(像素)
# 为了简化表示,我们只模拟单张图片输入,单通道图片,图片大小是6x6
input = torch.randn(1, 1, 6, 6)

print(input)

output = m(input)

print(output)

结果:

在这里插入图片描述

红色的还是我们的窗口,但是我们的步长变为了2,可以看到第一个窗口和向右滑动后的窗口,他们的最大值刚好是重叠的部分都是2.688,向下滑动之后,最大值是0.8030,再次向右滑动,最大值是2.4859。

可以看到我们在滑动的时候省略了部分数值,因为剩下的数据不够一次滑动了,于是我们将他们丢弃了。

其实最后图片的宽度和高度还可以通过上面两个公式来计算,我们公式中用的是向下取整,因此我们丢弃了不足的数据。现在我们试试向上取整。

利用 ceil_mode 参数向上取整

import torch
import torch.nn as nn

# 仅定义一个 3x3 的池化层窗口
m = nn.MaxPool2d(kernel_size=(3, 3), stride=(2, 2), ceil_mode=True)

# 定义输入
# 四个参数分别表示 (batch_size, C_in, H_in, W_in)
# 分别对应,批处理大小,输入通道数,图像高度(像素),图像宽度(像素)
# 为了简化表示,我们只模拟单张图片输入,单通道图片,图片大小是6x6
input = torch.randn(1, 1, 6, 6)

print(input)

output = m(input)

print('\n\n\n\n\n')

print(output)

结果:

在这里插入图片描述

从结果可以看出,输出的size由原来的

2

×

2

2 \times 2

2×2 变成了现在的

3

×

3

3 \times 3

3×3 。这就是向上取整的结果。为什么会出现这样的结果呢?

这看起来像是我们对输入进行了填充,但是这个填充值不会参与到计算最大值中。

继续验证 padding 参数

import torch
import torch.nn as nn

# 仅定义一个 3x3 的池化层窗口
m = nn.MaxPool2d(kernel_size=(3, 3), stride=(3, 3), padding=(1, 1))

# 定义输入
# 四个参数分别表示 (batch_size, C_in, H_in, W_in)
# 分别对应,批处理大小,输入通道数,图像高度(像素),图像宽度(像素)
# 为了简化表示,我们只模拟单张图片输入,单通道图片,图片大小是6x6
input = torch.randn(1, 1, 6, 6)

print(input)

output = m(input)

print('\n\n')

print(output)

结果:

在这里插入图片描述

我们对周围填充了一圈0,我们滑动窗口的范围就变化了,这就是填充的作用。

但是有一点需要注意,就是即使我们填充了0,这个0也不会被选为最大值。例如上图的左上角四个数据,如果我们全部变为负数,结果是-0.1711,而不会是我们填充的0值,这一点要注意。

最后验证 return_indices 参数:

import torch
import torch.nn as nn

# 仅定义一个 3x3 的池化层窗口
m = nn.MaxPool2d(kernel_size=(3, 3), return_indices=True)

# 定义输入
# 四个参数分别表示 (batch_size, C_in, H_in, W_in)
# 分别对应,批处理大小,输入通道数,图像高度(像素),图像宽度(像素)
# 为了简化表示,我们只模拟单张图片输入,单通道图片,图片大小是6x6
input = torch.randn(1, 1, 6, 6)

print(input)

output = m(input)

print(output)

结果:

在这里插入图片描述

仅仅是多返回了一个位置信息。元素位置从0开始计数,6表示第7个元素,9表示第10个元素…需要注意的是,返回值实际上是多维的数据,但是我们只看相关的元素位置信息,忽略维度的问题。

最后一个参数 dilation ,不会

版权声明:本文为CSDN博主「Medlen」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_38481963/article/details/109962715

Medlen

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

深度学习之目标检测YOLOv5

一.简介 YOLOV4出现之后不久,YOLOv5横空出世。YOLOv5在YOLOv4算法的基础上做了进一步的改进,检测性能得到进一步的提升。虽然YOLOv5算法并没有与YOLOv4算法进行性能比较与分析&#xff0

目标检测入坑指南4:GoogLeNet神经网络

前面介绍的三个神经网络都是“串联”的,仅仅是卷积层的不断堆叠,结构比较简单。接下来两篇博客要介绍的GoogLeNet和ResNet中开始出现“并联”结构,这也是正式进入目标检测算法前最后要介绍的两个神经

玩转KITTI3D目标检测:KITTI评估工具evaluate的使用

近期因实验需要利用kitti数据集,发现关于评估工具使用的部分网上教程不够详细,特此记录. 文末为了方便对数据结果观看,附上了修改代码. 1. KITTI评估工具来源 官网评估工具 下载后文件目录包含: matlab(2D/3D框显示和

yolov5训练数据集划分

yolov5训练数据集划分 按照默认8:1:1划分训练集,测试集,验证集。 txt文件出现在imageset文件夹。 import os import randomtrainval_pe