一文讲清 nn.Conv2d MaxPool2d 图像处理层

136 阅读5分钟

我们用生活化比喻 + 直观图解 + 简单代码 + 参数详解,向初学者彻底讲清楚 PyTorch 中最常用的两个图像处理层:

nn.Conv2d —— 卷积层(特征提取器)
nn.MaxPool2d —— 最大池化层(降维压缩器)


🎯 一句话总览:

Conv2d 像“扫描仪+特征探测器”,从图像中提取边缘、纹理等特征;
MaxPool2d 像“压缩器”,保留最强特征,缩小图像尺寸,减少计算量。

它们是构建 CNN(卷积神经网络)的基石!


一、预备知识:图像在计算机中长什么样?

一张彩色图像 = 一个 3D 张量 → 形状 [C, H, W]

  • C = 通道数(Channel)→ RGB 彩色图 = 3,灰度图 = 1
  • H = 高度(Height)
  • W = 宽度(Width)

📌 示例:一张 224x224 的 RGB 图片 → torch.Size([3, 224, 224])


🧱 1. nn.Conv2d —— 卷积层(Convolution Layer)

🧩 生活化比喻:

想象你拿着一个“小放大镜”(卷积核),在图像上从左到右、从上到下滑动扫描。每扫到一个位置,就把放大镜下的局部区域和放大镜上的“探测模板”做匹配,输出一个“匹配强度值”。

👉 多个放大镜 → 提取不同特征(边缘、角点、纹理…)
👉 输出 → 一堆“特征图”(Feature Maps)


📐 核心参数详解:

torch.nn.Conv2d(
    in_channels,   # 输入通道数(如 RGB=3)
    out_channels,  # 输出通道数(你想要多少个“放大镜”=多少个特征图)
    kernel_size,   # 卷积核大小(如 3x3, 5x5)
    stride=1,      # 步长:放大镜每次移动几步
    padding=0,     # 填充:在图像边缘补0,控制输出大小
    bias=True      # 是否加偏置
)

🖼️ 图解卷积过程(以 5x5 图像,3x3 卷积核,padding=0, stride=1 为例):

输入图像 (1通道):
[[1 2 3 4 5]
 [6 7 8 9 0]
 [1 2 3 4 5]
 [6 7 8 9 0]
 [1 2 3 4 5]]

卷积核 (3x3):
[[1 0 -1]
 [1 0 -1]
 [1 0 -1]]

👉 滑动计算 → 输出 3x3 特征图

🧑‍💻 代码示例:

import torch
import torch.nn as nn

# 创建一个卷积层:输入3通道(RGB),输出16个特征图,卷积核3x3
conv = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1)

# 模拟输入:batch=2, 3通道, 32x32 图像
x = torch.randn(2, 3, 32, 32)

# 前向计算
out = conv(x)
print(out.shape)  # torch.Size([2, 16, 32, 32])

📌 输出尺寸计算公式(H 和 W 相同)

输出尺寸 = (输入尺寸 + 2 * padding - kernel_size) / stride + 1

✅ 上例:(32 + 2*1 - 3)/1 + 1 = 32 → 尺寸不变!


💡 为什么用 padding=1?

  • 保持输入输出尺寸一致(常见于 ResNet 等结构)
  • 避免边缘信息丢失

🎨 卷积层的作用:

  • 提取局部特征(边缘、纹理、形状)
  • 参数共享 → 减少参数量
  • 平移不变性 → 物体在图中移动,仍能识别

🧱 2. nn.MaxPool2d —— 最大池化层(Max Pooling Layer)

🧩 生活化比喻:

想象你把图像划分成 2x2 的小格子,在每个格子里只保留最大的那个像素值,然后把整个格子“压缩”成一个点。

👉 效果:图像尺寸缩小,保留最显著特征,去除冗余,抗微小平移。


📐 核心参数:

torch.nn.MaxPool2d(
    kernel_size,  # 池化窗口大小(常用 2 或 3)
    stride=None,  # 步长(默认 = kernel_size)
    padding=0     # 填充
)

