\[ \begin{align}\begin{aligned}\newcommand{\ba}{\boldsymbol{a}} \newcommand{\bb}{\boldsymbol{b}} \newcommand{\be}{\boldsymbol{e}} \newcommand{\bq}{\boldsymbol{q}} \newcommand{\bk}{\boldsymbol{k}} \newcommand{\bw}{\boldsymbol{w}} \newcommand{\bx}{\boldsymbol{x}} \newcommand{\by}{\boldsymbol{y}} \newcommand{\bz}{\boldsymbol{z}} \newcommand{\bd}{\boldsymbol{d}} \newcommand{\bv}{\boldsymbol{v}} \newcommand{\bs}{\boldsymbol{s}}\\\newcommand{\btheta}{\boldsymbol{\theta}} \newcommand{\bbeta}{\boldsymbol{\beta}} \newcommand{\bgamma}{\boldsymbol{\gamma}} \newcommand{\bsigma}{\boldsymbol{\sigma}} \newcommand{\md}{\mbox{d}} \newcommand{\bmu}{\boldsymbol{\mu}} \newcommand{\bone}{\boldsymbol{1}} \newcommand{\bzero}{\boldsymbol{0}} \newcommand{\bepsilon}{\boldsymbol{\epsilon}} \newcommand{\bphi}{\boldsymbol{\phi}} \newcommand{\bh}{\boldsymbol{h}} \newcommand{\bc}{\boldsymbol{c}} \newcommand{\br}{\boldsymbol{r}} \newcommand{\bQ}{\boldsymbol{Q}} \newcommand{\bK}{\boldsymbol{K}} \newcommand{\bV}{\boldsymbol{V}} \newcommand{\bSigma}{\boldsymbol{\Sigma}} \newcommand{\bg}{\boldsymbol{g}} \newcommand{\bxi}{\boldsymbol{\xi}} \newcommand{\bvarepsilon}{\boldsymbol{\varepsilon}} \newcommand{\bdelta}{\boldsymbol{\delta}} \newcommand{\bq}{\boldsymbol{q}} \newcommand{\bk}{\boldsymbol{k}} \newcommand{\bJ}{\boldsymbol{J}} \newcommand{\bp}{\boldsymbol{p}} \newcommand{\bi}{\boldsymbol{i}} \newcommand{\bo}{\boldsymbol{o}} \newcommand{\bE}{\boldsymbol{E}} \newcommand{\bH}{\boldsymbol{H}} \newcommand{\bL}{\boldsymbol{L}} \newcommand{\bu}{\boldsymbol{u}} \newcommand{\bLambda}{\boldsymbol{\Lambda}} \newcommand{\trans}{^{\rm\scriptsize T}} \newcommand{\var}{\mathrm{var}}\\\newcommand{\bA}{\boldsymbol{A}} \newcommand{\bB}{\boldsymbol{B}} \newcommand{\bC}{\boldsymbol{C}} \newcommand{\bD}{\boldsymbol{D}} \newcommand{\bG}{\boldsymbol{G}} \newcommand{\bI}{\boldsymbol{I}} \newcommand{\bM}{\boldsymbol{M}} \newcommand{\bP}{\boldsymbol{P}} \newcommand{\bS}{\boldsymbol{S}} \newcommand{\bU}{\boldsymbol{U}} \newcommand{\bW}{\boldsymbol{W}} \newcommand{\bX}{\boldsymbol{X}} \newcommand{\bY}{\boldsymbol{Y}} \newcommand{\bZ}{\boldsymbol{Z}} \newcommand{\cotp}{\textcolor[RGB]{48,209,88}{TP}} \newcommand{\cotn}{\textcolor[RGB]{100,210,255}{TN}} \newcommand{\cofp}{\textcolor[RGB]{94,92,230}{FP}} \newcommand{\cofn}{\textcolor[RGB]{191,90,242}{FN}}\\\newcommand{\numcotp}{\textcolor[RGB]{48,209,88}{50}} \newcommand{\numcotn}{\textcolor[RGB]{100,210,255}{30}} \newcommand{\numcofp}{\textcolor[RGB]{94,92,230}{10}} \newcommand{\numcofn}{\textcolor[RGB]{191,90,242}{10}} \DeclareMathOperator*{\argmin}{arg\,min}\end{aligned}\end{align} \]

卷积神经网络的基本结构#

