Pytorch深入浅出(四)之nn网络层(卷积)

上一篇笔记我们从宏观地学习了如何“搭建”模型,这次笔记深入剖析网络内部的具体网络层,从微观拆解。

一、卷积层

1.直观理解
卷积运算本质上是特征提取的过程。

  • 卷积核: 又称为过滤器、滤波器,可以把它想象成一个“手电筒”或“探测器”。它包含了一组权重,代表某种特定的纹理或特征(如边缘、圆弧)。
  • 卷积运算: 这个“手电筒”在输入(信号)图像上滑动,相应位置上进行乘加
  • 卷积维度:一般,卷积核在几个维度上滑动,就是几维卷积,如下图所示。

在这里插入图片描述
💡 关键理解:多通道卷积
上图有个小问题,没有考虑通道数,图中所示的都是一个通道的情况,比如二维灰度图像,通道数为1,如果是三通道的RGB二维图像呢?

  • 输入: 如果输入是 RGB 图像(3通道),那么每一个卷积核也必须有 3 个通道(深度)。
  • 运算: 3个通道分别进行卷积运算,产生 3 个中间矩阵,然后将它们相加,再加上一个偏置 (Bias),最终得到一张单通道的特征图 (Feature Map)。
  • 输出: 如果想要输出 N N N 个通道的特征图,就需要 N N N 个这样的卷积核。

2.nn.Conv2d核心参数详解
在 PyTorch 中,Conv2d 是实现二维卷积的类,其参数决定了卷积的行为:

参数名含义详细说明
in_channels输入通道数必须与输入数据的通道数一致(如 RGB 图像为 3)
out_channels输出通道数也就是卷积核的个数,决定了提取多少种特征
kernel_size卷积核尺寸3 代表 3 × 3 3\times3 3×3(3,5) 代表高3宽5
stride步长滑动的跨度。默认为 1。步长越大,输出尺寸越小
padding填充在输入边缘填充像素(通常填0)。用于保持输出尺寸或处理边缘信息
padding_mode填充模式有四种:‘zeros’、‘reflect’、‘replicate’、‘circular’,默认为‘zeros’
dilation空洞/扩张卷积核元素之间的间距。用于扩大感受野(Receptive Field)
groups分组卷积默认为 1。设为 in_channels 时即为深度可分离卷积
bias偏置默认为True

有一个需要注意的地方:
在这里插入图片描述
对于kernel_size,stride,padding和dilation四个参数,如果是一个值(int类),则对H和W两个维度都是这个值;如果是两个int组成的元组,则第一个数对应H维度,第二个数对应W维度,举例理解:

conv1 = nn.Conv2d(16, 33, 3, stride=2)
# 这里kernel_size等于一个值3,即H维和W维的尺寸都是3,等价于(3, 3);stride等于一个值2,即滑动时在H和W两个维度上步长都是2
conv2 = nn.Conv2d(16, 33, (3, 5), stride=(2, 1), padding=(4, 2))
# 这个卷积层,kernel_size为(3, 5),即H维是3,W维是5,即代表大小为3×5的卷积核;步长为(2, 1),即H维上滑动步长为2,W维上滑动步长为1;padding为(4, 2),即H维(上下各)填充4个像素,W维(左右各)填充2个像素。

3.卷积参数对输出尺寸的影响

  • kernel_size、stride、padding等参数会改变特征图的尺寸
  • out_channels会改变通道数
    直观上看看:
    在这里插入图片描述
    一般来说,H=W,那么 输出尺寸 = f l o o r [ (输入长度 − 核大小 + 2 × p a d d i n g ) / s t r i d e + 1 ] , f l o o r 代表向下取整 输出尺寸=floor[(输入长度 - 核大小 + 2 × padding) / stride + 1],floor代表向下取整 输出尺寸=floor[(输入长度核大小+2×padding/stride+1]floor代表向下取整,那H≠W,输出尺寸见如下公式:
    在这里插入图片描述
import torch
import torch.nn as nn

# 模拟一张图片:Batch=256, Channel=3, H=64, W=64
x = torch.randn(256, 3, 64, 64)

# 【情况1】改变通道数
# 输入3通道 -> 输出6通道 (需要6个卷积核)
conv_ch = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=3, padding=1)
print(f"{conv_ch(x).shape}")     # torch.Size([256, 6, 64, 64])

# 【情况2】下采样 (改变特征图大小)
# stride=2, 特征图尺寸减半
conv_stride = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=2, padding=1)
print(f"{conv_stride(x).shape}") # torch.Size([256, 6, 32, 32])
# stride=2,padding=2
conv_sp = nn.Conv2d(in_channels=3, out_channels=6, kernel_size=3, stride=2, padding=2)
print(f"{conv_sp(x).shape}")     # torch.Size([256, 6, 33, 33])

4.直观再理解
用一个例子来形象地体现卷积层的作用:

image_path = r"D:/Photos\desktop photos/t01662406687cf7219b.jpg"
img = Image.open(image_path).convert('RGB')
  
transform = transforms.Compose([transforms.ToTensor()])
img_tensor = transform(img)
x = img_tensor.unsqueeze_(dim=0)
  
conv = nn.Conv2d(3, 1, 3)
nn.init.xavier_normal_(conv_layer.weight.data)
  
img_conv = conv(img_tensor)

with torch.no_grad():
    out = conv_layer(x)          # 形状 (1,1,H,W)
  
# 取出并归一化到 0~1
out = out.squeeze().cpu().numpy()
out = (out - out.min()) / (out.max() - out.min() + 1e-8)
plt.figure(figsize=(10,6))
plt.subplot(1,2,1)
plt.imshow(img)
plt.axis('off')
plt.subplot(1,2,2)
plt.imshow(out)
plt.axis('off')
plt.show()

