上一讲第 4 讲:神经网络与反向传播 下一讲第 6 讲:CNN 架构

第 5 讲:卷积神经网络进行图像分类

Image Classification with CNNs —— 从手工特征到端到端学习、卷积层的来龙去脉、输出尺寸公式、感受野、池化与平移等变性

本讲概览:

本讲由课程的第四位主讲老师 Justin Johnson(CS231N 的创始讲师之一)带来,是课程从"深度学习基础"进入"感知与理解视觉世界"阶段的转折点。老师先完整回顾了前四讲的框架——问题形式化为张量的输入输出、线性分类器、损失函数、优化与反向传播,指出这套"配方"几乎是所有深度学习应用的通用公式;随后回顾了神经网络时代之前的手工特征(颜色直方图、方向梯度直方图),对比"人工特征 + 可学习分类器"与"端到端学习"两种范式;接着从全连接层破坏图像二维空间结构这一缺陷出发,详细讲解卷积层(滤波器、步长、零填充、输出尺寸公式、参数量与计算量手算)与池化层这两个新的计算图算子,并以感受野和平移等变性收尾。下一讲将把这些零件组装成完整的 CNN 架构。

一、开场:课程走到哪里了——前四讲主题回顾

老师开场先做了自我介绍:他在斯坦福读博期间(2012–2018)与 Andrej Karpathy 等人共同创建了 CS231N,此后在 Facebook AI Research 与密歇根大学工作,目前在做一家名为 World Labs 的创业公司。

更重要的是定位:课程第一段"深度学习基础"已经结束。前四讲的内容其实构成了一个完整的深度学习流水线,老师据此逐条回顾了几大主题:

由此得到一个万能配方:任何问题 → 编码为张量 → 写出从输入张量到输出张量的计算图 → 收集输入输出数据对 → 写损失函数 → 用梯度下降 + 反向传播优化。图像分类、图像生成、大语言模型,几乎一切神经网络系统都由这个公式或其变体训练。接下来课程进入第二段:如何把这套通用框架专门化到计算机视觉的问题上。

二、卷积网络之前:手工特征与端到端学习之争

在进入 CNN 之前,老师花了相当篇幅回顾"史前时代"(大约 2000 年到 2012 年前后)的做法:特征表示(feature representation)。既然神经网络的输入不一定是原始像素,那么可以让人类专家设计一个函数,把原始像素转换成一种"更高级"的表示,再把这个表示喂给线性分类器。两个代表性例子:

当时人们并不纠结"哪种特征最好",常见答案是把各种特征全部抽取出来拼接成一个大特征向量,再在上面接一个可学习的分类器。

把两代系统摆在一起对比很有启发:

两者的抽象结构其实一模一样(输入原始像素、输出类别分数),区别只在于哪部分由人设计、哪部分由数据学习。端到端范式的直觉是:梯度下降大概是比人类更好的程序员,大量数据对问题的了解也超过人类专家——人类设计的特征提取器可能成为整个系统的瓶颈(你的直觉可能错了,或者完美的特征提取器根本写不出来)。过去十五年,这个范式在无数问题上反复胜出。

需要澄清的一点:端到端学习并不意味着不需要人类设计。人不再设计"某一个具体的特征函数",而是设计"一整类函数"——由计算图结构(算子的排列、各层尺寸)定义的函数族,其中的权重留给数据去学。架构设计(第 6 讲的主题)仍然是人类的重要工作。

三、卷积网络简史与它的今天

卷积网络(ConvNets / CNN)是一类由线性层、非线性、卷积层、池化层等算子拼接而成、输入原始像素、输出预测分数的网络架构。典型结构是:前面一段卷积 + 池化 + 非线性交错组成的"特征提取主体",后面接一到多层全连接层充当分类器,整个系统端到端地用梯度下降训练。

它的历史比很多同学想象的久:

那为什么今天还要认真学 CNN?老师给了四条理由:(1)重大的历史意义;(2)实践中仍然被大量使用;(3)它能帮助你建立"对图像而言什么重要"的直觉;(4)它并没有死——现代系统常常是卷积与 Transformer 混用的混合架构。Transformer 将在第 8 讲详述。

