🏠 总目录📚 本教程 12 · CNN 处理图像 ← →
📑 本页目录(点开跳转)

12 / 先看直觉,再看细节

卷积先看一小块,
同一个规则滑过整张图。

卷积通过局部连接与权重共享利用图像结构;先跟踪尺寸,再堆网络。

先抓住一个具体结果

32 格输入,输出为什么是 16 格?

高度 32,卷积核 3,左右各补 1,步幅 2:先算 (32+2−3)/2,向下取整后加 1,得到 16。这里假设 dilation=1,宽度同理。

卷积输出高度(dilation=1)

$$H_{out}=\left\lfloor\frac{H_{in}+2p-k}{s}\right\rfloor+1$$

接下来,按需要选一项

你现在想看什么?

这些入口是选择,不是必须按顺序完成的任务。

可以停在这里

卷积通过局部连接与权重共享利用图像结构;先跟踪尺寸,再堆网络。

需要更多细节时,继续看完整正文 →
继续探索

原有正文、图解和例子都在下方。按需跳转,不必一次读完。

12 · CNN:处理图像

⏱ 30 分钟 | ⭐ 核心


🎯 一句话

CNN 的全部智慧是两条归纳偏置:「局部相关」(相邻像素才有关系)和 「平移不变」(猫在左上角还是右下角都是猫)。把这两条硬编码进网络结构,参数量降几个数量级。

输入同一个 3×3 的核,在整张图上滑过去→ 权重共享:参数量和图片大小无关×核输出特征图窗口里的 9 个数 → 输出 1 个数
同一个 3×3 的核在整张图上滑过去,每次把窗口里的 9 个数变成 1 个数。⭐ 关键是整张图共用这一个核 —— 所以参数量和图片多大无关,而且「边缘」这个概念在左上角和右下角是同一套。

💥 一、为什么图像不能直接用 MLP

结果对照

一张 224×224 的彩色图 = 224×224×3 = 150,528 个数
接一个 1000 维的全连接层→1.5 亿个参数(就这一层!)
❌ 参数爆炸
❌ 完全没利用「相邻像素相关」这个先验
❌ 图片平移一个像素,所有权重都要重新学

🔲 二、卷积:一个小窗口滑过整张图

输入图像 卷积核 3×3 输出特征图

卷积:一个小窗口在图上滑,每次做一次逐元素相乘再求和输入(▓ = 有信号的像素)⊛101010101卷积核(要找的图案)=输出(亮 = 这块像那个图案)核在输入上逐格滑动,每停一次算一个输出格;所以同一组权重被全图复用
⭐ 关键不是「怎么算」,是同一个核在整张图上重复使用 —— 这就是卷积比全连接省几个数量级参数的原因。

每个位置算一次

两个关键机制:

机制 含义 好处
局部连接 每个输出只看输入的一个小窗口 参数从 1.5 亿降到 9 个
权重共享 ⭐ 同一个卷积核滑遍全图 平移不变性 + 参数再降

💡 人话:一个卷积核就是一个「特征探测器」(比如"检测竖直边缘"), 它滑遍整张图,在哪都用同一套标准。 这就是「平移不变」。

🧮 算一次参数量,感受一下差距

信息关系

任务:224×224×3 的图→64 个特征图
MLP 做法:150,528 × 64 = 9,633,792 个参数
卷积做法:3×3 × 3(输入通道) × 64 = 1,728 个参数 ⭐
少了【5575 倍】
而且卷积的每个参数被复用了 224×224 = 5 万次

🔑 这就是"归纳偏置很值钱"的最直观证据。 🔗 数学原理第 9 章 PAC 给了它的定量版: 限制假设空间能把样本需求降几个数量级。 CNN 不是"更聪明的 MLP",而是一个被"图像的结构"约束过的 MLP。

🎨 多通道是怎么回事(最容易卡住的一点)

因果链