学习目标与记号#

  1. 准确解释卷积层、激活层与池化层;

  2. 根据公式和张量维度分析展平,并识别常见实现错误;

  3. Python 实现或验证分类输出层;在其他条件相同的情况下,只改变一个需要研究的因素进行比较,并根据实验结果分析该因素可能带来的影响。

  本节沿用统一记号:普通小写字母表示标量,粗体小写字母表示向量,粗体大写字母表示矩阵或高阶张量,层编号写作上标 \([l]\)⁠。用 \(m\) 表示一个批量中的图片数,用 \(d_C^{[l]}\)⁠、\(d_H^{[l]}\)\(d_W^{[l]}\) 分别表示第 \(l\) 层输出的通道数、高和宽。第 \(l\) 层的卷积核尺寸、填充、步幅和空洞率分别写作 \(f_1^{[l]}\times f_2^{[l]}\)⁠、\(p_1^{[l]}\times p_2^{[l]}\)⁠、\(s_1^{[l]}\times s_2^{[l]}\)\(r_1^{[l]}\times r_2^{[l]}\)⁠;若两个方向设置相同,则分别简写为 \(f^{[l]}\)⁠、\(p^{[l]}\)⁠、\(s^{[l]}\)\(r^{[l]}\)⁠。除非另有说明,本节令 \(r_1^{[l]}=r_2^{[l]}=1\)⁠。程序采用 NCHW 排列,即批量轴位于最前。正文与练习中的程序都应同时检查数值结果和数组维度。本节练习的参考答案见 CNN 基本模块与维度传播答案⁠。

  本节把 二维卷积多通道卷积 组织为网络层;梯度计算见 卷积与池化的后向传播⁠。

  CNN 通常把卷积、归一化、激活和下采样组织成若干模块,再接任务相关的输出头。理解每一层时,应同时跟踪四件事:张量维度、感受野、参数量和是否引入非线性。

基本框架

  经典卷积神经网络通常由卷积层、池化层和全连接层组成。卷积层提取局部特征,池化层在每个通道内汇总局部信息并降低空间分辨率,全连接层或其他任务输出部分则根据已经提取的特征完成分类、检测或分割等任务。现代网络不一定同时使用这三类层,例如可以用带步幅的卷积代替池化,也可以用全局平均池化减小全连接部分的规模。

  随着网络加深,通道数 \(d_C^{[l]}\) 通常逐步增加,而特征图的高 \(d_H^{[l]}\) 和宽 \(d_W^{[l]}\) 通常逐步减小。这样的设计使网络在减少空间位置数量的同时,可以用更多通道记录不同类型的特征,但它不是所有网络都必须遵循的硬性规则。

  若要把第 \(L-2\) 层的特征图接入全连接层,可以对每个样本执行展平(flatten)。记第 \(i\) 个样本展平后的列向量为 \(\ba_i^{[L-1]}\)⁠,则得到下面这个二维矩阵:

\[\bA^{[L-1]} =[(\ba_1^{[L-1]})\trans;\ldots;(\ba_n^{[L-1]})\trans] =\operatorname{Flatten}\!\left(\bA^{[L-2]}\right) \in\mathbb{R}^{m\times \left(d_C^{[L-2]}d_H^{[L-2]}d_W^{[L-2]}\right)}.\]

展平只改变元素的排列方式,不引入新的模型参数。展平后的元素个数必须由前一层的实际输出维度计算,不能凭经验写成一个固定数值。

卷积层#

  先考虑单输入通道、单输出通道以及步幅 1、无填充、空洞率 1 的情形。把线性输出和激活输出分开记为

\[\begin{split}\begin{aligned} z_{ij}^{[l]} &=b^{[l]}+\sum_{u=1}^{f_1^{[l]}}\sum_{v=1}^{f_2^{[l]}} a_{i+u-1,j+v-1}^{[l-1]}w_{uv}^{[l]},\\ a_{ij}^{[l]}&=\sigma^{[l]}\!\left(z_{ij}^{[l]}\right). \end{aligned}\end{split}\]

  矩阵形式简记为

(85)#\[\bZ^{[l]}=\bA^{[l-1]}\ast\bW^{[l]}+b^{[l]}, \qquad \bA^{[l]}=\sigma^{[l]}\!\left(\bZ^{[l]}\right),\]

