Skip to content

Convolutional Network

约 4258 个字 7 张图片 预计阅读时间 14 分钟

1. 深度网络中的卷积算子

1.1 全连接网络 (Fully Connected Networks) 的问题

到目前为止,我们接触的网络(如全连接网络,FCN)在处理图像时,通常会先将图像“展平”(flatten)成一个长长的一维向量。

|500

这种方法对于像MNIST这样的小尺寸、单通道图像(28x28)或许还行,但当面对更大、更复杂的图像时,会遇到两个主要问题:

  1. 参数量爆炸 (Parameter Explosion):
    * 一个256x256的RGB三通道彩色图像,展平后会变成一个维度大约为 \(256 \times 256 \times 3 \approx 20\)万 的输入向量。
    * 如果我们将这个20万维的输入映射到一个仅有1000个神经元的隐藏层,单单这一层就需要 \(20\text{万} \times 1000 = 2\text{亿}\) 个参数!
    * 如此巨大的参数量不仅计算成本高,而且极易导致模型过拟合。

  2. 无法捕捉图像的“直观不变性” (Fails to Capture "Intuitive" Invariances):
    * 图像具有空间结构。比如,一张猫的图片,无论猫在左上角还是右下角,它仍然是猫。这种平移不变性 (Translation Invariance) 是图像的一个核心特征。
    * 全连接网络破坏了这种空间结构。图像展平后,相邻的像素可能在向量中相距很远。如果将整个图像向右平移一个像素,对于全连接网络来说,输入向量会发生巨大变化,导致输出也可能完全不同。
    * 它为每个输入位置都学习一套完全独立的权重,没有利用到图像中“局部模式”(如边缘、角点)可以在不同位置重复出现的特性。

核心思想

深度学习架构设计的核心思想之一,就是将数据本身的结构特性(归纳偏置,Inductive Bias)嵌入到网络架构中。对于图像,最重要的结构就是空间局部性平移不变性。卷积网络正是为此而生。

1.2 卷积如何“简化”深度网络

卷积网络通过引入两个关键思想来解决全连接网络的问题:

|475

  1. 局部连接 (Local Connectivity): 网络中的每个神经元(或叫单元)只与前一层的一个局部区域内的神经元相连。 这就像我们看东西时,视野中的每个细胞只负责感知一小块区域一样。这背后是基于一个假设:图像中的信息是局部相关的
  2. 权重共享 (Weight Sharing): 用于连接一个局部区域的同一组权重(称为滤波器 Filter核 Kernel)会在整个图像的所有空间位置上重复使用。 这意味着网络用同一个“模式探测器”(例如,一个边缘检测器)去扫描整张图片,大大减少了参数数量,并且自然地实现了平移不变性。

隐藏层的形式

与全连接网络不同,卷积网络中的隐藏层不再是一维向量,而是保持了二维空间结构(通常还带有深度,即通道数),可以看作是一系列的“特征图”(Feature Maps)。

1.3 卷积计算步骤

卷积的核心操作是“滑动”一个 \(k \times k\) 的权重矩阵(滤波器 \(w\))覆盖在输入图像(或特征图 \(z\))上,以生成一个新的输出图像(特征图 \(y\))。数学上记为 \(y = z * w\)

卷积计算步骤 - 方法1

假设我们有一个5x5的输入 \(z\) 和一个3x3的滤波器 \(w\)

|550

  1. 计算第一个输出 \(y_{11}\):
    将滤波器 \(w\) 对准输入 \(z\) 的左上角3x3区域。将对应位置的元素相乘,然后求和。
    \(y_{11} = z_{11}w_{11} + z_{12}w_{12} + z_{13}w_{13} + z_{21}w_{21} + \dots + z_{33}w_{33}\)
    这本质上是两个矩阵(展平成向量后)的点积运算。

|550

  1. 计算第二个输出 \(y_{12}\):
    将滤波器 \(w\) 向右滑动一个单位,对准 \(z\) 中以 \(z_{12}\) 为左上角的新3x3区域,重复上述乘加运算。
    \(y_{12} = z_{12}w_{11} + z_{13}w_{12} + z_{14}w_{13} + z_{22}w_{21} + \dots\)

  2. 例如:计算 \(y_{21}\):

|500

  1. 重复操作:
    不断滑动滤波器,扫过所有可能的位置,直到生成完整的输出特征图 \(y\)
    对于一个5x5的输入和一个3x3的滤波器,输出的大小将是3x3。