输入:H × W × C_in (比如 224×224×3,RGB 三通道)
卷积核:k × k × C_in ← ⭐ 卷积核【也是三维的】,深度必须等于输入通道数
一个核→一张 H'×W'×1 的特征图
要 C_out 个特征图?→用 C_out 个这样的核
⭐ 所以参数量 = k × k × C_in × C_out (+ C_out 个 bias)

💡 一句话记住:卷积核在空间上滑动,但在通道上是"一次吃掉全部"的。

⭐ 这句话能不能落到代码里,是「懂了」和「以为懂了」的分界: 🔗 附录 C 第 7 题用 15 行 numpy 把 conv2d 写出来了(和 F.conv2d 误差 0.0)。 那一行 np.tensordot(patch, w, axes=([1,2,3],[1,2,3])) 就是「通道一次吃掉全部」的字面翻译。 顺便还有两个这一节没说的:输出尺寸公式 $\lfloor(H+2p-k)/s\rfloor+1$(记不住就用 padding = k//2 配 stride=1,尺寸不变), 以及深度学习里的「卷积」严格说是互相关——数学上的卷积要先把核翻转 180°,框架都没翻。主动说出这条是加分项。


🎛️ 三、四个必须懂的超参

import torch.nn as nn
nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1, padding=1)
参数 含义 经验
kernel_size 窗口大小 3×3 是现代默认(两个 3×3 ≈ 一个 5×5 但参数更少、非线性更多)
stride 每次滑几格 1(保持尺寸)或 2(尺寸减半)
padding 边缘补零 padding=1 配 kernel=3 可保持尺寸不变 ⭐
out_channels 用多少个不同的卷积核 越深越多:64→128→256→512

输出尺寸公式:

下面这条简化公式假设 dilation=1、两侧填充相同。宽度也用相同方法计算。

$$H_{out} = \left\lfloor \frac{H_{in} + 2p - k}{s} \right\rfloor + 1$$

💡 奇数大小的卷积核、dilation=1、stride=1 时,取 padding = kernel_size // 2 可保持尺寸不变。偶数卷积核不能直接套用这个结论。


🏗️ 四、一个典型 CNN 的结构

空间 ↓ 通道 ↑ 图像 224×224×3 [Conv 3×3 + BN + ReLU] ×2 提取低级特征(边缘、颜色块) MaxPool 2×2 → 112×112 降采样,扩大感受野 [Conv + BN + ReLU] ×2 中级特征(纹理、部件) MaxPool → 56×56 ... 重复几次 ... 高级特征(眼睛、轮子) GlobalAvgPool → 512 维向量 ⭐ 现代做法,替代大全连接层 Linear(512, 类别数)
从上往下走一遍:空间尺寸一路变小、通道数一路变多(左边两小列就是这两个趋势)。⭐ 真正要记的是右边那列 —— 浅层学边缘和颜色,中层学纹理和部件,深层学物体概念,每一层都在上一层的基础上组合。

🔑 层级抽象:浅层学边缘和颜色,中层学纹理和部件,深层学物体概念。 这是"深比宽好"最直观的证据(第 7 章)——每一层都在上一层的基础上组合。

池化 Pooling:取窗口内最大值(MaxPool)或平均值(AvgPool) - 作用:降尺寸、扩大感受野、提供一点平移容忍 - 现代趋势:有时用 stride=2 的卷积替代池化(可学习的降采样比固定规则更灵活)

👁️ 感受野:一个必须建立的直觉

算一算

感受野 = 一个输出神经元"能看到"输入的多大区域

1 层 3×3 卷积 → 感受野 3×3

2 层 3×3 卷积 → 感受野 5×5

3 层 3×3 卷积 → 感受野 7×7 (每加一层 +2)

加一次 stride=2 池化 → 之后每层的感受野【翻倍增长】⭐

⭐ 所以池化不只是"省算力",它是【快速扩大感受野】的主要手段

💡 为什么两个 3×3 优于一个 5×5:

一个 5×5 两个 3×3
感受野 5×5 5×5(一样)
参数量 25·C² 18·C²(少 28%) ⭐
非线性 1 次 ReLU 2 次 ReLU(表达力更强) ⭐