四、全连接层回顾:为什么它不适合图像

到目前为止我们见过的网络只有三类节点:全连接层(矩阵乘法)、激活函数(ReLU)和损失函数。所谓全连接层的流程是:把 \(32\times32\times3\) 的图像(\(32\times32\) 是空间维,\(3\) 是 RGB 通道维)拉直成一个 \(3072\) 维的长向量,再乘一个 \(3072\times10\) 的权重矩阵,得到 10 个类别的分数。

换一个角度理解全连接层:输出向量的每个元素都是权重矩阵的一行与整个输入向量的内积,而内积本质上是一个模板匹配(template matching)——两个向量方向一致时内积大,正交时为 0。所以全连接层 = 一组与输入同尺寸的模板,输出是每个模板与整幅输入的匹配分数。

问题在于:图像不是一维对象,它有二维空间结构,而这个结构对图像内容是本质性的。把图像拉直成向量,等于在设计网络架构时就忽略掉了输入数据最重要的特性之一。我们需要能"尊重"二维结构的新算子——这正是卷积层与池化层。

五、卷积层:滑动的小模板

把全连接层推广为卷积层,思路是保留"模板匹配、学习一组滤波器(filters)"的思想,只改一件事:模板不再与输入一样大,而只看输入的一小块

5.1 从一个滤波器到一张激活图

不再把 \(32\times32\times3\) 的图像拉直,而是保持三维张量结构(通道/深度维 + 高 + 宽)。一个滤波器是 \(5\times5\times3\) 的小"子图像"——注意它的通道数必须与输入通道数相同(这里都是 \(3\)),但空间尺寸更小。计算过程是:

  1. 把滤波器对准图像某个空间位置,覆盖住一个 \(5\times5\times3\) 的小块;
  2. 计算两者的内积(\(75\) 对数相乘求和),得到一个标量——这一小块图像与模板的匹配程度;
  3. 把滤波器在整幅图像上到处滑动,每个位置都算一次内积;
  4. 把所有匹配分数按空间位置收集起来,得到一张二维平面,称为激活图(activation map)

当然,这是深度学习,一个滤波器不够就多来几个:第二个 \(5\times5\times3\) 的滤波器重复上述过程得到第二张激活图……共 \(6\) 个滤波器,就把 \(6\) 张 \(28\times28\) 的激活图在通道维上堆叠,得到 \(6\times28\times28\) 的输出(\(28\times28\) 是空间维,\(6\) 是通道维)。所有滤波器收集成一个 \(6\times3\times5\times5\) 的四维张量。

与线性层类比,卷积层也有偏置(bias):每个滤波器对应一个标量偏置,加到该滤波器产生的整张激活图上(沿空间维广播)。\(6\) 个滤波器就是 \(6\) 维偏置向量。

5.2 批处理与一般公式

实践中卷积层按批(batch)工作,一切都变成四维张量:

所谓卷积神经网络,就是把这些卷积算子在计算图里逐层堆叠起来的网络。例如一个最简单的两卷积层网络:\(3\times32\times32\) 的输入经过 \(6\) 个 \(5\times5\times3\) 的滤波器得到 \(6\times28\times28\);再经过 \(10\) 个 \(5\times5\times6\) 的滤波器得到 \(10\times24\times24\)——第二层滤波器的通道数 \(6\) 必须与上一层输出的通道数对上。

但这里有个课堂互动中同学发现的 bug:两个卷积层直接堆叠仍是线性的!卷积就是内积,内积是线性算子,线性算子的复合还是线性算子——两层卷积的表示能力等价于一层。修复方法与第 4 讲的多层网络完全相同:在卷积层之间插入非线性激活函数(如 ReLU)。所以真实的 ConvNet 是卷积层、非线性、池化层等多种算子的堆叠。

5.3 滤波器从哪里来:梯度下降、随机初始化与对称性破缺

