卷积操作记录(pytorch)

最近复现很多DL算法(尤其是涉及到多尺度的情况),经常使用卷积操作,输入参数还需调整,很容易出现维度对不上的情况。方法比较老,有些操作细节不太记得了,这里整理一下。主要整理nn.Conv2d与nn.ConvTranspose2d(转置卷积)操作。

nn.Conv2d的使用

nn.Conv2d 实现二维卷积运算,核心作用是从输入的二维特征图中提取局部特征(比如边缘、纹理、形状等),是卷积神经网络(CNN)的基础组件。可以把卷积理解为:用一个 "过滤器(卷积核)" 在输入图像上滑动,逐位置计算加权求和,最终得到新的特征图

nn.Conv2d 的初始化参数如下:

python 复制代码
torch.nn.Conv2d(
    in_channels: int,          # 输入通道数(比如RGB图是3,灰度图是1)
    out_channels: int,         # 输出通道数(卷积核的数量)
    kernel_size: Union[int, Tuple[int, int]],  # 卷积核大小(如3表示3x3,(3,5)表示3行5列)
    stride: Union[int, Tuple[int, int]] = 1,   # 步长(卷积核滑动的步幅,默认1)
    padding: Union[int, Tuple[int, int], str] = 0,  # 填充(边缘补0,默认0;也可设'same'保持尺寸)
    dilation: Union[int, Tuple[int, int]] = 1,  # 空洞卷积的扩张率(默认1,普通卷积)
    groups: int = 1,           # 分组卷积的组数(默认1,普通卷积)
    bias: bool = True,         # 是否添加偏置项(默认True)
    padding_mode: str = 'zeros'# 填充模式(默认补0,还有'reflect'/'replicate'等)
)

基础使用示例:

python 复制代码
import torch
import torch.nn as nn

# 1. 定义一个Conv2d层:输入3通道(RGB),输出16通道,卷积核3x3,步长1, padding=1(same padding)
conv_layer = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)

# 2. 构造模拟输入:batch_size=2,3通道,高宽为32x32(比如2张32x32的RGB图片)
# 输入形状:[batch_size, in_channels, height, width]
input_tensor = torch.randn(2, 3, 32, 32)

# 3. 执行卷积运算
output_tensor = conv_layer(input_tensor)

# 4. 打印输入输出形状
print(f"输入形状: {input_tensor.shape}")   # 输出:torch.Size([2, 3, 32, 32])
print(f"输出形状: {output_tensor.shape}")  # 输出:torch.Size([2, 16, 32, 32])(padding=1+stride=1,尺寸不变)

这里说明一下输出尺寸计算:
(1)无空洞卷积

示例验证:

  • 输入尺寸 32,kernel_size=3,padding=1,stride=1:
    (32+2∗1−3)/1+1=32 → 输出尺寸 32(和输入一致,即 same padding)。
  • 若 padding=0,stride=2:向下取整 → 输出尺寸 16。

(2)有空洞卷积

空洞卷积的输出尺寸公式需要加入 dilation 参数,完整公式:

示例验证(空洞卷积)

python 复制代码
import torch
import torch.nn as nn

# ========== 1. 定义普通卷积(dilation=1)和空洞卷积(dilation=2) ==========
# 普通卷积:3x3核,无空洞
conv_normal = nn.Conv2d(
    in_channels=3, out_channels=16,
    kernel_size=3, stride=1, padding=1, dilation=1
)

# 空洞卷积:3x3核,dilation=2(扩张率2)
# 注意:空洞卷积的padding需要对应调整,否则尺寸会缩小
conv_dilated = nn.Conv2d(
    in_channels=3, out_channels=16,
    kernel_size=3, stride=1, padding=2, dilation=2  # padding=2 保持尺寸不变
)

# ========== 2. 构造输入(单通道、10x10的特征图) ==========
# 输入形状:[batch_size, in_channels, H, W]
input_tensor = torch.randn(2, 3, 32, 32)

# ========== 3. 执行卷积 ==========
out_normal = conv_normal(input_tensor)
out_dilated = conv_dilated(input_tensor)

# ========== 4. 打印结果 ==========
print(f"输入形状: {input_tensor.shape}")       # torch.Size([2, 3, 32, 32])
print(f"普通卷积输出形状: {out_normal.shape}")  # torch.Size([2, 16, 32, 32])
print(f"空洞卷积输出形状: {out_dilated.shape}") # torch.Size([2, 16, 32, 32])

# 查看参数量(两者一致,因为卷积核尺寸都是3x3)
print(f"\n普通卷积参数量: {sum(p.numel() for p in conv_normal.parameters())}")  
print(f"空洞卷积参数量: {sum(p.numel() for p in conv_dilated.parameters())}")  

nn.ConvTranspose2d的使用

nn.ConvTranspose2d(转置卷积层),它也常被称为 "反卷积"(虽然这个叫法并不严谨),核心作用是对二维特征图进行上采样(放大尺寸),是图像分割、生成对抗网络(GAN)等需要恢复高分辨率特征图任务的核心组件。

nn.ConvTranspose2d 并不是卷积的逆运算(不能完美还原卷积前的输入),而是通过 "补零 + 跨步卷积" 的方式实现特征图的尺寸放大。可以简单理解为:

  • 普通卷积(nn.Conv2d):下采样(缩小特征图,提取特征);
  • 转置卷积(nn.ConvTranspose2d):上采样(放大特征图,恢复空间维度)。

