CNN 卷积原理:从滤镜到 ResNet
在 Transformer 称霸前,CNN 是计算机视觉的统治者。它今天仍然是处理图像的标配。这一篇讲清楚"卷积"到底是什么。
L0-02 我们讲过:2012 年 AlexNet 用 8 层卷积网络在 ImageNet 比赛上把错误率砍掉一半,引爆了深度学习革命。
那是 CNN(卷积神经网络) 的高光时刻。今天虽然 Transformer 在很多领域抢了风头,但处理图像,CNN 仍然是标配——因为它的归纳偏好(inductive bias)天生适合视觉。
这一篇把 CNN 的核心—— 卷积 ——讲清楚。
🎮 建议先去 CNN 卷积扫描可视化 玩 5 分钟。亲手拖几个 kernel 看效果,比读 1000 字都有用。
第一站:为什么不能用全连接处理图像
最朴素的神经网络叫 全连接(Fully Connected, FC)——每个输入和每个输出都连接。
但用 FC 处理图像有三个致命问题:
问题 1:参数量爆炸
一张 224×224 的彩色图片有 个像素。 如果第一层有 1000 个神经元——参数数量 = 亿。
仅一层!
问题 2:丢失”局部性”
图像的关键信息是局部的——猫的耳朵在某一小块区域里。 FC 把所有像素一视同仁,把局部结构信息全打散了。
问题 3:不具有”平移不变性”
猫在图片左上还是右下,对识别”是不是猫”应该没影响。但 FC 学到的是”左上像素 0 怎样” + “左上像素 1 怎样”——位置敏感,学了猫在左上认不出右下的猫。
CNN 完美解决这三个问题——靠的是卷积。
第二站:卷积是什么
如果你做过照片滤镜调整(PS、Lightroom、Snapseed),你已经用过卷积了。
卷积 = 一个小窗口(kernel)在图像上滑动,每个位置算一个数。
最简单的例子:模糊滤镜。
输入图像:
模糊 kernel(3×3 平均):
操作:让 kernel 在图像上滑动,每个位置做”元素相乘再求和”。例如算左上角输出:
最终输出比输入平滑(模糊)。
这就是卷积。
第三站:不同的 kernel = 不同的”过滤器”
让我们看几个经典 kernel:
水平边缘检测(Sobel-Y)
直觉:上面减下面——如果图像在垂直方向有强烈变化(亮→暗),结果就大。找的是水平方向的边。
垂直边缘检测(Sobel-X)
类似道理,找的是垂直方向的边。
锐化
中心 ×5,邻居 ×-1——增强中心和邻居的差异,让边缘”跳出来”。
模糊
平均了 3×3 邻居,让图像变平滑。
这些 kernel 在传统图像处理里都是手工设计的。
第四站:CNN 的革命——让 kernel 自己学
CNN 的关键洞察:
kernel 里的数,不用人写——让模型从数据里学出来。
数学上:把 kernel 也当成可训练参数,用梯度下降优化。
- 一层 CNN 通常有几十个 kernel
- 每个 kernel 学到一种不同的”特征检测器”
- 浅层学边缘、纹理;深层学眼睛、脸、车轮等
一个完整 CNN 层的伪代码
import torch.nn as nn
class CNNBlock(nn.Module):
def __init__(self, in_channels, out_channels):
super().__init__()
# 卷积层:64 个 3×3 kernel
self.conv = nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1)
# 激活函数
self.relu = nn.ReLU()
# Pooling 减小尺寸
self.pool = nn.MaxPool2d(2)
def forward(self, x):
x = self.conv(x) # 卷积
x = self.relu(x) # 非线性
x = self.pool(x) # 池化
return x
这就是 CNN 的”积木”。
第五站:CNN 解决了 FC 的三个问题
✅ 参数量爆炸 → 参数共享
一个 3×3 kernel 只有 9 个参数。它在整张图上滑动——所有位置共享这 9 个参数。
CNN 一层的参数 = n_kernels × 9 + bias——比 FC 少几千倍。
✅ 局部性 → 卷积天然只看局部
每个输出位置只看附近 3×3 区域。局部信息被精准捕获。
✅ 平移不变性 → 同一个 kernel 用在所有位置
学到的特征不依赖位置——“耳朵 kernel”无论扫到图像哪里都能识别耳朵。
CNN = 卷积 + 参数共享 + 平移不变性。 这三点让它特别适合”有局部结构 + 位置无关”的数据——典型就是图像。
第六站:CNN 的标准积木
一个真实 CNN 通常长这样:
输入图像 (3×224×224)
↓ [Conv 64个 3×3] → 64×224×224
↓ [ReLU + Pool 2×2] → 64×112×112
↓ [Conv 128个 3×3] → 128×112×112
↓ [ReLU + Pool 2×2] → 128×56×56
↓ [Conv 256个 3×3] → 256×56×56
↓ [ReLU + Pool 2×2] → 256×28×28
↓ ... (更多层)
↓ [Flatten] → 一个长向量
↓ [Fully Connected] → 1000 类的概率分布
每层逐渐”看”得更广(尽管 kernel 小,但因为是堆叠,深层每个神经元的”感受野”很大)。
几个重要概念
Padding:在图像边缘补 0,让卷积后大小不变。 Stride:kernel 滑动步长(默认 1,2 就是隔一个滑)。 Pooling:把 2×2 区域压缩成 1 个数(取最大值或平均),把图像尺寸缩小一半。 Channel:彩色图像有 RGB 3 个通道,每个通道独立卷积,然后求和。
第七站:经典 CNN 架构演化
LeNet (1998) - CNN 的祖宗
LeCun 设计的 5 层 CNN,用于手写数字识别。
AlexNet (2012) - 引爆深度学习
8 层。证明了”深度学习真行”。
VGG (2014) - 小而深
所有 kernel 都是 3×3,做 16-19 层。简洁优雅。
GoogLeNet / Inception (2014) - “走多路”
同一层有多种 kernel size 并行,结果拼接。
ResNet (2015) - 改变游戏的”残差连接”
解决了”太深就训不动”的问题。让 100 层、1000 层成为可能。
ResNet 的关键是跳跃连接(skip connection):
def residual_block(x):
y = conv(x)
y = relu(y)
y = conv(y)
return x + y # ← 这一加是革命
**这个简单的”加”**让梯度能直接从深层流回浅层,深网络从此可训。
历史小知识:ResNet 那篇论文里最有名的可视化——20 层网络比 56 层还好——揭示了”深网络反而难训”的反常现象。残差连接解决了这个问题。
EfficientNet (2019) - 平衡深度、宽度、分辨率
按系统化方法缩放网络,性价比最高。
Vision Transformer (2020) - 颠覆 CNN 统治
“如果 attention 这么好,为啥不用在图像上?” 把图像切成 16×16 patch,喂给 Transformer——发现也能打。
但实际工业部署里,CNN 仍然是主力——它在小数据上表现更好、推理更快、能耗更低。
第八站:CNN 还做什么
除了分类,CNN 是这些任务的基础:
- 目标检测(YOLO 系列、Faster R-CNN)—— 框出图里的物体
- 语义分割(U-Net)—— 给每个像素打类别标签
- 图像生成(GAN)—— 生成新图像
- 超分辨率 —— 把模糊图变清晰
- 风格迁移 —— 把梵高风格套到你照片上
- 医疗影像 —— 看 CT/MRI
- 自动驾驶感知 —— 识别车道、车辆、行人
所有这些都依赖卷积。
一句话总结
CNN = 让 kernel 学。
Transformer 革命前,整个视觉领域都在堆 CNN。今天它仍然是视觉的默认起点——直到你证明 Transformer 在你的具体任务上更好为止。
想”看见”它
👀 CNN 卷积扫描可视化 —— 玩 6 种预设 kernel,看它们抽取不同的特征。
- 能读懂 ResNet、VGG、EfficientNet 的论文
- 能用 PyTorch 搭一个简单的图像分类器
- 能理解为什么”图像 + Transformer” 是 ViT,“图像 + CNN” 是 ResNet——本质都是 inductive bias 的选择
下一篇推荐:L3-07 · 词嵌入(Word Embedding)从 Word2Vec 到 BERT —— CNN 处理像素,词嵌入处理文字。
读到这里说明你认真在学 🎯
订阅每周精选 —— 下一篇新文章 / 新可视化第一时间送到邮箱。
讨论区
· 用 GitHub 账号登录评论src/components/Comments.astro 顶部填入
仓库 ID 和分类 ID(见组件注释里的配置步骤)。