🔑 这就是 VGG 的核心发现,也是"3×3 成为现代默认"的原因。

⚠️ 感受野不够的症状:模型能识别小物体但对大物体/整体结构判断差。 解法:加深、加池化、或用空洞卷积(dilated conv)。

🕳️ 三个变体(知道存在即可)

变体 做什么 用在哪
1×1 卷积 不看空间,只在通道间做线性组合 降/升通道数(省算力)、跨通道信息融合 ⭐
深度可分离卷积 空间卷积和通道卷积拆开做 MobileNet,参数量降 8~9 倍
空洞卷积 卷积核中间"留空" 不降分辨率也能扩感受野(分割任务)

💡 1×1 卷积值得单独理解:它看起来什么都没做,但 它是所有"瓶颈结构"(bottleneck)的核心—— 先用 1×1 把 256 通道压到 64,做完 3×3 再用 1×1 升回 256, 算力降到 1/4 而表达力几乎不变。ResNet-50 及以上全靠它。


📜 五、架构演进(记思想,不必记细节)

架构 年份 关键贡献
LeNet 1998 第一个成功的 CNN
AlexNet 2012 ReLU + Dropout + GPU,深度学习引爆点
VGG 2014 全部用 3×3,证明「深度」的价值
ResNet ⭐ 2015 残差连接,让上百层可训练
MobileNet 2017 深度可分离卷积,移动端
EfficientNet 2019 复合缩放(深度/宽度/分辨率一起调)
ConvNeXt 2022 借鉴 Transformer 设计的现代 CNN

⭐ 残差连接:最重要的那一个

恒等映射(捷径) ⭐ 反向传播时,梯度可以从这条捷径直接走回去 x [Conv-BN-ReLU] 主路:F(x) + 输出 = F(x) + x 两条路在这里相加
主路 [Conv-BN-ReLU] 照常算,另外拉一条什么都不做的捷径,把 x 原样送到加号那里。⭐ 关键就在那个 +:反向传播时梯度可以顺捷径原样穿回去,不必挤过中间每一层 —— 这就是上百层能训起来的原因。

为什么它能让网络变深: - 梯度高速公路:反向传播时梯度可以直接通过 + 走捷径,不用穿过所有层 → 缓解梯度消失(第 8 章) - 学残差比学映射容易:网络只需学"要改变多少",最差就是学成 0(恒等映射)

🔗 你在全景导论主线 2 · 模型怎样看懂一句话看到 Transformer 每层都有残差连接——同一个思想,源头在 ResNet。


💡 六、实践:别从零训练

流程图

❌ 从随机初始化训练自己的 CNN
需要大量数据和算力,效果还差
✅ 迁移学习(99% 的场景该这么做)⭐
拿在 ImageNet 上预训练好的模型→换掉最后一层→在你的数据上微调
import torchvision.models as models
import torch.nn as nn

model = models.resnet18(weights="IMAGENET1K_V1")   # 预训练权重
model.fc = nn.Linear(model.fc.in_features, num_classes)   # 换分类头

# 数据少时:冻结主干,只训分类头
for p in model.parameters(): p.requires_grad = False
for p in model.fc.parameters(): p.requires_grad = True

策略选择:

数据量 做法
< 1000 张 冻结主干,只训分类头
1000–1 万 微调最后几层,小 lr
> 1 万 全部微调,lr 比从零训小 10 倍

🔗 Kaggle CV 板块(09–12 章)全是这套流程的实战细节。


⚠️ 七、CNN 的假设什么时候会害你

对照

平移不变的代价:CNN【故意】丢掉了"物体在哪"这个信息

✅ 适合:图像分类(猫在哪都是猫)

❌ 不适合:位置本身携带关键信息的任务

· 医学影像:"病灶在左肺还是右肺"

· 文档理解:"这个数字在表格的哪一列"

· 界面识别:"按钮在屏幕上部还是下部"

解法:把位置作为额外通道喂进去(CoordConv),或改用带位置编码的 Transformer。