nn.ConvTranspose2d的核心参数

python 复制代码
torch.nn.ConvTranspose2d(
    in_channels: int,          # 输入通道数(和普通卷积一致)
    out_channels: int,         # 输出通道数(和普通卷积一致)
    kernel_size: Union[int, Tuple[int, int]],  # 卷积核大小
    stride: Union[int, Tuple[int, int]] = 1,   # 步长(决定上采样倍数)
    padding: Union[int, Tuple[int, int]] = 0,  # 输入边缘填充(注意:作用和普通卷积相反)
    output_padding: Union[int, Tuple[int, int]] = 0,  # 输出边缘补零(解决尺寸对齐问题)
    groups: int = 1,           # 分组卷积组数
    bias: bool = True,         # 是否加偏置
    dilation: Union[int, Tuple[int, int]] = 1,  # 空洞卷积扩张率
    padding_mode: str = 'zeros'# 填充模式
)

⚠️ 关键区别:output_padding 是转置卷积独有的参数,用于微调输出尺寸,解决步长 > 1 时的尺寸歧义问题。

使用示例:(通过代码对比普通卷积和转置卷积的尺寸变化)

python 复制代码
import torch
import torch.nn as nn

# ========== 1. 普通卷积(下采样) ==========
# 定义:输入1通道,输出1通道,3x3核,步长2,padding=1
conv = nn.Conv2d(in_channels=1, out_channels=1, kernel_size=3, stride=2, padding=1)
# 构造输入:1个样本,1通道,4x4特征图
x = torch.randn(1, 1, 4, 4)
# 普通卷积输出(下采样)
x_conv = conv(x)
print(f"普通卷积输入形状: {x.shape}")       # torch.Size([1, 1, 4, 4])
print(f"普通卷积输出形状: {x_conv.shape}")  # torch.Size([1, 1, 2, 2])(步长2导致尺寸减半)

# ========== 2. 转置卷积(上采样) ==========
# 定义:输入1通道,输出1通道,4x4核,步长2,padding=1,output_padding=0
conv_trans = nn.ConvTranspose2d(
    in_channels=1, out_channels=1,
    kernel_size=4, stride=2, padding=1, output_padding=0
)
# 对普通卷积的输出做转置卷积(上采样)
x_trans = conv_trans(x_conv)
print(f"转置卷积输入形状: {x_conv.shape}")  # torch.Size([1, 1, 2, 2])
print(f"转置卷积输出形状: {x_trans.shape}") # torch.Size([1, 1, 4, 4])(还原回4x4)

输出结果:

转置卷积的输出尺寸公式是普通卷积的 "逆运算",完整公式:

常见场景的参数搭配(快速上采样)

注意:转置卷积虽然好用,但存在棋盘格效应(Checkerboard Artifact) :由于卷积核的重叠不均,上采样后的特征图会出现类似棋盘格的伪影,这在 GAN 生成图像时尤其明显。

更优的上采样方式:插值 + 普通卷积:先用torch.nn.functional.interpolate 做插值上采样(如双线性插值),再用 1x1 或 3x3 卷积调整通道,避免棋盘格效应。

python 复制代码
# 更优的上采样方式:插值 + 卷积
x = torch.randn(1, 1, 2, 2)
# 第一步:双线性插值上采样2倍
x_up = nn.functional.interpolate(x, scale_factor=2, mode='bilinear', align_corners=False)
# 第二步:普通卷积调整特征
conv_1x1 = nn.Conv2d(1, 1, kernel_size=1)
x_final = conv_1x1(x_up)
print(x_final.shape)  # torch.Size([1, 1, 4, 4])

参考资料

  1. 空洞卷积(Dilated Convolution)详解、代码实现与应用
  2. 抽丝剥茧,带你理解转置卷积(反卷积)
  3. 关于上采样方法总结(插值和深度学习)
相关推荐
无忧智库几秒前
数据安全与数据合规体系建设规划:从“合规应付”到“数据可控、可用、可审计”的落地指南(PPT)
大数据·人工智能·安全
沐风___3 分钟前
DESIGN.md:把 AI 生成 UI 从凭感觉变成可验证工程
人工智能
玩美数据7 分钟前
2026全球出海市场研究TOP5:深度行业洞察与高效供应商网络塑造市场竞争力
大数据·人工智能
中科岩创14 分钟前
全栈本地化专属部署架构,服务智慧矿山信息化安全建设!
大数据·人工智能·物联网
数智前线15 分钟前
Forrester最新报告发布:OceanBase成唯一入选中国厂商
人工智能
葡萄城技术团队15 分钟前
AI 不会淘汰电子表格,反而会让电子表格成为 AI 操作系统
前端·人工智能
monsion16 分钟前
AG-UI 协议详解:Agent 与用户之间缺失的那一层
人工智能·ui·个人开发
Token炼金师19 分钟前
千卡月崩五次:loss spike、硬件故障、checkpoint 与断点续训 —— 训练可靠性的生死线
人工智能·深度学习·llm
有Li21 分钟前
通过渐进式域变换实现组织分类的无监督单域泛化文献速递/基于多模态的医学影像分割与理解
人工智能·学习·分类·数据挖掘·文献·医学生
Token炼金师23 分钟前
指令的塑形:从野生语言模型到听话助手 —— 监督微调 SFT 全链路
人工智能·深度学习·llm