共计 2817 个字符,预计需要花费 8 分钟才能阅读完成。
背景介绍
卷积神经网络(Convolutional Neural Network, CNN)是计算机视觉领域的基石技术。1998 年,Yann LeCun 提出的 LeNet- 5 架构首次将卷积操作(convolution)、下采样(subsampling,即现代所说的池化 /pooling)和全连接层(fully connected layer)有机结合,成功应用于手写数字识别。这个只有 5 层的 ” 浅 ” 网络,在当时 MNIST 数据集上达到了 99.2% 的惊人准确率。

LeNet- 5 的里程碑意义在于:
- 首次证明了局部感受野(local receptive fields)和权值共享(weight sharing)的有效性
- 确立了 ” 卷积 - 池化 - 非线性激活 ” 的基础架构范式
- 为后来的 AlexNet、VGG 等现代 CNN 奠定了设计基础
架构详解
LeNet- 5 的原始论文中描绘的架构包含 7 层(实际可训练参数的是 5 层),其数据流如下图所示:
输入 (32×32) → 卷积 C1(6@28×28) → 池化 S2(6@14×14) → 卷积 C3(16@10×10) → 池化 S4(16@5×5) → 卷积 C5(120@1×1) → 全连接 F6(84) → 输出 (10)
核心层解析
- 卷积层(Convolution Layer)
- 使用 5×5 卷积核(kernel)与输入做滑动窗口乘加运算
- 原始实现使用 sigmoid 激活函数:$\sigma(x) = \frac{1}{1+e^{-x}}$
-
C1 层输出特征图(feature map)尺寸计算:$(32-5)/1 + 1 = 28$
-
池化层(Subsampling Layer)
- 当时的平均池化(average pooling),现代更多使用最大池化(max pooling)
- 2×2 窗口配合步长 (stride)2,实现 75% 的下采样
-
每个池化单元会乘以可训练权重再加偏置(现代架构通常省略)
-
全连接层(Fully Connected Layer)
- 将最后一个卷积层的输出展平后接传统神经网络
- 使用径向基函数(RBF)作为输出层(现代架构已弃用)
PyTorch 实现
网络定义
import torch
import torch.nn as nn
class LeNet5(nn.Module):
def __init__(self):
super().__init__()
# 输入 1×32×32(现代实现常改为 28×28)self.conv1 = nn.Conv2d(1, 6, kernel_size=5, padding=2) # 输出 6@28×28
self.pool1 = nn.AvgPool2d(kernel_size=2, stride=2) # 输出 6@14×14
self.conv2 = nn.Conv2d(6, 16, kernel_size=5) # 输出 16@10×10
self.pool2 = nn.AvgPool2d(kernel_size=2, stride=2) # 输出 16@5×5
self.conv3 = nn.Conv2d(16, 120, kernel_size=5) # 输出 120@1×1
self.fc1 = nn.Linear(120, 84)
self.fc2 = nn.Linear(84, 10)
def forward(self, x):
x = torch.sigmoid(self.conv1(x))
x = self.pool1(x)
x = torch.sigmoid(self.conv2(x))
x = self.pool2(x)
x = torch.sigmoid(self.conv3(x))
x = x.view(x.size(0), -1) # 展平
x = torch.sigmoid(self.fc1(x))
x = self.fc2(x)
return x
训练代码
from torchvision import datasets, transforms
# 数据预处理
transform = transforms.Compose([transforms.Resize(32),
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])
train_set = datasets.MNIST('./data', train=True, download=True, transform=transform)
train_loader = torch.utils.data.DataLoader(train_set, batch_size=64, shuffle=True)
model = LeNet5()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
# 训练循环
for epoch in range(10):
for images, labels in train_loader:
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
print(f'Epoch {epoch+1}, Loss: {loss.item():.4f}')
现代 CNN 对比
| 指标 | LeNet-5 | ResNet-18 |
|---|---|---|
| 参数量 | 60k | 11M |
| 感受野 | 14×14 | 483×483 |
| 典型准确率 | 99.2% | 99.7% |
| 计算量 (FLOPs) | 0.5M | 1.8G |
虽然 LeNet- 5 在性能上已被超越,但其设计理念仍值得学习:
- 通过堆叠小卷积核(而非大卷积核)实现层次化特征提取
- 交替使用卷积和池化逐步压缩空间维度
- 末端使用全连接层完成分类决策
避坑指南
- 学习率设置
- 浅层网络对学习率更敏感,建议初始值设为 0.01-0.1
-
可配合线性 warmup 策略:前 5 个 epoch 逐渐增大学习率
-
梯度消失缓解
- 原始论文使用 sigmoid 激活函数,容易引发梯度消失
-
现代实现可替换为 ReLU:
nn.ReLU() -
参数初始化
- 卷积层建议使用 Xavier 初始化:
nn.init.xavier_uniform_(self.conv1.weight) - 避免全零初始化导致对称性问题
延伸思考
为什么现代 CNN 不再使用 LeNet- 5 的某些设计?
- sigmoid 激活函数 :存在梯度消失问题,计算开销大(需要指数运算)
- 平均池化 :最大池化能更好保留纹理特征
- RBF 输出层 :softmax+ 交叉熵的组合更易优化
- 全连接层 :被全局平均池化(GAP)取代以减少参数量
扩展阅读
- 原始论文:Gradient-Based Learning Applied to Document Recognition
- PyTorch 官方教程:LeNet- 5 实现
- 现代改进版:LeNet-5 with BatchNorm
通过复现这个经典架构,我们能更深刻地理解 CNN 的设计哲学。虽然现代网络更加复杂,但特征层次化提取的核心思想从未改变。建议初学者在掌握 LeNet- 5 后,逐步过渡到 AlexNet、VGG 等更深的架构。
正文完
发表至: 未分类
近两天内
