共计 2099 个字符,预计需要花费 6 分钟才能阅读完成。
作为一名刚接触深度学习的新手,第一次看到 CNN(卷积神经网络)的结构框图时,完全被那些层层叠叠的方块和箭头搞晕了。特别是各种专业术语,比如卷积层、池化层、特征图,听起来就让人头皮发麻。经过一段时间的摸索和实践,我终于搞明白了 CNN 的核心结构和实现方法,今天就和大家分享一下我的学习笔记。

为什么需要 CNN?
刚开始学深度学习时,我最先接触的是传统的全连接神经网络。这种网络在处理图像数据时有个致命缺点:参数量太大。举个例子,一张 100×100 像素的彩色图片,输入层就需要 3 万个神经元(100x100x3),如果再连接几个隐藏层,参数数量会爆炸式增长。
CNN 通过三个核心思想解决了这个问题:
- 局部感受野:每个神经元只连接输入图像的一小块区域
- 参数共享:同一层的所有神经元使用相同的权重(卷积核)
- 空间下采样:通过池化层逐步降低特征图的分辨率
CNN 结构框图详解
一个典型的 CNN 结构框图可以分为以下几个部分(以 LeNet- 5 为例):
输入层 -> [卷积层 -> 激活函数 -> 池化层] x N -> 全连接层 -> 输出层
让我们用中文标注一个具体的例子:
- 输入层:32x32x1 的灰度图像(MNIST 手写数字)
- 卷积层 C1:使用 6 个 5 ×5 的卷积核,输出 6 个 28×28 的特征图
- 为什么是 28×28?因为 (32-5)/1 + 1 = 28
- 池化层 S2:2×2 最大池化,输出 6 个 14×14 的特征图
- 卷积层 C3:使用 16 个 5 ×5 卷积核,输出 16 个 10×10 的特征图
- 池化层 S4:2×2 最大池化,输出 16 个 5 ×5 的特征图
- 全连接层:将 400 个神经元(16x5x5)连接到 120 个神经元
- 输出层:10 个神经元对应 0 - 9 的数字分类
PyTorch 实现代码(带中文注释)
import torch
import torch.nn as nn
import torch.nn.functional as F
class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()
# 第一个卷积层:1 个输入通道,6 个输出通道,5x5 卷积核
self.conv1 = nn.Conv2d(1, 6, 5)
# 第二个卷积层:6 个输入通道,16 个输出通道,5x5 卷积核
self.conv2 = nn.Conv2d(6, 16, 5)
# 全连接层
self.fc1 = nn.Linear(16*5*5, 120) # 为什么是 16*5*5?见上面的维度计算
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
# 第一组卷积 + 激活 + 池化
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) # 池化窗口 2x2
# 第二组卷积 + 激活 + 池化
x = F.max_pool2d(F.relu(self.conv2(x)), (2, 2))
# 展平特征图用于全连接层
x = x.view(-1, self.num_flat_features(x))
# 全连接层
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
def num_flat_features(self, x):
size = x.size()[1:] # 去掉 batch 维度
num_features = 1
for s in size:
num_features *= s
return num_features
# 实例化网络
net = LeNet()
print(net)
避坑指南
卷积核大小选择
- 常见误区:认为卷积核越大越好
- 实际情况:3×3 卷积核最常用,可以通过堆叠多个小卷积核来获得与大卷积核相同的感受野,同时参数更少
激活函数使用
- ReLU 是最常用的激活函数,但要小心 ” 死亡 ReLU” 问题(神经元永远不激活)
- 可以在全连接层后使用 Dropout 防止过拟合
实践建议
数据集推荐
- MNIST:手写数字识别,最适合入门
- CIFAR-10:10 类彩色小图像
- ImageNet:大规模图像分类(适合进阶)
可视化工具
- TensorBoard:PyTorch 内置的可视化工具
- Netron:网络结构可视化工具
- 特征图可视化代码示例:
import matplotlib.pyplot as plt
# 获取第一层卷积后的特征图
conv1 = net.conv1
output = conv1(input_image)
# 可视化第一个特征图
plt.imshow(output[0, 0].detach().numpy(), cmap='gray')
plt.show()
延伸思考
- 为什么现代 CNN(如 ResNet)都使用大量 3 ×3 卷积核?
- 池化层在 CNN 中真的是必需的吗?有没有可以替代池化的方法?
- 如何解释 CNN 中间层学习到的特征?
经过这段时间的学习,我深刻体会到 CNN 的强大之处在于它能够自动学习图像的多层次特征。从最初只能识别边缘的低层特征,到能够识别物体部件的中层特征,再到能够识别整个物体的高层特征,CNN 的这种层次化特征提取能力让它成为计算机视觉领域的基石模型。建议初学者一定要动手实现一个简单的 CNN,观察每一层的输出变化,这对理解 CNN 的工作原理非常有帮助。
正文完