卷积计算方法2:公式法

Step1: 翻转\(h[n]\)

Step2: 单元格内求和相加

术语说明:卷积 vs. 互相关

在严格的信号处理定义中,卷积操作在乘加之前需要将滤波器旋转180度(上下翻转再左右翻转)。我们在这里描述的、在深度学习中广泛使用的操作,实际上是互相关 (Cross-correlation)

不过,由于深度学习中的滤波器权重是学习出来的,翻不翻转对模型的表达能力没有影响(模型可以自己学会翻转后的权重)。因此,社区统一将这种不翻转的操作称为“卷积”。

1.4 卷积的优势

  1. 参数数量急剧减少 (Drastically Reduces Parameter Count):

参数对比

假设我们将一个256x256的灰度图映射到一个同样大小的单通道隐藏层:

  • 全连接网络: 需要 \((256 \times 256) \times (256 \times 256) \approx 40\text{亿}\) 个参数。
  • 3x3卷积: 由于权重共享,我们只需要一个 \(3 \times 3 = 9\) 个参数的滤波器即可!
    这种效率提升是惊人的。
  1. 捕捉“自然”不变性 (Captures "Natural" Invariances):
    * 由于使用了相同的滤波器在整个图像上滑动,如果输入图像中的一个模式(比如一只眼睛)向右平移了,那么在输出的特征图中,对应这个模式的激活值也会在相应的位置向右平移。这被称为平移等变性 (Translation Equivariance),是实现平移不变性的基础。

1.5 传统图像处理中的卷积

在深度学习兴起之前,卷积早已是计算机视觉和图像处理的核心工具。不同之处在于,那时的滤波器是人工设计的,用于实现特定功能。

  • 高斯模糊 (Gaussian Blur): 使用一个形状像高斯分布的滤波器,对每个像素及其邻域进行加权平均,从而使图像变得平滑模糊。
  • 图像梯度与边缘检测 (Image Gradient & Edge Detection): 使用像Sobel算子这样的滤波器,可以计算出图像在水平和垂直方向上的强度变化率(梯度)。梯度值大的地方通常对应着图像的边缘。

深度学习的突破在于,我们不再需要手动设计这些滤波器,而是让网络在训练过程中自动学习出对当前任务最有用的滤波器。

1.6 深度网络中的多通道卷积 (Multi-Channel Convolutions)

在实践中,卷积网络几乎总是处理多通道数据。

  • 输入: 比如彩色图像有红、绿、蓝3个通道 (\(c_{in}=3\))。
  • 隐藏层: 也可以有多个通道,比如64、128或更多 (\(c_{out}=64, 128, \dots\))。这些通道代表了模型学到的不同类型的特征。

工作方式:
从一个有 \(c_{in}\) 个通道的输入,生成一个有 \(c_{out}\) 个通道的输出,我们需要一个形状为 (\(c_{in}, c_{out}, k, k\)) 的4阶张量作为滤波器 \(W\)

要计算输出特征图的某一个通道(比如第 \(s\) 个通道),我们需要:
1. 对每一个输入通道(第 \(r\) 个通道),都使用一个独立的 \(k \times k\) 滤波器 \(W[r, s, :, :]\) 进行卷积。
2. 将这 \(c_{in}\) 个卷积结果逐元素相加,得到最终的输出特征图的第 \(s\) 个通道。
公式为: \(z[:, :, s] = \sum_{r=1}^{c_{in}} x[:, :, r] * W[r, s, :, :]\)

一个更直观的理解方式:矩阵-向量形式

我们可以把多通道卷积看作是传统卷积的扩展,其中标量乘法被替换为了矩阵-向量乘法

[Image of Slide 16]

  • 输入图像的每个像素: 不再是一个数值,而是一个长度为 \(c_{in}\)向量 (例如,RGB值)。
  • 滤波器的每个位置: 不再是一个权重值,而是一个 \(c_{out} \times c_{in}\)权重矩阵
  • 卷积操作: 在每个滑动位置,我们用滤波器的权重矩阵去乘以输入的像素向量,得到一个长度为 \(c_{out}\) 的输出向量。这些矩阵-向量乘积的结果再相加,就构成了输出特征图的一个像素(它也是一个向量)。

这种视角对于理解和实现高效的卷积运算至关重要。

2. 实践中卷积的要素

为了让卷积在深度网络中更实用、更灵活,我们通常会加入以下几个元素。

