第 8 章 · 视觉

卷积网络:图像的读法

前面七章的网络都只吃「一串数字」。可图像是二维的、有位置结构的——硬塞给全连接网络,既浪费又读不懂。这一章看卷积网络(CNN)如何用一个小窗口把图像读明白。

8.1 全连接为什么怕图像

一张再普通不过的照片:224×224 个像素,每个像素有红、绿、蓝 3 个通道。要喂给全连接网络,得先把它「展平」成一长串数字——150,528 个输入。假设第一层只放 1,000 个神经元,光这一层的权重就有:

第一层参数量 = 224 × 224 × 3 × 1,000 ≈ 1.5 亿

一张照片的信息量,可能还没有描述它的参数多。这既是天文数字的存储和计算开销,也是第 7 章里过拟合的完美温床。

更致命的是第二点:全连接网络把图像当成一串没有结构的数字。像素 (1, 1) 和像素 (1, 2) 在现实中是邻居,展平后只是向量里第 2 个和第 4 个数,谁也不认识谁。于是猫出现在左上角还是右下角,对它来说是两个毫不相干的输入——它没有平移不变性:同一个东西换个位置,就不认识了。

💡 一句话直觉

让全连接网络看图,就像把一幅拼图全倒进袋子里摇匀再拼:每块都还在,但「谁挨着谁」这个位置结构被彻底洗掉了。

8.2 卷积 = 滑动窗口的模式匹配

换个思路。图像里值得识别的东西——一条边缘、一个角点、一抹纹理——都是局部的:看清一小片就够了。而且一个模式出现在左边还是右边,它还是那个模式。那就拿一个很小的窗口(比如 3×3,叫卷积核,kernel),在图上从左到右、从上到下滑动;每停在一个位置,就把窗口盖住的像素和核里的 9 个权重逐元素相乘再求和,得到一个数:

y(i, j) = Σu Σv x(i+u, j+v) · k(u, v)

这个数越大,说明这一小片越「像」核所代表的模式。所有位置算完,拼成一张新的图,叫特征图(feature map)——哪里像核,哪里就亮。9×9 的图配 3×3 的核,窗口有 7×7 个落脚位置,特征图就是 7×7。

两个关键点:第一,核里的权重不是人工设计的,而是用第 5 章的反向传播学出来的——网络自己发现哪些模式值得找。第二,同一个核滑遍全图,9 个权重处处复用,这叫权重共享

∑ 权重共享有多省

一个 3×3 卷积核只有 9 个权重(再加 1 个偏置),不管图像是 224×224 还是 4096×4096。对比 8.1 的 1.5 亿——10 个数干了 1.5 亿个数的活,而且天然具备平移不变性:核走到哪里,认的都是同一个模式。

演示 1 · 手绘卷积实验室

可涂色画布

输入 9×9:点击或拖动涂色(0 / 1)。

特征图 7×7:正值蓝、负值橙、零为白。把鼠标移上去,看每个数怎么算出来。

卷积核:

把鼠标移到右侧特征图的某一格上,这里会展示该格的逐元素乘加算式。

当前核矩阵:

特征图最大值 特征图最小值

拿「数字 7」配「垂直边缘」核试试:特征图上最亮的格子,正好落在笔画的左右两侧边缘——核在「左暗右亮」的落差处给出大数,在反向落差处给出负数,在平淡处给出 0。换一个核,整张特征图的反应完全变了:核的权重就是学出来的「模式」本身

8.3 步长与 padding:窗口怎么滑

窗口每次移动几格,叫步长(stride,记作 S)。S=1 是一格一格挨着扫;S=2 则每次移动 2 格,输出边长直接减半——有时故意用它来快速缩小尺寸。另一个问题更隐蔽:像演示 1 那样「窗口不许出界」(valid 卷积),每卷一次图就小一圈(9×9 → 7×7),而且角上的像素只被窗口看过 1 次,中心的像素却被看了 9 次——边缘信息在悄悄流失。解决办法是在四周补一圈 0,叫 padding(记作 P):

