学术, 机器学习

卷积和池化后的数据维度

这是之前的一篇:卷积和池化的作用以及代码实现

在卷积和池化后,通常需要和全连接的神经网络进行连接,为了能够匹配,数据维度的计算是很有必要的。当然,通过代码直接打印给出维度也是可以,也更方便,但人为的简单估算可以作为一种补充和验证,从而能够防止一些不必要的错误。另外,可能会有一些通用的维度计算公式,这里不给出公式,只是讨论了一些简单的情况,因为公式会掩盖直观的物理图像。

假设图像的维度为:L (Nx=L, Ny=L)。

一、卷积后的数据维度

文档 torch.nn.Conv2d():https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html

(1)有填充的情况(padding=1),不同卷积核大小对输出数据的维度的影响:

  • 如果 kernel_size=3, stride=1, padding=1, dilation=1,即,卷积核为 3,步幅为 1,边缘有填充,元素之间没有间隔,那么卷积后的维度和卷积之前的维度保持一致,为 L。
  • 一般情况,如果 kernel_size=3+n, stride=1, padding=1, dilation=1,那么卷积后的维度为 L-n。其中,n 的取值为 -2, -1, 0, 1, 2, 3, ... 。

(2)无填充的情况(padding=0),不同卷积核大小对输出数据的维度的影响:

  • 如果 kernel_size=1, stride=1, padding=0, dilation=1,即,卷积核为 1,步幅为 1,边缘有填充,元素之间没有间隔,那么卷积后的维度和卷积之前的维度保持一致,为 L。
  • 一般情况,如果 kernel_size=1+n, stride=1, padding=0, dilation=1,那么卷积后的维度为 L-n。其中,n 的取值为 0, 1, 2, 3, ... 。

(3)不同步幅对输出数据的维度的影响(以kernel_size=3, padding=1的情况为例):

  • 如果 kernel_size=3, stride=1, padding=1, dilation=1,也就是步幅为 1,那么卷积后的维度和卷积之前的维度保持一致,为 L。这个情况和前面的第一个例子是完全一样的。
  • 对于图像维度和步幅能整除的情况,如果 kernel_size=3, stride=n, padding=1, dilation=1,那么卷积后的维度为 L/n。
  • 对于图像维度和步幅不能整除的情况,如果 kernel_size=3, stride=n, padding=1, dilation=1,那么卷积后的维度为 int(L/n)+1。

(4)不同扩张率对输出数据的维度的影响(以stride=1, padding=1的情况为例):

  • 如果 kernel_size=3, stride=1, padding=1, dilation=1,也就是扩张率为 1,元素之间没有间隔,那么卷积后的维度和卷积之前的维度保持一致,为 L。这个情况和前面的第一个例子是完全一样的。
  • 一般情况,如果 kernel_size=3, stride=1, padding=0, dilation=n,那么卷积后的维度为 L-(n-1)*2。这里乘 2 和卷积核的大小有关,如果卷积核大小为m,那么卷积后的维度为 L-(n-1)*(m-1)。

代码验证:

"""
This code is supported by the website: https://www.guanjihuan.com
The newest version of this code is on the web page: https://www.guanjihuan.com/archives/39320
"""

import torch
input_data = torch.randn(1, 1, 28, 28)