2.1 填充 (Padding)

  • 挑战: "朴素"的卷积会导致输出特征图的尺寸比输入小。经过多层卷积后,特征图会变得非常小,丢失边缘信息。
  • 解决方案: 在输入图像的边界周围填充一些值(通常是0)。
    * 对于一个奇数尺寸的 \(k \times k\) 滤波器,如果在四周各填充 \((k-1)/2\) 个像素,输出特征图的尺寸将与输入完全相同。 这种操作通常被称为 "Same" Padding
    * 例如,对于3x3滤波器,我们在四周各填充1个像素。对于5x5滤波器,各填充2个像素。

[Image of Slide 18]

2.2 步幅卷积 (Strided Convolutions) 与 池化 (Pooling)

  • 挑战: 普通的"Same" Padding卷积会保持分辨率不变。但在很多任务中,我们希望网络能够逐渐降低空间分辨率(下采样),同时增加通道数,从而构建一个包含从低级到高级特征的层次化表示。下采样也能有效减少计算量。
  • 解决方案 #1: 池化 (Pooling)
    * 池化是一种独立的下采样层,它将输入的某个区域(例如2x2)的信息聚合成一个单一的输出值。
    * 最大池化 (Max Pooling): 在区域内取最大值。它对特征的位置有很强的鲁棒性,能捕捉到最显著的特征。
    * 平均池化 (Average Pooling): 在区域内取平均值。它能保留更多的背景信息。
  • 解决方案 #2: 步幅卷积 (Strided Convolutions)
    * 在滑动滤波器时,不每次只移动一个像素,而是移动多个像素。这个移动的步长称为步幅 (Stride)
    * 如果步幅为2,滤波器会跳着滑动,生成的输出特征图在每个维度上的尺寸大约会减半。

[Image of Slide 19]

2.3 分组卷积 (Grouped Convolutions)

  • 挑战: 当输入和输出通道数非常大时(例如上千),即使是卷积,参数量和计算量也可能变得非常庞大。
  • 解决方案: 将输入和输出通道分成若干个组 (Groups)。卷积只在对应的组内进行。
    * 例如,如果有4个输入通道和4个输出通道,分成2组。那么前2个输入通道只与前2个输出通道进行卷积,后2个输入通道只与后2个输出通道进行卷积。
    * 这相当于将原本密集的 \(c_{out} \times c_{in}\) 权重矩阵变成了块对角矩阵,从而减少了参数。
    * 一个极端情况是,当分组数等于通道数时,称为深度分离卷积 (Depthwise Separable Convolution) 中的 深度卷积 (Depthwise Convolution),每个通道独立进行卷积。

[Image of Slide 20]

2.4 扩张/空洞卷积 (Dilated / Atrous Convolutions)

  • 挑战: 小尺寸的滤波器(如3x3)的感受野 (Receptive Field) 很小,意味着每一层只能看到非常局部的输入信息。要获得大的感受野,需要堆叠很多层,计算成本高。
  • 解决方案: 在滤波器的权重之间插入“空洞”,从而在不增加参数数量的情况下扩大其覆盖范围。
    * 这个“空洞”的大小由扩张率 (Dilation Rate) 控制。扩张率为1即为标准卷积。扩张率为2时,会在权重之间插入1个像素的空隙。
    * 这样,一个3x3的滤波器可以拥有5x5甚至更大的感受野。

[Image of Slide 21]


3. 卷积的微分 (Backward Pass)

为了将卷积集成到自动微分框架(如Needle)中,我们必须能够计算它的梯度。

为什么需要自定义梯度?

理论上,我们可以用基本的加法和乘法运算来构建卷积。但这样做会在计算图中产生海量的中间节点,导致内存消耗巨大,效率低下。因此,我们必须将卷积实现为一个原子操作 (atomic operation),并为其手动定义前向和反向传播规则。

3.1 回顾:线性运算的微分

让我们回顾一下矩阵-向量乘法 \(z = Wx\)
* 前向传播: 用矩阵 \(W\) 左乘输入向量 \(x\)
* 反向传播 (对 \(x\)求导): 根据链式法则,我们需要将上游传来的梯度(adjoint)左乘 \(W\) 的导数,即 \(W\) 本身。由于我们通常将梯度向量放在右边,这等价于用 \(W\) 的转置 \(W^T\) 左乘上游梯度向量。
* 核心结论: 对于一个线性算子,其反向传播(梯度计算)的核心是乘以该算子的转置 (Transpose) 或伴随 (Adjoint)