课堂上被反复问到的问题是:滤波器的数值是谁定的?答案是没有人手工设计滤波器——这正是从手工特征到端到端学习的核心区别。滤波器在训练开始时随机初始化,然后通过反向传播计算损失对每个滤波器中每个标量权重的梯度("动一动这个权重,损失会变多少"),再由优化器每步更新。训练循环永远是:取一批数据 → 前向 → 算损失 → 反向 → 梯度步,每一步都在改变滤波器。

随机初始化还有一个微妙但关键的作用:打破对称性。如果所有滤波器初始化成完全相同的值,它们的损失和梯度也完全相同,训练中将永远保持一致、学到相同的特征——初始化相同则永远相同。只有让各滤波器的初始值不同,它们才能分化出各自不同的功能。

要区分两个概念:超参数(hyperparameter)是训练前由人设定的,如滤波器个数(输出通道数)、滤波器尺寸——它们决定了张量的形状;参数(parameter)是梯度下降优化的对象,即张量内部的数值。超参数通常用交叉验证等方式选取。

另一个常见问题:一个卷积层里能用不同尺寸的滤波器吗?通常单层卷积固定一种滤波器尺寸(便于写高效的 GPU 核函数),但可以在更大网络里并联/串联不同滤波器尺寸的卷积层——下一讲的 Inception 架构正是这么做的。此外 PyTorch 等框架还提供 groups(分组卷积)、dilation(空洞卷积)等超参数;卷积思想也可以推广到一维信号(1D 卷积)和三维体数据(3D 卷积)。

5.4 卷积滤波器学到了什么:可视化

第一层卷积滤波器可以直接当作小图片可视化(因为它们就是 \(5\times5\times3\) 的 RGB 小块)。把 AlexNet 在 ImageNet 上训练得到的第一层滤波器画出来,会发现几乎所有卷积网络、几乎所有数据集上学到的第一层都长得很像,主要分两类:

更高层的滤波器不能这么直接可视化,需要更复杂的技巧:例如对每个滤波器,展示一批让它响应强烈的输入图像块。可以看到第 5、6 层的滤波器在响应"眼睛""文字片段""车轮/圆弧"等更大的空间结构——这个现象与下面要讲的感受野直接相关。这一切都是梯度下降在大数据上学出来的,没有任何人手工设计。有同学问"用所有滤波器的响应能否重建原图"——可以,用的还是梯度 descent,几讲之后会讲到这类机制。

六、卷积运算的几何细节:尺寸、填充、步长、感受野

6.1 输出尺寸从何而来

忽略通道维,看一个 \(7\times7\) 的输入配 \(3\times3\) 的核:核从左上角开始每次挪一格,横向可以放 \(5\) 个位置、纵向也是 \(5\) 个,输出就是 \(5\times5\)。一般地,一维方向上输入长 \(W\)、核长 \(K\) 时:

$$\text{输出尺寸} = W - K + 1$$

问题是:每过一层卷积,特征图就缩一圈,层数一多就缩没了。有些架构接受这一点,但更多时候我们希望每层保持尺寸不变——设计起来省心。

6.2 零填充(zero padding)

解决办法是在卷积前给输入四周补 \(P\) 圈零("虚拟数据")。补 \(P\) 圈后输出尺寸增加 \(2P\)。最常用的配置是取奇数核,并令:

$$K = 2k + 1 \text{ 时,取 } P = (K - 1) / 2 \text{,则输出空间尺寸与输入相同}$$

例如 \(3\times3\) 的核配 \(P=1\)、\(5\times5\) 的核配 \(P=2\),特征图尺寸都不变。老师也提醒:从信号处理的角度零填充会带来一些边界的"怪异",但工程上通常只关心张量尺寸对齐,用零填充在实践中效果良好。

6.3 步长(stride)与完整输出尺寸公式

另一个旋钮是步长:滤波器不必逐像素滑动,可以每次跳 \(S\) 个像素。回到 \(7\times7\) 输入 + \(3\times3\) 核 + 步长 \(2\):可以放 \(3\) 个位置,输出 \(3\times3\)。综合 \(W\)、\(K\)、\(P\)、\(S\) 的完整公式("栅栏柱"计数带来了最后那个 \(+1\)):

