首页人工智能Pytorch【深度学习(PyTorch...

【深度学习(PyTorch篇)】33.卷积层(Convolutional Layer)

本系列文章配套代码获取有以下两种途径:

  • 通过百度网盘获取:
链接:https://pan.baidu.com/s/1XuxKa9_G00NznvSK0cr5qw?pwd=mnsj 提取码:mnsj
  • 前往GitHub获取
https://github.com/returu/PyTorch





01

卷积层


卷积层是卷积网络的核心构件,主要执行一个叫做”卷积”的数学运算操作,在这个计算过程中,我们首先会定义一个卷积内核,然后将该卷积内核在图像上滑动,进行卷积运算。卷积运算的本质就是将卷积内核与图像的像素点进行矩阵相乘,将结果值相加,得到输出。

02

Conv2d


Pytorch中,卷积层主要通过torch.nn模块下的Conv1dConv2dConv3d等类实现,用于处理不同维度的数据。

  • torch.nn.Conv1d:一维卷积层,主要用于时间序列数据,如心电图(ECG)信号。

  • torch.nn.Conv2d:二维卷积层,主要用于图像数据,如RGB图像。

  • torch.nn.Conv3d:三维卷积层,主要用于体数据和视频数据医学成像数据(MRI和CT扫描)。

本次以最常使用的torch.nn.Conv2d为例,其构造函数如下:

torch.nn.Conv2d(in_channels, 
                out_channels, 
                kernel_size, 
                stride=1
                padding=0
                dilation=1
                groups=1
                bias=True
                padding_mode='zeros')
其中:
  • in_channels (整数):输入图像的通道数(维度)。例如,RGB图像有3个通道,灰度图像有1个通道。
  • out_channels (整数):输出特征图的通道数,即卷积核的数量。输出图像的通道越多,表示网络的容量越大,借助这些通道能够检测到许多不同类型的特征。
  • kernel_size (整数或元组):卷积核的大小。可以是一个整数(例如,3),表示卷积核的高度和宽度都是该数值;也可以是一个元组(例如,(3, 5)),分别表示卷积核的高度和宽度。
  • stride (整数或元组,可选):卷积步长,默认为1。步长定义了卷积核在图像上移动的速度。与kernel_size类似,它可以是一个整数或一个表示高度和宽度步长的元组。
  • padding (整数或元组,可选):在输入图像周围填充的零的数量,默认为0。填充可以控制输出特征图的大小。它也可以是一个整数或一个表示高度和宽度填充的元组。
  • dilation (整数或元组,可选):卷积核元素之间的间距,默认为1。当dilation大于1时,表示进行空洞卷积(也称为扩张卷积),这可以增加卷积核的感受野而不增加参数数量。
  • groups (整数,可选):控制输入和输出之间的连接。默认为1,表示所有输入通道都连接到每个输出通道。如果groups等于in_channelsout_channels,那么每个输入通道只与对应的输出通道相连,这被称为分组卷积。
  • bias (布尔值,可选):是否添加偏置项到输出中,默认为True。如果设置为False,则不会添加偏置项。
  • padding_mode (字符串,可选):指定填充模式,默认为‘zeros’。其他可选模式包括‘reflect’‘replicate’‘circular’。这些模式决定了如何填充输入图像的边界。

在卷积操作中需要知道输出结果的形状,以便设计后续网络结构。卷积操作后的输出形状取决于几个因素:输入的尺寸、卷积核的尺寸、步长(stride)、填充(padding)以及是否使用了dilation

假设输入张量的尺寸为 (N, C_in, H_in, W_in),其中 N 是批量大小,C_in 是输入的通道数,H_inW_in 分别是输入的高度和宽度。

卷积层的参数为:

  • kernel_size (k_h, k_w):卷积核的高度和宽度。
  • stride (s_h, s_w):卷积核在输入上移动的步长。
  • padding (p_h, p_w):在输入的边界添加的零填充的数量。
  • dilation (d_h, d_w):卷积核元素之间的间距。

输出的高度 H_out 和宽度 W_out 可以按照以下公式计算(⌊ ⌋ 表示向下取整):

因此,输出张量的完整形状将是 (N, C_out, H_out, W_out),其中 N 是批量大小,C_out 是输出的通道数,由卷积层的 out_channels 参数决定。
03

卷积实战


  • 1、随机卷积核:

本次以经典的Lena图作为处理图像。
lena = Image.open('lena.png')
# 将其转换为PyTorch张量
to_tensor = transforms.ToTensor()
lena_tensor = to_tensor(lena).unsqueeze(0)

Lena图原名为“Lenna”,其原型是瑞典模特莱娜·瑟德贝里(Lena Söderberg)。这张图片最初出现在1972年11月的《花花公子》杂志上。由于该图像在视觉上的吸引力和其细节的丰富性,后来被广泛地用作数字图像处理和计算机视觉研究中的标准测试图。

在这个例子中,原始图像是200×200,由于使用了5×5的卷积核,且步长为1(默认),填充为0(默认)。所以,卷积后的图像大小是196×196。由于有3个输出通道,所以最终的输出形状是(1, 3, 196, 196)。这里的1表示批处理大小(batch size)。
# 创建一个卷积层
# 这里使用5x5的卷积核,输入通道为1,输出通道为3,其他参数使用默认值
conv = nn.Conv2d(1,3,(5,5))

# 权重张量和偏置张量的大小
conv.weight.shape , conv.bias.shape

# 对图像张量进行卷积操作 
out = conv(lena_tensor)

# 输出卷积后的结果形状  
print("Output shape:", out.shape)
# Output shape: torch.Size([1, 3, 196, 196])
通过matplotlib对输出的三个特征图进行可视化:
to_pil = transforms.ToPILImage()

out1 = to_pil(out1.squeeze(0))
out2 = to_pil(out2.squeeze(0))
out3 = to_pil(out3.squeeze(0))

# 创建一个一行三列的子图布局  
fig, axs = plt.subplots(nrows=1, ncols=3, figsize=(155))  

# 特征图1  
axs[0].imshow(out1,cmap='gray')  
axs[0].set_title('channel_1')  
axs[0].axis('off')  # 关闭坐标轴  

# 特征图2
axs[1].imshow(out2,cmap='gray')  
axs[1].set_title('channel_2')  
axs[1].axis('off'

# 特征图3
axs[2].imshow(out3,cmap='gray')  
axs[2].set_title('channel_3')  
axs[2].axis('off'
可视化结果如下图所示:
需要注意的是,在本例中卷积核的权重和偏置都是随机初始化的,因此输入的特征图并没有特别的意义。

  • 2、锐化卷积核:
我们可以使用卷积操作完成特征操作,例如可以创建一个简单的卷积层,用于图像锐化。
要完成操作需要使用到锐化卷积核,其主要作用是强调中心像素与其邻近像素之间的差异。即,它会增加中心像素的权重,同时减少其周围像素的权重。这种操作有助于突出图像中的边缘和细节,因为它增强了中心像素与周围像素之间的对比度。
# 锐化卷积核
kernel = torch.full([3,3],-1.) / 9
kernel[1 ,1] = 1

# 创建一个卷积层
conv = nn.Conv2d(1,1,(3,3),1,bias=False)

# 设置卷积层的权重
conv.weight.data = kernel.reshape(1,1,3,3)

# 对图像张量进行卷积操作 
out = conv(lena_tensor)

# 输出卷积后的结果形状  
print("Output shape:", out.shape)
# Output shape: torch.Size([1, 1, 198, 198])

# 可视化特征图
to_pil = transforms.ToPILImage()
to_pil(out.squeeze(0))
可视化结果如下图所示:

  • 3、填充边界:
padding参数的作用是在输入张量的边缘添加额外的像素(通常是零值),以控制卷积操作后输出张量的尺寸。这在深度学习中非常重要,因为它可以确保输出的尺寸符合预期,特别是当你希望保持输出尺寸与输入尺寸一致时。
使用padding的一个重要场景是构建残差网络(ResNet)或U-Net这样的架构,其中需要保持尺寸不变以便进行跳跃连接(skip connections)。此外,padding还有助于保留输入边缘的信息,否则这些信息可能会在卷积过程中被“裁剪”掉,因为卷积核不会完全覆盖输入张量的边缘部分。

padding参数可以接受几种形式的输入:

  • 整数:表示在所有四个边缘(顶部、底部、左侧、右侧)都添加相同数量的零填充。
  • 元组:如果提供两个整数的元组,第一个整数表示垂直方向(高度)的填充,第二个整数表示水平方向(宽度)的填充。如果提供四个整数的元组,则它们分别对应于(左、右、顶、底)的填充。

假设我们有一个3×3的输入矩阵,然后使用padding=1,那么首先会在输入矩阵的四周各添加一个像素宽的零填充,得到一个新的5×5矩阵,然后在进行卷积操作。

在下面的示例中,由于设置了填充为2,这意味着在原始图像的边界周围添加了两层零填充。当使用5×5的卷积核进行卷积时,填充会“抵消”卷积核造成的空间尺寸减小。因此,输出特征图的尺寸与输入图像相同,即200×200。
# 创建一个卷积层
# 这里使用5x5的卷积核,输入通道为1,输出通道为3,填充为2,其他参数使用默认值
conv = nn.Conv2d(1,3,(5,5),padding=2)

# 对图像张量进行卷积操作 
out = conv(lena_tensor)

# 输出卷积后的结果形状  
print("Output shape:", out.shape)
# Output shape: torch.Size([1, 3, 200, 200])


更多内容可以前往官网查看

https://pytorch.org/


本篇文章来源于微信公众号: 码农设计师

RELATED ARTICLES

欢迎留下您的宝贵建议

Please enter your comment!
Please enter your name here

- Advertisment -

Most Popular

Recent Comments