其中 \(\ast\) 表示本章采用的互相关运算,偏置 \(b^{[l]}\) 在同一输出通道的所有空间位置共享。

  对于包含 \(m\) 张图片的批量,第 \(l\) 个卷积层涉及的张量维度为

\[\begin{split}\begin{aligned} \bA^{[l-1]} &\in\mathbb{R}^{m\times d_C^{[l-1]}\times d_H^{[l-1]}\times d_W^{[l-1]}},\\ \bW^{[l]} &\in\mathbb{R}^{d_C^{[l]}\times d_C^{[l-1]}\times f_1^{[l]}\times f_2^{[l]}},\\ \bb^{[l]} &\in\mathbb{R}^{d_C^{[l]}\times1\times1},\\ \bZ^{[l]},\bA^{[l]} &\in\mathbb{R}^{m\times d_C^{[l]}\times d_H^{[l]}\times d_W^{[l]}}, \end{aligned}\end{split}\]

其中,\(\bW^{[l]}\) 中的维度的第一个分量 \(d_C^{[l]}\) 表示输出通道数,第二个分量 \(d_C^{[l-1]}\) 表示输入通道数。每个输出通道有一个偏置,它在该通道的所有空间位置和批量中的所有图片上共享。程序可以把偏置保存为长度为 \(d_C^{[l]}\) 的向量,参与计算时再将其理解为 \(d_C^{[l]}\times1\times1\) 并进行广播。

  第 \(l\) 层的批量前向传播可以简写为

\[\begin{split}\begin{aligned} \bZ^{[l]}&=\bA^{[l-1]}\ast\bW^{[l]}+\bb^{[l]},\\ \bA^{[l]}&=\sigma^{[l]}\!\left(\bZ^{[l]}\right). \end{aligned}\end{split}\]

若每个输出通道都使用一个偏置,则该层参数量为

\[d_C^{[l]} \left(d_C^{[l-1]}f_1^{[l]}f_2^{[l]}+1\right).\]

该参数量与输入的空间尺寸 \(d_H^{[l-1]}\times d_W^{[l-1]}\) 无关。若卷积后紧接 Batch Normalization,实践中常关闭卷积偏置,此时应去掉上式中的 1。

多输出通道卷积层

  卷积层的结构体现了我们对图像数据的两个基本认识:

  1. 局部连接⁠:每个输出位置只读取输入的一个局部窗口。堆叠多层后,深层单元的有效感受野逐步扩大。

  2. 参数共享⁠:同一个核在所有空间位置复用。这使卷积对平移近似等变,并显著减少参数量。

  参数共享不等于严格的平移不变性。边界填充、步幅和下采样都会影响等变性质;分类输出的近似不变性通常由全局池化和数据增强进一步获得。

输出尺寸#

  对第 \(l\) 个卷积层,输入特征图的高和宽为 \(d_H^{[l-1]}\)⁠、\(d_W^{[l-1]}\)⁠,卷积核尺寸为 \(f_1^{[l]}\times f_2^{[l]}\)⁠,填充为 \(p_1^{[l]}\times p_2^{[l]}\)⁠,步幅为 \(s_1^{[l]}\times s_2^{[l]}\)⁠,空洞率为 \(r_1^{[l]}\times r_2^{[l]}\)⁠。输出空间尺寸为

(86)#\[\begin{split}\begin{aligned} d_H^{[l]} &=\left\lfloor \frac{d_H^{[l-1]}+2p_1^{[l]} -r_1^{[l]}(f_1^{[l]}-1)-1}{s_1^{[l]}}+1 \right\rfloor,\\ d_W^{[l]} &=\left\lfloor \frac{d_W^{[l-1]}+2p_2^{[l]} -r_2^{[l]}(f_2^{[l]}-1)-1}{s_2^{[l]}}+1 \right\rfloor. \end{aligned}\end{split}\]

输出张量的完整维度为 \(m\times d_C^{[l]}\times d_H^{[l]}\times d_W^{[l]}\)⁠。实现网络时应逐层核对这个维度,特别是在卷积模块与全连接层相接的位置。

下采样

  增大卷积步幅、采用池化或其他重采样操作都可以减小空间分辨率。下采样减少计算量并扩大后续单元相对于原图的感受野,但也会丢失细粒度位置信息;检测和分割任务常通过多尺度特征或上采样路径补回细节。