$$\text{输出尺寸} = \frac{W - K + 2P}{S} + 1$$

6.4 感受野(receptive field)

一层卷积的每个输出值只看输入中与核同样大的一小块。但把卷积逐层堆叠,这个"视野"会逐层放大:第 2 层的一个激活值依赖第 1 层的一个局部区域,其中每个又依赖原始图像的一个局部区域……于是有效感受野——能够影响网络下游某个激活值的原始图像像素数——随卷积层数线性增长。这解释了 5.4 节"更深层学更大结构"的现象。

问题在于:分类决策最终需要聚合整幅图像的全局信息,而线性增长意味着需要非常多层才能"看全"。步长卷积给出了加速手段:步长卷积本身就在网络内部做下采样。如果每层都以步长 \(2\) 把特征图尺寸减半,有效感受野将随网络深度指数增长——只需相对较少的层就能覆盖整幅输入图像。

拓展

感受野的递推计算。设第 \(l\) 层的感受野为 \(r_l\),常用的递推公式为 \(r_l = r_{l-1} + (K_l - 1) \cdot j_{l-1}\),其中 \(j_l\) 是第 \(l\) 层输出上相邻像素在输入上的间距(jump),\(j_l = j_{l-1} \cdot S_l\)。例如三层 \(3\times3\)、步长 \(1\) 的卷积叠起来,感受野是 \(7\times7\)(每层加 \(2\));若每层再隔一个 \(2\times2\) 步长 \(2\) 的池化,感受野增速立刻翻倍。设计深层网络时感受野是要盯紧的量:若最终特征图上单个位置的感受野还盖不住目标物体,网络的判别力会受限。这也是 VGG 用两个 \(3\times3\) 卷积替代一个 \(5\times5\) 卷积(感受野相同、参数更少、非线性更多)的经典设计的出发点,下一讲会展开。

6.5 手算例子:输出尺寸、参数量与计算量

手算

课上反复演练的例子:输入 \(3\times32\times32\),卷积层有 10 个 \(5\times5\) 的滤波器,步长 \(1\)、填充 \(2\)。逐项计算:

(1)输出尺寸。通道维:输出通道数 = 滤波器个数 = \(10\)。空间维套公式:\((32 - 5 + 2\times2)/1 + 1 = 32\)。所以输出是 \(10\times32\times32\)——注意输出里的 \(32\) 与输入的 \(32\) 是两个不同的 \(32\),前者由公式算出,恰好因为 \(K=5\)(奇数)、\(P=(5-1)/2=2\) 而与输入同尺寸。

(2)参数量。每个滤波器是 \(5\times5\times3 = 75\) 个权重,加 \(1\) 个偏置,共 \(76\) 个可学习参数;10 个滤波器共 \(76\times10 = 760\) 个参数。对比:一个同样输入输出尺寸的全连接层需要 \(3072\times10 \approx 3\) 万参数——权重共享让卷积层参数少了两个数量级。

(3)计算量(乘加次数)。思路:先数输出有多少个标量,再数每个标量花了多少乘加。输出共 \(10\times32\times32 = 10240\) 个数;每个数来自一个 \(5\times5\times3\) 滤波器与图像块的点积,即 \(75\) 次乘加。总计 \(10240\times75 = 768{,}000\) 次浮点乘加。可见卷积层"参数少但计算多"——参数在空间上共享,但每个滤波器都要在所有位置滑一遍。这也是卷积占据整个 CNN 绝大部分浮点运算量的原因。

七、池化层:便宜的下采样

下采样(缩减特征图空间尺寸)之所以有用,是因为它能更快地堆大感受野。步长卷积是一种方式,但它计算昂贵(卷积本来就是网络中浮点运算的大头)。池化(pooling)则是另一种几乎不花计算量的下采样方式。