✅ 最常用:MaxPool2d(2) → 2x2 窗口,步长=2 → 图像长宽减半


🖼️ 图解最大池化(2x2 窗口,stride=2):

输入特征图:
[[1 3 2 1]
 [4 2 1 0]
 [1 5 3 2]
 [2 1 0 1]]

划分 2x2 区块:
区块1: [[1 3] → max=4
        [4 2]]
区块2: [[2 1] → max=2
        [1 0]]
区块3: [[1 5] → max=5
        [2 1]]
区块4: [[3 2] → max=3
        [0 1]]

输出:
[[4 2]
 [5 3]]  → 尺寸减半!

🧑‍💻 代码示例:

pool = nn.MaxPool2d(kernel_size=2, stride=2)  # 常用配置

# 输入:batch=2, 16通道, 32x32
x = torch.randn(2, 16, 32, 32)
out = pool(x)
print(out.shape)  # torch.Size([2, 16, 16, 16]) ← 长宽减半!

💡 池化层的作用:

  • 降维 → 减少后续计算量
  • 防止过拟合 → 丢弃部分细节
  • 增强平移不变性 → 物体稍微移动,池化后结果变化不大

🏗️ 组合使用:构建一个简单 CNN

import torch
import torch.nn as nn

class SimpleCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.features = nn.Sequential(
            nn.Conv2d(3, 32, kernel_size=3, padding=1),  # 32x32 → 32x32
            nn.ReLU(),
            nn.MaxPool2d(2),                             # 32x32 → 16x16

            nn.Conv2d(32, 64, kernel_size=3, padding=1), # 16x16 → 16x16
            nn.ReLU(),
            nn.MaxPool2d(2),                             # 16x16 → 8x8

            nn.Conv2d(64, 128, kernel_size=3, padding=1),# 8x8 → 8x8
            nn.ReLU(),
            nn.AdaptiveAvgPool2d(1)                      # 8x8 → 1x1
        )
        self.classifier = nn.Linear(128, 10)  # 分10类

    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size(0), -1)  # 展平: [batch, 128, 1, 1] → [batch, 128]
        x = self.classifier(x)
        return x

model = SimpleCNN()
x = torch.randn(4, 3, 32, 32)
out = model(x)
print(out.shape)  # torch.Size([4, 10])

🆚 对比总结:

作用是否有可学习参数输出尺寸变化常用配置
Conv2d提取局部特征✅ 有权重和偏置可控(靠padding)kernel=3, padding=1
MaxPool2d降维、保留最强特征❌ 无参数通常减半kernel=2, stride=2

🧠 给初学者的贴心提示:

  1. ✅ 卷积层是“智能特征提取器”,池化层是“压缩降噪器”
  2. padding=1, kernel_size=3 是黄金搭档 → 保持尺寸不变
  3. MaxPool2d(2) 是最常用池化 → 尺寸减半,计算量降为1/4
  4. ✅ 卷积后通常接 ReLU() 激活函数 → 增加非线性
  5. ✅ 调试时打印 .shape 看每层输出尺寸是否符合预期!

🎁 小挑战(巩固理解):

假设输入图像尺寸是 [3, 64, 64],经过以下层:

nn.Conv2d(3, 16, 5, stride=2, padding=2)
nn.MaxPool2d(3, stride=2, padding=1)

👉 输出尺寸是多少?

解答:

  • 卷积后:H = (64 + 2*2 - 5)/2 + 1 = (64+4-5)/2+1 = 63/2+1 = 31.5+1 → ❌ 不行!
    → 实际 PyTorch 会向下取整 → (64+4-5)//2 + 1 = 31 + 1 = 32

  • 池化后:H = (32 + 2*1 - 3)/2 + 1 = (32+2-3)/2 +1 = 31/2 +1 = 15.5+1 → 向下取整 = 15+1 = 16

✅ 最终输出:[16, 16, 16]

💡 公式记住:output = floor( (input + 2*pad - kernel) / stride ) + 1


🎉 恭喜你!现在你已经掌握了 CNN 最核心的两个构建块!