那么问题来了:一个卷积操作的“转置”是什么?

3.2 将卷积表示为矩阵乘法

为了找到答案,我们可以将卷积操作等效地写成一个巨大的矩阵乘法。

1D卷积的矩阵形式

考虑一个1D卷积,输入为 [x1, x2, x3, x4, x5],滤波器为 [w1, w2, w3],并进行了零填充。

我们可以构建一个特殊的矩阵 \(\hat{W}\),使得卷积操作等价于 \(\hat{W}x\)

[Image of Slide 25]

$
\begin{bmatrix} z_1 \ z_2 \ z_3 \ z_4 \ z_5 \end{bmatrix} =
\begin{bmatrix}
w_2 & w_3 & 0 & 0 & 0 \
w_1 & w_2 & w_3 & 0 & 0 \
0 & w_1 & w_2 & w_3 & 0 \
0 & 0 & w_1 & w_2 & w_3 \
0 & 0 & 0 & w_1 & w_2
\end{bmatrix}
\begin{bmatrix} x_1 \ x_2 \ x_3 \ x_4 \ x_5 \end{bmatrix}
$

这个由滤波器权重构成的、具有对角线常数结构的稀疏矩阵称为托普利茨矩阵 (Toeplitz Matrix)

注意: 这只是一个概念工具,我们不会在实践中真的去构造这个可能非常巨大的稀疏矩阵。

3.3 卷积的伴随(转置)

既然卷积可以表示为矩阵 \(\hat{W}\),那么它的转置就是 \(\hat{W}^T\)。让我们把上面例子中的 \(\hat{W}\) 转置一下:

[Image of Slide 26]

\[ \hat{W}^T = \begin{bmatrix} w_2 & w_1 & 0 & 0 & 0 \\ w_3 & w_2 & w_1 & 0 & 0 \\ 0 & w_3 & w_2 & w_1 & 0 \\ 0 & 0 & w_3 & w_2 & w_1 \\ 0 & 0 & 0 & w_3 & w_2 \end{bmatrix} \]

关键发现!

仔细观察 \(\hat{W}^T\),它也是一个托普利茨矩阵!它代表了另一个卷积操作。

  • 原始卷积使用的滤波器是 [w1, w2, w3]
  • \(\hat{W}^T\) 对应的卷积,其有效滤波器是 [w3, w2, w1]——正是原始滤波器翻转 (flipped) 后的结果!

结论:
* 对于输入 \(x\) 的反向传播: 计算上游梯度与卷积操作对 \(x\) 的导数之积,等价于用翻转后的滤波器对上游梯度图进行一次常规卷积
* 这也被称为转置卷积 (Transposed Convolution)反卷积 (Deconvolution) (后者是误称但常用),它在生成模型和分割任务中也很重要。

3.4 另一种矩阵形式 (im2col) 与对权重的微分

我们不仅可以把权重构造成矩阵,也可以把输入构造成矩阵。

im2col (Image to Column)

同样是上面的1D卷积例子,我们可以将输入 x 的局部区域提取出来,堆叠成一个矩阵 X_hat,使得卷积操作等价于 X_hat * w

[Image of Slide 27]

$
\begin{bmatrix} z_1 \ z_2 \ z_3 \ z_4 \ z_5 \end{bmatrix} =
\begin{bmatrix}
0 & x_1 & x_2 \
x_1 & x_2 & x_3 \
x_2 & x_3 & x_4 \
x_3 & x_4 & x_5 \
x_4 & x_5 & 0
\end{bmatrix}
\begin{bmatrix} w_1 \ w_2 \ w_3 \end{bmatrix}
$

这种将输入图像的局部块(patch)转换为矩阵列的操作,被称为 im2col

这种表示方法非常重要,原因有二:

  1. 对权重 \(W\) 的反向传播: 在这种形式下,输出 \(z\) 是矩阵 X_hat 与权重向量 \(w\) 的乘积。因此,对 \(w\) 的梯度计算就变成了用 X_hat 的转置去乘以上游梯度。
  2. 高效实现: 尽管 im2col 会复制数据,导致内存占用增加,但它将复杂的卷积操作转换成了一个标准、高度优化的通用矩阵乘法 (GEMM)。现代硬件(CPU/GPU)对GEMM的优化极致,因此 im2col + GEMM 是目前许多深度学习框架中实现卷积的最快方法之一

Comments