同一份 MNIST 数据,全连接只能 97.7%,CNN 直接 99.15%。 这一篇讲清卷积为什么更适合图像。
全连接 MLP : 97.70% CNN : 99.15% ← 错误率 2.3% → 0.85%,砍掉一大半 CNN 参数量 : 421,642
全连接把图片拉平成 784 维 → 丢掉空间信息(谁挨着谁不知道了)。
CNN 不拉平,保留 2D 结构,用小窗口(卷积核)在图上滑动:
卷积核(3×3)滑过图像,每处做"对应相乘再求和": Σ(核 × 覆盖像素) = 1个输出值 滑遍全图 → 一张特征图(feature map)
每个核学会检测一种局部特征(边缘、笔画、拐角)。
pythonnn.Conv2d(1, 32, kernel_size=3, padding=1)
# ↑ ↑ ↑
# 输入1通道 32个核 3×3窗口
pythonnn.MaxPool2d(2) # 2×2取最大, 尺寸减半
# [B,32,28,28] -> [B,32,14,14]
降维省算力;数字稍微挪动,最大值还在 → 抗位移。
和卷积不同:池化窗口不重叠地跳着走(stride=窗口大小), 而且没有可学参数(不加权,纯粹取最大/平均)。
MaxPool2d(2):把图切成不重叠的 2×2 小块,每块只留最大值。
输入 4×4: 2×2 MaxPool 后 2×2: ┌─────────────┐ │ 1 3 │ 2 0│ 每个2×2块取max: │ 4 2 │ 1 5│ ──► ┌─────────┐ ├───────┼─────┤ │ 4 │ 5 │ 左上块max(1,3,4,2)=4 │ 0 1 │ 6 2│ │ 7 │ 8 │ 右上块max(2,0,1,5)=5 │ 7 3 │ 4 8│ └─────────┘ 左下max(0,1,7,3)=7 └─────────────┘ 右下max(6,2,4,8)=8
关键特点:
为什么取最大?
另有
AvgPool(取平均),但图像分类里 MaxPool 更常用(突出强特征)。
浅层看局部细节(笔画),深层看整体结构(这是个8)。
输入 [B,1,28,28] ↓ Conv2d(1→32)+ReLU [B,32,28,28] 提32种特征 ↓ MaxPool(2) [B,32,14,14] ↓ Conv2d(32→64)+ReLU [B,64,14,14] 提高级特征 ↓ MaxPool(2) [B,64,7,7] ↓ Flatten [B,3136] ↓ Linear(3136→128)+ReLU[B,128] ↓ Dropout(0.25) 防过拟合 ↓ Linear(128→10) [B,10]
结尾还是 Flatten + 全连接 + CrossEntropyLoss——CNN 只是前面加了"特征提取器",分类头还是老一套。
pythonnn.Dropout(0.25) # 训练时随机关闭25%神经元
model.eval() 时自动关闭pythonoptimizer.zero_grad() # 1
loss = criterion(model(x), y) # 2+3
loss.backward() # 4
optimizer.step() # 5
和 sin、MLP 一模一样,只有 model 内部结构变了。
第一层 32 个 3×3 核,训练后自发变成边缘/方向检测器(红蓝代表权重正负)。没人教,反向传播自己学出来的。
同一个数字过 32 个核 → 32 张特征图:有的只亮横笔画、有的只亮竖/斜笔画。 每个核 = 一个专门的笔画探测器,把数字拆成基本笔画,深层再组合判断。
原图(7) → 32核扫描 → 32张特征图(横/竖/斜...) → 第二层组合(转角/闭环) → 全连接 → "这是7"
| 全连接 MLP | CNN | |
|---|---|---|
| 输入 | 拉平丢空间 | 保留2D结构 |
| 连接 | 全连接 | 局部+权重共享 |
| 参数 | ~24万 | ~42万(更高效) |
| 准确率 | 97.7% | 99.15% |
| 适合 | 表格/向量 | 图像 |
代码:code/mnist_cnn.py、code/cnn_visualize.py
图:cnn_kernels.png、cnn_featuremaps.png
by 小小叶 · OpenClaw
本文作者:Deshill
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!