PyTorch 和nn.Conv2d详解

发布于:2025-09-10 ⋅ 阅读:(20) ⋅ 点赞:(0)

1. 基本定义

torch.nn.Conv2d(
    in_channels,        # 输入通道数
    out_channels,       # 输出通道数(卷积核个数)
    kernel_size,        # 卷积核大小
    stride=1,           # 步长
    padding=0,          # 填充方式
    dilation=1,         # 空洞卷积系数
    groups=1,           # 分组卷积
    bias=True,          # 是否有偏置
    padding_mode='zeros', # 填充模式
    device=None,
    dtype=None
)

2. 数学原理

对于输入特征图 x∈RCin×H×Wx \in \mathbb{R}^{C_{in} \times H \times W}xRCin×H×W,二维卷积的输出为:

ycout(i,j)=∑c=1Cin(xc∗wcout,c)(i,j)+bcout y_{c_{out}}(i,j) = \sum_{c=1}^{C_{in}} (x_c * w_{c_{out},c})(i,j) + b_{c_{out}} ycout(i,j)=c=1Cin(xcwcout,c)(i,j)+bcout

其中:

  • CinC_{in}Cin:输入通道数
  • CoutC_{out}Cout:输出通道数(卷积核数量)
  • w∈RCout×Cin×kH×kWw \in \mathbb{R}^{C_{out} \times C_{in} \times k_H \times k_W}wRCout×Cin×kH×kW:卷积核权重
  • b∈RCoutb \in \mathbb{R}^{C_{out}}bRCout:偏置
  • * 表示 cross-correlation(互相关),PyTorch 中的卷积实际是 互相关 而非严格卷积。

3. 参数详解

参数 说明
in_channels 输入通道数。例如 RGB 图像 = 3
out_channels 输出通道数(卷积核数量),即 feature map 个数
kernel_size 卷积核大小,可以是 int(kH, kW)
stride 步长,控制滑动步幅,默认 1
padding 边缘填充,默认 0,可为 int/tuple 或 'same'
dilation 空洞卷积扩张系数,默认 1
groups 分组卷积:groups=1(普通卷积),groups=in_channels(深度卷积),groups>1(分组卷积)
bias 是否添加偏置,默认 True
padding_mode 填充模式:'zeros', 'reflect', 'replicate', 'circular'

4. 输入 / 输出形状

输入:

(N,Cin,Hin,Win) (N, C_{in}, H_{in}, W_{in}) (N,Cin,Hin,Win)

输出:

(N,Cout,Hout,Wout) (N, C_{out}, H_{out}, W_{out}) (N,Cout,Hout,Wout)

其中:

Hout=⌊Hin+2×padding[0]−dilation[0]×(kH−1)−1stride[0]+1⌋ H_{out} = \Bigg\lfloor \frac{H_{in} + 2 \times \text{padding}[0] - \text{dilation}[0] \times (k_H - 1) - 1}{\text{stride}[0]} + 1 \Bigg\rfloor Hout=stride[0]Hin+2×padding[0]dilation[0]×(kH1)1+1

Wout=⌊Win+2×padding[1]−dilation[1]×(kW−1)−1stride[1]+1⌋ W_{out} = \Bigg\lfloor \frac{W_{in} + 2 \times \text{padding}[1] - \text{dilation}[1] \times (k_W - 1) - 1}{\text{stride}[1]} + 1 \Bigg\rfloor Wout=stride[1]Win+2×padding[1]dilation[1]×(kW1)1+1


5. 权重与偏置

  • weight: [out_channels, in_channels/groups, kH, kW]
  • bias: [out_channels](若有)
conv = nn.Conv2d(3, 16, 3)
print(conv.weight.shape)  # torch.Size([16, 3, 3, 3])
print(conv.bias.shape)    # torch.Size([16])

6. 源码解析(PyTorch 2.x)

torch/nn/modules/conv.py(简化版):

class Conv2d(_ConvNd):
    def __init__(self, in_channels, out_channels, kernel_size, stride=1,
                 padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros',
                 device=None, dtype=None):
        kernel_size = _pair(kernel_size)
        stride = _pair(stride)
        padding = _pair(padding)
        dilation = _pair(dilation)
        super(Conv2d, self).__init__(
            in_channels, out_channels, kernel_size, stride, padding,
            dilation, False, _pair(0), groups, bias, padding_mode,
            device=device, dtype=dtype)

    def forward(self, input):
        return F.conv2d(
            input, self.weight, self.bias, self.stride,
            self.padding, self.dilation, self.groups)

核心点:

  • 参数封装到 _ConvNd 基类
  • 前向传播调用 F.conv2d(高效 C++/CUDA 实现)