池化层#

  池化在每个通道上独立汇总局部窗口,不含可训练权重。记第 \(l\) 个卷积层的激活输出为 \(\bA^{[l]}\)⁠,紧随其后的池化层输出为 \(\bP^{[l+1]}\)⁠。设池化窗口大小为 \(f_1^{[l+1]}\times f_2^{[l+1]}\)⁠、步幅为 \(s_1^{[l+1]}\times s_2^{[l+1]}\)⁠。不考虑填充时,第 \(c\) 个通道的最大池化与平均池化分别为

\[\begin{split}\begin{aligned} P_{cij,\max}^{[l+1]} &=\max_{\substack{1\leq u\leq f_1^{[l+1]}\\ 1\leq v\leq f_2^{[l+1]}}} A_{c,(i-1)s_1^{[l+1]}+u,(j-1)s_2^{[l+1]}+v}^{[l]},\\ P_{cij,\operatorname{avg}}^{[l+1]} &=\frac{1}{f_1^{[l+1]}f_2^{[l+1]}} \sum_{u=1}^{f_1^{[l+1]}}\sum_{v=1}^{f_2^{[l+1]}} A_{c,(i-1)s_1^{[l+1]}+u,(j-1)s_2^{[l+1]}+v}^{[l]}, \end{aligned}\end{split}\]

其中,池化不会把不同通道混在一起,因此 \(d_C^{[l+1]}=d_C^{[l]}\)⁠;它只根据窗口、填充和步幅改变高和宽。若池化不使用空洞窗口,其输出高和宽可以用式 (86) 计算,只需令两个方向的空洞率都为 1。

  最大池化保留窗口中的强响应,平均池化保留平均水平。池化本身没有参数,但会改变后续层的计算量和信息保留方式,也可能降低模型对轻微局部变化的敏感程度,因此不能简单地说它必然防止过拟合。现代分类网络也常使用带步幅卷积完成下采样,并在末端使用全局平均池化,把每个通道的整个特征图压缩为一个数。

从特征提取到任务输出#

卷积神经网络的基本结构

图 15 卷积神经网络的基本结构#

  1. 特征提取主干⁠:重复使用卷积块,通常在通道数增加的同时逐步降低空间分辨率。浅层卷积核通常更容易响应边缘、颜色变化和简单纹理;中间层可以组合这些局部响应,形成纹理片段、角点或物体局部结构;更深层的特征具有更大的感受野,因而能够组合出与任务相关的较复杂模式。这只是常见现象,不能把每一层机械地解释为固定语义。Zeiler 和 Fergus(2014) 通过特征可视化展示了这种由简单到复杂的层级变化。

  2. 任务输出头⁠:分类可采用全局平均池化加线性层;检测和分割会保留多尺度空间特征,并使用专门的预测头。全连接层不是所有 CNN 的必需组成,也不应笼统地称为模型的“记忆模块”。

  图片翻转、裁剪、缩放、颜色扰动和加入适量噪声等数据增强方法,可以从已有训练图片构造合理的变化版本,帮助模型减少对偶然位置、方向或颜色的依赖。数据增强只能用于训练集,并且变换后不应改变观测标签的含义。具体程序将在 用 ResNet-18 分类 CIFAR-10 中介绍。

  卷积与池化的后向传播 将从局部窗口的依赖关系出发,说明梯度怎样传播回输入和参数。

Shiny 交互演示:池化计算

  下面的交互页面可以比较最大池化和平均池化,并观察池化窗口、步长与填充怎样影响输出。页面会保留二维输出,给出输出维度和参数有效性提示,并说明最大池化与平均池化对填充值的不同处理方式。

点击打开“最大池化与平均池化”交互演示

核心推导与实现核验#

核心关系

\[\begin{split}\begin{aligned} \bZ^{[l]} &=\operatorname{Conv}\!\left(\bA^{[l-1]};\bW^{[l]}\right)+\bb^{[l]},\\ \bA^{[l]}&=\sigma^{[l]}\!\left(\bZ^{[l]}\right). \end{aligned}\end{split}\]

  推导路径。 卷积产生线性响应,偏置按输出通道广播,激活逐元素作用;池化只改变预定空间窗口,展平后才接全连接分类输出层。

关键条件

  最大池化在每个窗口选择最大值,因此对窗口内元素的任意排列保持输出不变,但窗口移动或边界变化仍可改变结果。