输出边长 = ⌊(N + 2P − K) / S⌋ + 1

其中 N 是输入边长,K 是核边长。比如 N=224、K=3、S=1、P=1 时,输出仍是 224——尺寸一寸不丢。

步长 S = 2:窗口一次跳 2 格 实线是这次的位置,虚线是下一步跳到的位置 padding P = 1:四周补一圈 0 0 0 0 0 虚线圈是补上的 0,窗口可以探出原图边缘
左:步长 2 让窗口跳着走,输出边长约减半。右:padding 在外圈补 0,边缘像素也能被窗口多次覆盖,输出尺寸不再缩水。
⚠ 小心「缩水」

不做 padding 时,每过一层卷积图就小一圈,几十层下去什么都不剩了。所以实践中 K=3 的卷积几乎总是配 P=1——这个组合让特征图尺寸保持不变,是绝大多数 CNN 的默认配置。

8.4 池化:降采样与鲁棒性

卷积完,特征图还是很大,得想办法压缩。池化(pooling)的做法简单粗暴:把特征图切成不重叠的 2×2 小块,每块只留一个数——max 池化留最大的,average 池化留平均值。8×8 直接变 4×4,数据量砍掉 75%,后面的层轻松一大截。

演示 2 · 池化压缩

点击切换

把鼠标移到右侧输出的某一格,左侧会高亮它对应的 2×2 小块。注意 max 池化:亮块不管在 2×2 里的哪个角落,都被如实上报——max 池化 = 只报告「这片区域有没有这个模式」,不管具体位置,这就是平移鲁棒性的来源之一。

把鼠标移到右侧输出格上,这里会展示该格的计算。

输入8×8 = 64 格 输出4×4 = 16 格
数据量压缩到 25% 当前方式max

对比两种方式:average 池化保留「这片区域的平均强度」,整体平滑但会把醒目的尖峰冲淡;max 池化只留最强的响应,噪声大多被丢掉,模式的位置小幅挪动也不影响输出。实践中 max 池化用得更多。

8.5 层级特征:从边缘到整体

一层卷积只能认出一种小模式,那「一张脸」怎么办?答案是堆叠:第一层的输出是第二层的输入,一层比一层看得大、看得抽象。浅层认横竖斜的边缘;中层把边缘拼成纹理、角点;再深一层拼出部件——眼睛、车轮;最深处的特征已经对应「整张脸」「整辆车」这样的概念。每一层的感受野都比上一层更大,直到能「看见」整张图。

浅层 · 边缘 横、竖、斜的线段 中层 · 纹理 边缘拼成重复的图案 深层 · 部件 车轮、眼睛、机翼 整体 整张脸、整辆车
层级特征:越深的层感受野越大,认出的模式越抽象——边缘 → 纹理 → 部件 → 整体。

8.6 CNN 全貌,一句话

把这一章的零件拼起来,一个典型的 CNN 就是:

图像 → [ 卷积 → ReLU 激活 → 池化 ] × 若干层 → 展平 → 全连接分类头 → 类别

前面反复堆叠的「卷积 + 激活 + 池化」负责把图像蒸成一小堆高层特征(每个特征通道对应一种学出来的模式);最后展平,交给第 1–3 章的老朋友——全连接层加激活函数——做最终分类。特征提取和分类是一起端到端训练的,用的还是第 5、6 章的反向传播和梯度下降。

✓ 本章小结

全连接怕图像:展平后参数爆炸,且没有平移不变性。卷积让一个小核滑过全图做模式匹配,权重共享把参数量从亿级压到个位数;步长控制窗口跳多远,padding 补 0 防止缩水;池化把 2×2 小块压成一个数,既降采样又带来位置鲁棒性;层层堆叠后,网络从边缘一路认到整体。图像读完了,下一章轮到序列——看注意力机制怎么读一段话。