print('【有填充的情况,不同卷积核大小对输出数据的维度的影响】')
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=1, stride=1, padding=1, dilation=1)
output_data = conv_layer(input_data)
print("维度(卷积核大小为1):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=2, stride=1, padding=1, dilation=1)
output_data = conv_layer(input_data)
print("维度(卷积核大小为2):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=1, dilation=1)
output_data = conv_layer(input_data)
print("维度(卷积核大小为3):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=4, stride=1, padding=1, dilation=1)
output_data = conv_layer(input_data)
print("维度(卷积核大小为4):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=5, stride=1, padding=1, dilation=1)
output_data = conv_layer(input_data)
print("维度(卷积核大小为5):", output_data.shape)

print()
print('【无填充的情况,不同卷积核大小对输出数据的维度的影响】')
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=1, stride=1, padding=0, dilation=1)
output_data = conv_layer(input_data)
print("维度(卷积核大小为1):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=2, stride=1, padding=0, dilation=1)
output_data = conv_layer(input_data)
print("维度(卷积核大小为2):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=0, dilation=1)
output_data = conv_layer(input_data)
print("维度(卷积核大小为3):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=4, stride=1, padding=0, dilation=1)
output_data = conv_layer(input_data)
print("维度(卷积核大小为4):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=5, stride=1, padding=0, dilation=1)
output_data = conv_layer(input_data)
print("维度(卷积核大小为5):", output_data.shape)

print()
print('【不同步幅对输出数据的维度的影响(以卷积核大小为3,有填充的情况为例)】')
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=1, dilation=1)
output_data = conv_layer(input_data)
print("维度(步幅为1):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=2, padding=1, dilation=1)
output_data = conv_layer(input_data)
print("维度(步幅为2):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=3, padding=1, dilation=1)
output_data = conv_layer(input_data)
print("维度(步幅为3):", output_data.shape)

print()
print('【不同扩张率对输出数据的维度的影响(以步幅为1,有填充的情况为例)】')
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=1, dilation=1)
output_data = conv_layer(input_data)
print("维度(卷积核大小为3,扩张率为1):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=1, dilation=2)
output_data = conv_layer(input_data)
print("维度(卷积核大小为3,扩张率为2):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=1, padding=1, dilation=3)
output_data = conv_layer(input_data)
print("维度(卷积核大小为3,扩张率为3):", output_data.shape)

print()
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=4, stride=1, padding=1, dilation=1)
output_data = conv_layer(input_data)
print("维度(卷积核大小为4,扩张率为1):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=4, stride=1, padding=1, dilation=2)
output_data = conv_layer(input_data)
print("维度(卷积核大小为4,扩张率为2):", output_data.shape)
conv_layer = torch.nn.Conv2d(in_channels=1, out_channels=1, kernel_size=4, stride=1, padding=1, dilation=3)
output_data = conv_layer(input_data)
print("维度(卷积核大小为4,扩张率为3):", output_data.shape)

运行结果:

【有填充的情况,不同卷积核大小对输出数据的维度的影响】
维度(卷积核大小为1): torch.Size([1, 1, 30, 30])
维度(卷积核大小为2): torch.Size([1, 1, 29, 29])
维度(卷积核大小为3): torch.Size([1, 1, 28, 28])
维度(卷积核大小为4): torch.Size([1, 1, 27, 27])
维度(卷积核大小为5): torch.Size([1, 1, 26, 26])

【无填充的情况,不同卷积核大小对输出数据的维度的影响】
维度(卷积核大小为1): torch.Size([1, 1, 28, 28])
维度(卷积核大小为2): torch.Size([1, 1, 27, 27])
维度(卷积核大小为3): torch.Size([1, 1, 26, 26])
维度(卷积核大小为4): torch.Size([1, 1, 25, 25])
维度(卷积核大小为5): torch.Size([1, 1, 24, 24])

【不同步幅对输出数据的维度的影响(以卷积核大小为3,有填充的情况为例)】
维度(步幅为1): torch.Size([1, 1, 28, 28])
维度(步幅为2): torch.Size([1, 1, 14, 14])
维度(步幅为3): torch.Size([1, 1, 10, 10])

【不同扩张率对输出数据的维度的影响(以步幅为1,有填充的情况为例)】
维度(卷积核大小为3,扩张率为1): torch.Size([1, 1, 28, 28])
维度(卷积核大小为3,扩张率为2): torch.Size([1, 1, 26, 26])
维度(卷积核大小为3,扩张率为3): torch.Size([1, 1, 24, 24])

维度(卷积核大小为4,扩张率为1): torch.Size([1, 1, 27, 27])
维度(卷积核大小为4,扩张率为2): torch.Size([1, 1, 24, 24])
维度(卷积核大小为4,扩张率为3): torch.Size([1, 1, 21, 21])

二、池化后的数据维度

文档 torch.nn.MaxPool2d():https://pytorch.org/docs/stable/generated/torch.nn.MaxPool2d.html

这里不考虑填充和扩张率,设置为默认值:padding=0, dilation=1,也就是边缘无填充,元素之间没有间隔。

池化后数据维度的结论和前面的卷积的差不多,以下直接给出代码验证:

"""
This code is supported by the website: https://www.guanjihuan.com
The newest version of this code is on the web page: https://www.guanjihuan.com/archives/39320
"""

import torch
input_data = torch.randn(1, 1, 28, 28)

print('【不同卷积核大小对输出数据的维度的影响】')
max_pool = torch.nn.MaxPool2d(kernel_size=1, stride=1)
output_data = max_pool(input_data)
print("维度(卷积核大小为1):", output_data.shape)
max_pool = torch.nn.MaxPool2d(kernel_size=2, stride=1)
output_data = max_pool(input_data)
print("维度(卷积核大小为2):", output_data.shape)
max_pool = torch.nn.MaxPool2d(kernel_size=3, stride=1)
output_data = max_pool(input_data)
print("维度(卷积核大小为3):", output_data.shape)

print()
print('【不同步幅对输出数据的维度的影响(以卷积核大小为2为例)】')
max_pool = torch.nn.MaxPool2d(kernel_size=2, stride=1)
output_data = max_pool(input_data)
print("维度(步幅为1):", output_data.shape)
max_pool = torch.nn.MaxPool2d(kernel_size=2, stride=2)
output_data = max_pool(input_data)
print("维度(步幅为2):", output_data.shape)
max_pool = torch.nn.MaxPool2d(kernel_size=2, stride=3)
output_data = max_pool(input_data)
print("维度(步幅为3):", output_data.shape)

运行结果:

【不同卷积核大小对输出数据的维度的影响】
维度(卷积核大小为1): torch.Size([1, 1, 28, 28])
维度(卷积核大小为2): torch.Size([1, 1, 27, 27])
维度(卷积核大小为3): torch.Size([1, 1, 26, 26])

【不同步幅对输出数据的维度的影响(以卷积核大小为2为例)】
维度(步幅为1): torch.Size([1, 1, 27, 27])
维度(步幅为2): torch.Size([1, 1, 14, 14])
维度(步幅为3): torch.Size([1, 1, 9, 9])
357 次浏览

【说明:本站主要是个人的一些笔记和代码分享,内容可能会不定期修改。为了使全网显示的始终是最新版本,这里的文章未经同意请勿转载。引用请注明出处:https://www.guanjihuan.com

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

Captcha Code