共计 2641 个字符,预计需要花费 7 分钟才能阅读完成。
为什么需要卷积神经网络?
传统全连接网络在处理图像时会遇到两个致命问题:
- 参数爆炸:一张 100×100 的 RGB 图像展平后就是 30,000 维的输入向量,假设第一个隐藏层有 1000 个神经元,仅这一层就需要 3000 万个参数!
- 平移不变性缺失:全连接网络难以理解 ” 猫在图像左上角 ” 和 ” 猫在图像右下角 ” 其实是同一个概念
这就像要求快递员记住每户人家的全部家具摆放位置才能送货,而人类视觉系统其实是通过局部感受野(Receptive Field)来分层理解图像的——这正是 CNN 的生物学灵感来源。
卷积 vs 全连接的数学本质
参数量对比
假设处理 $32\times32$ 的 CIFAR-10 图像:
- 全连接层:输入 1024 维,输出 512 维时,参数量为 $1024\times512=524,288$
- 卷积层:使用 $3\times3$ 卷积核,输出通道 64 时,参数量仅 $3\times3\times3\times64=1,728$
数学表达式对比:
- 全连接:$y = W_{1024\times512}x + b$
- 卷积:$y_{i,j,k} = \sum_{l,m,n} x_{i+l-1,j+m-1,n} \cdot w_{l,m,n,k} + b_k$

PyTorch 实现核心组件
import torch
import torch.nn as nn
# 构建一个包含卷积、激活、池化的最小 CNN
model = nn.Sequential(# 输入[B,3,32,32] → 输出[B,16,32,32]
nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3,
padding=1), # NOTE: padding= 1 保持尺寸不变
nn.ReLU(),
# 输入[B,16,32,32] → 输出[B,16,16,16]
nn.MaxPool2d(kernel_size=2),
# 第二层卷积
nn.Conv2d(16, 32, 3, padding=1), # 自动推导 in_channels
nn.ReLU(),
nn.MaxPool2d(2),
# 展平后接全连接层
nn.Flatten(),
nn.Linear(32*8*8, 10) # CIFAR-10 有 10 类
)
关键参数实验:
- kernel_size:尝试 5 ×5 会捕获更大局部特征但计算量增加
- stride:设为 2 时输出尺寸减半,可替代池化层
- padding:
'same'等效于padding=kernel_size//2
CIFAR-10 实战训练
import torchvision
transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize((0.5,0.5,0.5), (0.5,0.5,0.5))
])
trainset = torchvision.datasets.CIFAR10(root='./data', train=True,
download=True, transform=transform)
trainloader = torch.DataLoader(trainset, batch_size=64, shuffle=True)
# 训练循环关键代码
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
for epoch in range(10):
for images, labels in trainloader:
outputs = model(images)
loss = criterion(outputs, labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()
典型训练曲线:
新手避坑指南
特征图尺寸计算
牢记公式:
$$
W_{out} = \left\lfloor\frac{W_{in} + 2P – F}{S}\right\rfloor + 1
$$
- $F$: 卷积核大小
- $P$: padding 数
- $S$: stride 步长
当除不尽时 PyTorch 默认向下取整,可能导致尺寸不匹配错误。
学习率与 batch_size
- 大 batch_size 需要增大学习率(线性缩放规则)
- 但 batch_size 超过 2048 时可能需改用 LAMB 优化器
- 推荐初始尝试:batch_size=64, lr=0.001
显存不足解决方案
- 减小 batch_size(最低可到 1)
- 使用
torch.utils.checkpoint分段计算 - 尝试混合精度训练:
scaler = torch.cuda.amp.GradScaler() with torch.cuda.amp.autocast(): outputs = model(inputs) loss = criterion(outputs, labels) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()
进阶思考:1×1 卷积的妙用
看似无用的 1 ×1 卷积实际能实现:
- 通道数调整(降维或升维)
- 跨通道信息融合
- 大幅减少计算量
计算量对比实验:
# 原始 3 层卷积
conv3x3 = nn.Conv2d(256, 512, kernel_size=3, padding=1)
# 等效的 bottleneck 结构
bottleneck = nn.Sequential(nn.Conv2d(256, 128, 1), # 降维
nn.Conv2d(128, 128, 3, padding=1),
nn.Conv2d(128, 512, 1) # 升维
)
# 计算量对比
print(f"原始: {3*3*256*512:,}次乘法")
print(f"Bottleneck: {1*1*256*128 + 3*3*128*128 + 1*1*128*512:,}次乘法")
# 输出: 原始: 1,179,648 次乘法
# Bottleneck: 229,376 次乘法
这种结构正是 ResNet 等现代 CNN 的基础构件。
经过这样的拆解,相信你对 CNN 的理解已经超越了大多数调包侠。接下来可以尝试:
- 在 Kaggle 上找真实图像数据集实践
- 可视化中间特征图(推荐 torchcam 工具包)
- 阅读经典论文《ImageNet Classification with Deep Convolutional Neural Networks》
记住:理解每个超参数背后的物理意义,比盲目调参更重要。
正文完