🔗 这正是数学原理第 11 章说的 「算法的专长是守恒的」——CNN 在图像分类上的优势, 换个任务就变成了负担。归纳偏置永远是双刃剑。


🔗 和站内其他章的关系

相关的地方 这里的对应
第 7 章"深比宽好" 层级抽象是最直观的证据
第 8 章梯度消失 残差连接是解法
数学原理第 9 章 PAC 归纳偏置降低样本需求的定量版
数学原理第 11 章 NFL 为什么平移不变有时是负担
Kaggle 第 9–12 章 CV 竞赛 本章的实战版(检测/分割/框融合)
全景导论第 10 章的 ViT ViT 用 Transformer 替代了 CNN,但 patch 切分借鉴了卷积思想
全景导论主线 2 的残差连接 源头就是本章的 ResNet
推荐算法第 13 章的图像 Embedding 就是 CNN 倒数第二层的输出
扩散模型 07 · 去噪主干:U-Net 与 DiT ⭐ 卷积、下采样、跳跃连接的一个完整用场:U-Net 靠跳跃连接把高频细节直接送到解码器 —— 那一章实测 8 倍下采样后高频保留 0.0000,一点不剩

✅ 检查点

  1. CNN 硬编码了哪两条归纳偏置?靠什么机制实现?
  2. 算一下:224×224×3 → 64 个特征图,MLP 和卷积的参数量差多少倍?
  3. 卷积核在通道维度上是怎么工作的?参数量公式是什么?
  4. padding 怎么设能保持尺寸不变?
  5. 感受野怎么增长?为什么两个 3×3 优于一个 5×5?
  6. 1×1 卷积在做什么?为什么它是"瓶颈结构"的核心?
  7. 残差连接为什么能让网络变深?(两个理由)
  8. CNN 的平移不变在什么任务上会变成负担?
  9. 数据只有 500 张图,该怎么做?
👀 答案
  1. 局部相关(相邻像素才有关系)和平移不变(物体在哪个位置都一样)。靠局部连接和权重共享实现。
  2. MLP:150,528 × 64 ≈ 963 万;卷积:3×3×3×64 = 1,728。差约 5575 倍,而且每个卷积参数还被复用了 5 万次。
  3. 卷积核也是三维的,深度必须等于输入通道数,在通道上"一次吃掉全部",只在空间上滑动。参数量 = k × k × C_in × C_out(+ C_out 个 bias)。
  4. padding = kernel_size // 2,配合 stride=1。
  5. 每加一层 3×3 感受野 +2;加一次 stride=2 池化后,之后每层的感受野翻倍增长。两个 3×3 与一个 5×5 感受野相同,但参数少 28%、多一次 ReLU(表达力更强)——这是 VGG 的核心发现。
  6. 不看空间,只在通道间做线性组合——用来降/升通道数和跨通道融合。瓶颈结构:先 1×1 把 256 压到 64,做 3×3,再 1×1 升回去,算力降到 1/4 而表达力几乎不变。
  7. ①梯度高速公路:反传时梯度可直接走捷径,不必穿过所有层,缓解梯度消失;②学残差比学完整映射容易,最差学成 0(恒等映射)就行。
  8. 位置本身携带关键信息的任务:医学影像(病灶在左肺还是右肺)、文档理解(数字在哪一列)、界面识别。解法:CoordConv 或带位置编码的 Transformer。
  9. 迁移学习:用 ImageNet 预训练模型,冻结主干只训分类头,配合强数据增强。绝不从零训练(第 17 章的实验:100 张图时从零只有 25%,冻结主干能到 65%)。

🛑 可以停在这里

⚡ 走神救援

先记住这几件事

  • 卷积用局部连接和共享权重提取模式,不是给每个像素单独学一套规则。
  • 跟踪每层的通道数、空间尺寸与感受野,先弄清信息怎样流动。
  • 分类头、训练模式和数据增强都要与任务对应;用验证集检验设计。

下一节 👉 13-RNN与序列建模.md

打卡记录保存在你的浏览器里,首页能看到总进度