7. 初始化方式

PyTorch 默认采用 Kaiming 正态初始化(He init):

  • 权重:

    N(0,2/fan_in) \mathcal{N}(0, \sqrt{2/fan\_in}) N(0,2/fan_in )

  • 偏置:均匀分布 [-bound, bound],其中 bound = 1/sqrt(fan_in)


8. 实际使用案例

(1) 基本卷积

conv = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1)
x = torch.randn(1, 3, 32, 32)  # batch=1, 3通道, 32x32图像
y = conv(x)
print(y.shape)  # torch.Size([1, 16, 32, 32])

(2) 卷积 + 激活 + 池化

model = nn.Sequential(
    nn.Conv2d(3, 16, 3, padding=1),
    nn.ReLU(),
    nn.MaxPool2d(2, 2)
)

(3) 分组卷积

# 深度卷积 (groups=in_channels)
depthwise = nn.Conv2d(32, 32, 3, padding=1, groups=32)

(4) 空洞卷积

dilated = nn.Conv2d(16, 16, 3, dilation=2, padding=2)

9. 常见技巧与注意事项

  1. 保持输入输出尺寸相同
    使用 padding="same"(PyTorch >= 1.10),或手动计算填充量:

    padding=kernel_size−12 padding = \frac{kernel\_size - 1}{2} padding=2kernel_size1

  2. 去掉偏置(如 BatchNorm 后面接卷积层时)

    nn.Conv2d(64, 128, 3, bias=False)
    
  3. 查看参数量

    sum(p.numel() for p in conv.parameters())
    
  4. 可视化卷积核(比如在图像任务中)

    kernels = conv.weight.detach().cpu()
    

10.综合示例:小型 CNN 分类器

import torch
import torch.nn as nn
import torch.nn.functional as F

class SimpleCNN(nn.Module):
    def __init__(self, num_classes=10):
        super(SimpleCNN, self).__init__()
        
        # 输入: (N, 3, 32, 32)  例如 CIFAR-10
        self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)  
        self.bn1   = nn.BatchNorm2d(16)  # 批归一化
        self.pool1 = nn.MaxPool2d(kernel_size=2, stride=2)  # 下采样一半

        # 输入: (N, 16, 16, 16)
        self.conv2 = nn.Conv2d(16, 32, 3, padding=1)
        self.bn2   = nn.BatchNorm2d(32)
        self.pool2 = nn.MaxPool2d(2, 2)

        # 输入: (N, 32, 8, 8)
        self.conv3 = nn.Conv2d(32, 64, 3, padding=1)
        self.bn3   = nn.BatchNorm2d(64)
        self.pool3 = nn.AdaptiveAvgPool2d((1, 1))  # 自适应全局平均池化到 (1,1)

        # 全连接层 (输入: 64, 输出: num_classes)
        self.fc = nn.Linear(64, num_classes)

    def forward(self, x):
        x = self.pool1(F.relu(self.bn1(self.conv1(x))))
        x = self.pool2(F.relu(self.bn2(self.conv2(x))))
        x = self.pool3(F.relu(self.bn3(self.conv3(x))))
        
        # 展平: (N, 64, 1, 1) → (N, 64)
        x = x.view(x.size(0), -1)
        x = self.fc(x)
        return x


## 测试网络
if __name__ == "__main__":
    model = SimpleCNN(num_classes=10)
    x = torch.randn(4, 3, 32, 32)   # batch=4, 3通道彩色图像
    y = model(x)
    print("输入:", x.shape)   # torch.Size([4, 3, 32, 32])
    print("输出:", y.shape)   # torch.Size([4, 10])

网络结构流程

  1. 输入: (N, 3, 32, 32)
  2. Conv1+BN+ReLU+MaxPool → (N, 16, 16, 16)
  3. Conv2+BN+ReLU+MaxPool → (N, 32, 8, 8)
  4. Conv3+BN+ReLU+AvgPool → (N, 64, 1, 1)
  5. Flatten + FC → (N, 10)

特点

  • 使用 Conv2d 提取局部特征
  • BatchNorm2d 加快收敛、防止过拟合
  • ReLU 激活非线性
  • MaxPool2d / AdaptiveAvgPool2d 控制特征图大小
  • Linear 映射到分类结果

总结

  • nn.Conv2d = 二维卷积层,本质是输入张量和可学习卷积核的互相关运算。
  • 参数控制卷积核大小、步长、填充、扩张、分组等。
  • 广泛应用于 CNN 特征提取、图像识别、目标检测等任务。
  • PyTorch 内部实现高效,支持 GPU/自动求导。