共计 2363 个字符,预计需要花费 6 分钟才能阅读完成。
为什么选择 CNN 处理图像?
卷积神经网络(Convolutional Neural Network, CNN)在图像处理领域有两大先天优势:

-
局部感知机制 :传统全连接网络需要每个神经元连接所有输入像素(比如 224×224 图像会产生 50176 个权重),而 CNN 通过卷积核(Kernel)在局部滑动窗口计算(通常 3 ×3 或 5 ×5),大幅减少参数量。例如 $S(i,j)=(I*K)(i,j)$ 表示在位置(i,j) 的卷积运算
-
参数共享特性:同一个卷积核会扫描整张图像,这意味着不同位置检测相同特征(如边缘、纹理)时共享同一组权重,既增强了平移不变性,又降低了模型复杂度
LeNet- 5 架构拆解
以经典的 LeNet- 5 为例(结构如下图),我们逐层分析其设计思想:
[INPUT] -> [CONV1] -> [POOL1] -> [CONV2] -> [POOL2] -> [FC1] -> [FC2] -> [OUTPUT]
- 卷积层(Convolutional Layer):
- CONV1 使用 5 ×5 卷积核提取空间特征,输出 6 个特征图(Feature Map)
-
每个神经元只连接输入区域的 5 ×5 邻域,实现局部连接
-
池化层(Pooling Layer):
- POOL1 采用 2 ×2 最大池化(Max Pooling),保留窗口内最大值
-
作用:降低维度同时保持特征不变性(平移 / 旋转鲁棒性)
-
全连接层(Fully Connected Layer):
- 将最后的特征图展平后送入传统神经网络
- FC2 层神经元数量对应分类类别数(如 MNIST 是 10 类)
Python 实战代码(PyTorch 版)
数据准备
import torch
import torchvision
transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor(),
torchvision.transforms.Normalize((0.5,), (0.5,)) # 像素值归一化到[-1,1]
])
trainset = torchvision.datasets.MNIST(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)
网络定义
import torch.nn as nn
import torch.nn.functional as F
class LeNet(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 6, 5) # 输入通道 1,输出通道 6,卷积核 5x5
self.pool = nn.MaxPool2d(2, 2) # 2x2 最大池化
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16*4*4, 120) # 展平后维度计算
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x))) # CONV -> ReLU -> POOL
x = self.pool(F.relu(self.conv2(x)))
x = torch.flatten(x, 1) # 保留 batch 维度
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
训练循环
model = LeNet()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
for epoch in range(10):
running_loss = 0.0
for i, data in enumerate(trainloader):
inputs, labels = data
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
print(f'Epoch {epoch+1} loss: {running_loss/len(trainloader):.3f}')
新手避坑指南
学习率设置
- 初始值尝试:常见范围在 0.1~0.0001 之间,简单任务可从 0.01 开始
- 动态调整 :使用
torch.optim.lr_scheduler.ReduceLROnPlateau当验证损失停滞时自动降低
应对过拟合
- Dropout 层:在全连接层间随机失活部分神经元(如
nn.Dropout(0.5)) - L2 正则化:优化器添加权重衰减参数(如
optim.Adam(model.parameters(), weight_decay=1e-4))
梯度消失解决方案
- 激活函数选择:用 ReLU 替代 Sigmoid/Tanh(梯度为 1 或 0)
- 残差连接:在深层网络中加入跳跃连接(ResNet 设计思想)
延伸思考与资源
完整代码已上传 CSDN:项目链接
进阶思考题:
1. 如何修改网络结构以适应 CIFAR-10 的 RGB 三通道输入?
2. 如果训练集准确率高但验证集差,可能是什么原因?如何诊断?
3. 尝试添加 Batch Normalization 层观察训练速度变化
测试环境说明:
– 显卡:NVIDIA GTX 1060 6GB
– 数据集:MNIST(60,000 训练样本)
– 训练时间:约 3 分钟 /epoch
正文完