做法:把输入(例如 \(64\times112\times112\))的每一个通道平面单独取出、独立下采样,再堆叠回去——通道数不变、空间尺寸变小。最常用的是最大池化(max pooling):把每张特征平面切成互不重叠的方块,每块取最大值。例如 \(2\times2\) 核、步长 \(2\),把一张平面切成不重叠的 \(2\times2\) 小块,每块输出 \(4\) 个数中的最大值,空间尺寸减半。池化层的超参数与卷积类似(核尺寸、步长),外加一个"池化函数"的选择:max 最常见,也有平均池化(average pooling)、抗混叠池化等。

几个课堂问答值得记录:

八、卷积网络的整体结构

把卷积与池化穿插排布,就得到经典 CNN 的原型结构。老师给出的样板模式是:

CONV - RELU - CONV - RELU - POOL - CONV - RELU - CONV - RELU - POOL - ... - FC - FC

即若干个"卷积 + 非线性"(有时连续两个)之后跟一个池化,把空间尺寸逐步减半、通道数逐层增加;末尾将特征图拉直,接一到数个全连接层输出类别分数。整个系统端到端地通过梯度下降最小化训练集损失。各种具体的成名架构(LeNet、AlexNet、VGG、ResNet 等)如何选择层数、尺寸与连接方式,是下一讲的主题。

九、平移等变性:卷积与池化的数学秉性

本讲开头的承诺是"找到尊重二维空间结构的算子"。怎样把这个直觉数学化?答案是平移等变性(translation equivariance),它是卷积和池化共有的性质:

想象两条路线:路线一,先对图像做卷积/池化,再把得到的特征图整体平移;路线二,先平移图像,再做同样的卷积/池化。两条路线得到的最终结果完全相同(忽略边界条件等数学细节,在无穷大图像的极限下严格成立)。用交换图表示即:

   图像 --卷积/池化--> 特征图
    |                    |
  平移                 平移
    ↓                    ↓
 平移图像 --卷积/池化--> 平移后的特征图   (两条路径终点相同)

换句话说,平移与卷积/池化这两个操作可以交换次序。这把一条关于图像的先验知识"烤"进了算子设计里:我们从图像中提取的特征应当只取决于内容本身,而不应取决于内容出现在图像中的绝对位置。向左看是人、长椅和树,向右看还是人、长椅和树,视觉系统应当用完全相同的方式处理这两种情况——卷积用同一组权重在所有空间位置滑动,正是这种不变立场的具体实现。

老师特别澄清:你并不会真的在网络里做"平移图像"这种操作,平移等变性是一个数学上的 curiosities(数学家称之为交换图,他们很喜欢),它的意义在于说明算子的结构本身编码了我们对图像世界的合理假设。注意区分:等变(equivariant)\(\neq\) 不变(invariant)——卷积对平移是等变的(输入平移,输出跟着平移),而"无论物体在哪都输出同一类别"的平移不变性通常要靠池化以及足够深的层级结构逐步近似。

拓展

\(1\times1\) 卷积。当输入通道很多时,可以用通道数等于 \(1\times1\) 的卷积核在空间上逐点做通道间的线性组合,几乎不增大感受野,却常被用来升维/降维通道数、大幅压缩参数量(Network in Network、Inception、ResNet 瓶颈结构中的关键组件)。它有时也被称为"逐点卷积"(pointwise convolution)。

拓展

现代 CNN 里的其他常客。2015 年之后的卷积网络在 CONV–RELU–POOL 骨架之外普遍加入了:批归一化(Batch Normalization)——对每个小批次的激活做标准化再缩放平移,显著稳定和加速训练,常插在卷积与 ReLU 之间;残差连接(residual / skip connection)——把输入直接加到若干层之后的输出上,使上百层的网络也能训练(ResNet,下一讲主角);全局平均池化(Global Average Pooling, GAP)——把每张特征图压成一个数,替代末尾昂贵的大全连接层。此外,现代检测/分割网络(如 FPN、U-Net)还用"上采样 + 跳跃连接"把特征图重新放大,形成编码器–解码器结构。这些组件将在后续讲次陆续登场。

十、本讲要点回顾

上一讲第 4 讲:神经网络与反向传播 下一讲第 6 讲:CNN 架构