数据规模

  卷积和池化都不会改变一个批量中的图片数 \(m\)⁠。卷积层可以改变通道数以及图像的高和宽,池化层通常保持通道数不变并缩小高和宽;送入全连接层前,每个样本需要展平为 \(d_C^{[l]}d_H^{[l]}d_W^{[l]}\) 个数,这个数必须根据前一层的实际输出计算。

常见误区

  如果在代码中直接填写卷积输出展平后的长度,那么输入图像大小、卷积核、填充或步幅发生变化后,全连接层的输入维度就可能不再匹配。池化通常只在特征图的高度和宽度方向进行,并分别处理每个通道;如果错误地在通道方向上池化,就会混合或丢失不同通道记录的特征。

动手检查

  用全 1 输入、单位脉冲输入和非对称核核对索引、核方向与输出尺寸,再与框架 conv2d 在相同约定下比较。

本节小结#

  1. CNN 模块的连接必须保持批量、通道和空间轴语义。

  2. 池化无参数但会改变信息与梯度路径。

  3. 分类输出层输入维度应从实际特征图推导。

综合练习#

  程序题应固定随机种子、写出维度断言并报告运行环境;比较题还应固定数据划分、随机种子集合和训练预算。全部参考答案见 CNN 基本模块与维度传播答案⁠。

  1. 维度与参数量计算。 输入维度为 \(m\times3\times32\times32\)⁠,依次经过“8 个 \(3\times3\)⁠、步幅 1、填充 1 的卷积核”“\(2\times2\)⁠、步幅 2 的池化”“16 个 \(3\times3\)⁠、步幅 1、填充 1 的卷积核”“全局平均池化”和 10 类线性层。写出每层输出维度并计算含偏置的总参数量。

  2. 感受野计算。 从感受野 \(r_0=1\)⁠、相邻特征点在输入上的间隔 \(j_0=1\) 出发,使用 \(r_l=r_{l-1}+(f^{[l]}-1)j_{l-1}\)⁠、\(j_l=j_{l-1}s^{[l]}\)⁠,计算“\(3\times3\) 卷积、\(2\times2\) 步幅 2 池化、\(3\times3\) 卷积”之后的感受野和间隔。

  3. 池化具体计算。\(4\times4\) 特征图 \(\begin{bmatrix}1&2&3&4\\5&6&7&8\\9&10&11&12\\13&14&15&16\end{bmatrix}\)⁠,分别计算核大小 \(2\times2\)⁠、步幅 2 的最大池化和平均池化输出。

  4. 池化性质。 证明最大池化对同一个池化窗口内部元素的任意排列保持不变;再构造一个最小例子说明,当输入平移导致窗口划分改变时,整个池化输出不一定保持不变。

  5. 模型与维度跟踪实现。Python 实现第 1 题的小型 CNN,并编写维度跟踪器记录每一层的输入/输出维度、参数量和中间激活元素数;分类输出层输入维度应由虚拟前向传播确定,而不是手工写死。

  6. 框架对照。 用卷积与池化输出尺寸公式手工计算第 5 题所有维度,再与框架前向钩子的记录逐层比较;手工统计参数量并与 sum(p.numel() for p in model.parameters()) 核对。

  7. 测试设计。 测试不同批量大小不改变除批次轴外的维度、最小合法空间尺寸能够前向、错误通道数会失败、全局平均池化输出与手工均值一致,以及一个前向—损失—后向步骤产生与所有可训练参数同形的有限梯度。

  8. CNN 与 MLP 比较。 在同一图像分类数据上比较小型 CNN 与参数量尽量接近的 MLP。固定训练/验证/测试划分、随机种子集合、训练预算和超参数选择预算;报告任务性能、训练时间、固定批量预测时间、参数量及峰值内存。

  9. 下采样方法比较。 在同一 CNN 中比较最大池化、平均池化和步幅卷积,并尽量匹配输出维度与模型容量。固定数据划分、随机种子集合和训练预算;报告任务性能、训练时间、固定批量预测时间、参数量、乘加量及峰值内存。

  10. 网络宽度比较。 把两个卷积层的通道数同时乘以 \(0.5,1,2\)⁠,其他结构不变。固定数据划分、随机种子集合、优化器和训练预算;报告任务性能、训练时间、固定批量预测时间、参数量及峰值内存,并讨论容量收益与计算代价。