左边是原图,右边是卷积之后的结果。卷积后的结果,其实就是某一种模式或者说是特征图,我们将卷积核内的权重换一下,就可以得到另一种不同的特征图:
在这里插入图片描述
在这里插入图片描述

二、转置卷积

1.直观理解
如果说卷积层是“特征提取”与“尺寸压缩”,那么转置卷积层(Transposed Convolution)本质上是尺寸恢复上采样的过程。

  • 功能定位: 常用于语义分割(恢复原图大小)或生成对抗网络(GAN,从噪音生成图片)。可以把它想象成一个“投影仪”或“幻灯机”,将小尺寸的图像放大投射成大尺寸的图像。
  • 转置运算: 它并不是卷积的数学逆运算(即无法恢复原始数值),而是尺寸上的逆变换。其内部通过在输入元素之间插入零(Zero Padding) 来扩大尺寸,然后再进行普通的卷积操作。
    在这里插入图片描述
    💡 关键理解:棋盘效应
    由于转置卷积是通过插入零并通过卷积核滑动生成的,如果步长(stride)和卷积核大小(kernel_size)配合不好,输出图像上会出现明显的网格状伪影,就像棋盘一样。这是转置卷积特有的常见问题。

2.nn.ConvTranspose2d参数详解
在 PyTorch 中,ConvTranspose2d 是实现二维转置卷积的类。它的参数名与 Conv2d 几乎一样,但作用逻辑往往是相反的
在这里插入图片描述
有一个需要注意的:
参数的反向理解:在普通卷积中,stride > 1 会导致尺寸缩小;而在转置卷积中,stride > 1 会导致尺寸变大。在普通卷积中,padding > 0 会导致“输入”变大(从而维持输出尺寸);而在转置卷积中,padding 参数参与公式运算的效果是让“输出”变小(相当于Crop操作)。

3.卷积参数对输出尺寸的影响

  • stride、kernel_size、padding会改变尺寸大小
  • output_padding 用于填补计算带来的微小空缺
    直观上看看:
    在这里插入图片描述
    对一个 4×4 的输入进行 3×3 的转置卷积操作
    尺寸变换: [ 4 × 4 ] ── T r a n s C o n v ( k = 3 ) ── > [ 6 × 6 ] [4×4] ──TransConv(k=3)──> [6×6] [4×4]──TransConv(k=3)──>[6×6]
    等同于对一个 2×2 的输入(在两侧添加 2×2 的零填充)进行 3×3 核心卷积
import torch
from torch import nn
# 模拟特征图:Batch=1, Channel=3, H=4, W=4
x = torch.randn(1, 3, 4, 4)
ConvTrans2d = nn.ConvTranspose2d(3, 3, kernel_size=3) # torch.Size([1, 3, 6, 6])
print(f"{ConvTrans2d(x).shape}")

下图是公式,它的输入输出尺寸的关系与卷积层正好相反,可以发现:stride是放大倍数的主导因素,padding用于微调(缩小)边缘
在这里插入图片描述

import torch
from torch import nn

# 模拟特征图:Batch=1, Channel=3, H=32, W=32
x = torch.randn(1, 3, 32, 32)

# 【情况1】尺寸翻倍 (最经典用法)
# kernel_size=2, stride=2
# (32-1) × 2 + (2-1) + 1
ConvTrans2d = nn.ConvTranspose2d(3, 5, kernel_size=2, stride=2) # torch.Size([1, 5, 64, 64])
print(f"{ConvTrans2d(x).shape}")

# 【情况2】尺寸增加
# stride=1,kernel_size大小改变尺寸大小
# (32-1) × 1 + (3-1) + 1
ConvTrans2d = nn.ConvTranspose2d(3, 3, kernel_size=3, stride=1) # torch.Size([1, 3, 34, 34])
print(f"{ConvTrans2d(x).shape}")
# (32-1) × 1 + (5-1) + 1
ConvTrans2d = nn.ConvTranspose2d(3, 3, kernel_size=5, stride=1) # torch.Size([1, 3, 36, 36])
print(f"{ConvTrans2d(x).shape}")

# 【情况3】有填充
# kernel_size=2, stride=1,padding多1,尺寸小2
# (32-1) × 1 - 2 × 1 + (2-1) + 1
ConvTrans2d = nn.ConvTranspose2d(3, 3, kernel_size=2, stride=1, padding=1) # torch.Size([1, 3, 31, 31])
print(f"{ConvTrans2d(x).shape}")
# (32-1) × 1 - 2 × 4 + (2-1) + 1
ConvTrans2d = nn.ConvTranspose2d(3, 3, kernel_size=2, stride=1, padding=4) # torch.Size([1, 3, 25, 25])
print(f"{ConvTrans2d(x).shape}")

4.直观再理解
最后来看一个转置卷积的例子,直观再理解

image_path = r"D:/Photos\desktop photos/t01662406687cf7219b.jpg"
img = Image.open(image_path).convert('RGB')
  
img_transform = transforms.Compose([transforms.ToTensor()])
img_tensor = img_transform(img)
x = img_tensor.unsqueeze_(dim=0)
  
conv_layer = nn.ConvTranspose2d(3, 1, 3, stride=2)
nn.init.xavier_normal_(conv_layer.weight.data)
  
img_conv = conv_layer(img_tensor)

with torch.no_grad():
    out = conv_layer(x)
  
out = out.squeeze().cpu().numpy()
out = (out - out.min()) / (out.max() - out.min() + 1e-8)
plt.figure(figsize=(10,6))
plt.subplot(1,2,1)
plt.imshow(img)
plt.subplot(1,2,2)
plt.imshow(out)
plt.show()

可以得到尺寸更大的特征图
在这里插入图片描述

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值