Chapter 6 卷积神经网络(CNN)(三)

前文我们基本实现了互相关函数,并探讨了一下如何对卷积运算进行矩阵乘法的优化以避免逐元素相乘带来的性能开销,但到目前为止,我们所有的卷积操作都是在单通道的情况下进行的,这显然不够普适化,因为即使是标准的 RGB 通道基本输入输出通道都应该是 3 ( 或者 4 – Alpha )条,因此我们需要将输入、卷积核和输出进行扩展,增加通道维度(channel dimension)以进行操作。

先透个底

“多通道输入是为了匹配输入张量,多通道输出相当于控制卷积核数量”

多通道输入

当输入包含多个通道时,此时卷积核也需要与输入数据具有相同输入通道数。此时的互相关运算变成窗口的切片与卷积核中对应的切片进行互相关运算后求和。

我们考虑一个简单的例子来说明这点:

输入张量 X: 假设我们有一个2通道的微小“图像”,尺寸为 2x3x3(此处为方便表述采用通道优先格式:$[C_{in}, H, W]$, 下同)。假设批量大小 N=1。

$$X[0,:,:] = \begin{bmatrix}1 & 2 & 3 \\4 & 5 & 6 \\7 & 8 & 9 \\ \end{bmatrix} \quad X[1,:,:] = \begin{bmatrix}10 & 20 & 30 \\40 & 50 & 60 \\70 & 80 & 90 \\ \end{bmatrix}$$

卷积核 K: 一个卷积核,其尺寸必须与输入的通道数匹配,即 $1\times 2 \times 2\times 2 ([C_{out}, C_{in}, k_H, k_W]$)。

$$K[0,0,:,:] = \begin{bmatrix}1 & 2 \\3 & 4 \\ \end{bmatrix} \quad K[0,1,:,:] = \begin{bmatrix}5 & 6 \\ 7 & 8 \\ \end{bmatrix}$$

超参数: 步幅 stride=1,填充 padding=0。

我们的目标是计算输出 Y,易得此时 Y 的规模将为 $[1, 2, 2]([C_{out}, H, W])$

为节约篇幅,考虑 $y_{00}$ 和 $y_{01}$:

$y_{[0, 0,0]}$

  • 定位输入窗口:
    • 在 $X_0$ 上,窗口是 $\begin{bmatrix} 1 & 2 \\ 4 & 5 \end{bmatrix}$
    • 在 $X_1$ 上,窗口是 $\begin{bmatrix} 10 & 20 \\ 40 & 50 \end{bmatrix}$
  • 逐通道进行乘加运算:
    • 通道 0: $X_0^{window} \odot K_0 = \begin{bmatrix} 1 & 2 \\ 4 & 5 \end{bmatrix} \odot \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} = \begin{bmatrix} 1\times1 & 2\times2 \\ 4\times3 & 5\times4 \end{bmatrix} = \begin{bmatrix} 1 & 4 \\ 12 & 20 \end{bmatrix}$
      • 对该矩阵所有元素求和: $1 + 4 + 12 + 20 = 37$
    • 通道 1: $X_1^{window} \odot K_1 = \begin{bmatrix} 10 & 20 \\ 40 & 50 \end{bmatrix} \odot \begin{bmatrix} 5 & 6 \\ 7 & 8 \end{bmatrix} = \begin{bmatrix} 10\times5 & 20\times6 \\ 40\times7 & 50\times8 \end{bmatrix} = \begin{bmatrix} 50 & 120 \\ 280 & 400 \end{bmatrix}$
      • 对该矩阵所有元素求和: $50 + 120 + 280 + 400 = 850$
  • 跨通道求和:

$Y[0,0,0] = 37 (来自通道0) + 850 (来自通道1) = \boxed{887}$

$y_{[0,0,1]}$

  • 定位输入窗口 (窗口向右移动1步):
    • $X_0$ 窗口: $\begin{bmatrix} 2 & 3 \\ 5 & 6 \end{bmatrix}$
    • $X_1$ 窗口: $\begin{bmatrix} 20 & 30 \\ 50 & 60 \end{bmatrix}$
  • 计算:
    • 通道 0: $\begin{bmatrix} 2 & 3 \\ 5 & 6 \end{bmatrix} \odot \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} = \begin{bmatrix} 2 & 6 \\ 15 & 24 \end{bmatrix} \rightarrow sum = 2+6+15+24 = 47$
    • 通道 1: $\begin{bmatrix} 20 & 30 \\ 50 & 60 \end{bmatrix} \odot \begin{bmatrix} 5 & 6 \\ 7 & 8 \end{bmatrix} = \begin{bmatrix} 100 & 180 \\ 350 & 480 \end{bmatrix} \rightarrow sum = 100+180+350+480 = 1110$
  • 跨通道求和:

$Y[0,0,1] = 47 + 1110 = \boxed{1157}$

所以以此类推,最终 $Y$ 为:

$$Y = \begin{bmatrix}887 & 1157 \\1697 & 1967 \\\end{bmatrix}$$

同样考虑 im2col 优化,原理是相同的,先将 $K$ 和 $X$ 向量化(即展平和拼接)然后相乘即可,给出关键描述:

$$K\rightarrow \vec{K} = [1,\ 2,\ 3,\ 4,\ 5,\ 6,\ 7,\ 8]$$

$$x_1(2\times 2\times 2) \rightarrow \vec{x_1} = [1,\ 2,\ 4,\ 5,\ 10,\ 20,\ 40,\ 50]^T\quad \dots$$

$$X’ = [\vec{x_1}, \vec{x_2}, \vec{x_3}, \vec{x_4}]$$

$$\vec{y} = \vec{K} \cdot X’$$

$$\vec{y}\rightarrow Y$$

多通道输出

多输出通道的本质非常简单:使用多个不同的卷积核。每个卷积核独立地在输入上执行操作,产生一个独立的2D输出特征图。所有这些特征图堆叠在一起,就构成了多通道的输出。

我们沿用之前的例子并加以扩展:

  • 输入 $X$ : 形状 $[2, 3, 3] (2个通道,3×3)
  • 卷积核 $K$ : 现在我们使用 2个 卷积核,形状为 $2\times 2 \times 2\times 2 ([C_{out}, C_{in}, k_H, k_W]$)
  • 超参数: 步幅 stride=1,填充 padding=0。

此时的输出 $Y$ 应该为 $[2, 2, 2]$

首先展平卷积核构造权重矩阵 $W$

注意此处我们不再只处理一个核,而是处理所有卷积核。

处理第一个卷积核 (K[0]): 将其两个通道展平并拼接成一个行向量 $\vec{K_0}$。

$\vec{K_0} = \text{flatten}(K[0,0,:,:]) + \text{flatten}(K[0,1,:,:])$

假设 $K[0] = \begin{bmatrix} [[1,2],[3,4]] & [[5,6],[7,8]] \end{bmatrix}$, 则 $\vec{K_0} = [1,2,3,4,5,6,7,8]$

处理第二个卷积核 (K[1]): 同样操作,得到另一个行向量 $\vec{K_1}$。

假设 $K[1] = \begin{bmatrix} [[9,10],[11,12]] & [[13,14],[15,16]] \end{bmatrix}$, 则 $\vec{K_1} = [9,10,11,12,13,14,15,16]$

构建权重矩阵 W: 将这两个(或多个)行向量堆叠起来,形成一个矩阵。

$$W = \begin{bmatrix}\vec{K_0} \\ \vec{K_1} \\ \end{bmatrix} = \begin{bmatrix}1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \\9 & 10 & 11 & 12 & 13 & 14 & 15 & 16 \\\end{bmatrix}$$

矩阵 W 的形状是 $[C_out, C_in \cdot k_H \cdot k_W] = [2, 8]$。它的每一行代表一个完整的、展平后的卷积核。

$X$ 的处理不变,那么进行矩阵乘法可得:

$$Y_{\text{flat}} = W \cdot X’$$

  • $W$ 的形状是 [2, 8]
  • $X’ $ 的形状是 [8, 4]
  • 结果 $Y_{\text{flat}}$ 的形状是 [2, 4]

$$Y_{\text{flat}} = \begin{bmatrix}1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 \\9 & 10 & 11 & 12 & 13 & 14 & 15 & 16 \\ \end{bmatrix} \cdot \begin{bmatrix}\vec{x_1} & \vec{x_2} & \vec{x_3} & \vec{x_4} \\ \end{bmatrix} = \begin{bmatrix}y_{0,1} & y_{0,2} & y_{0,3} & y_{0,4} \\y_{1,1} & y_{1,2} & y_{1,3} & y_{1,4} \\ \end{bmatrix}$$

此时一个矩阵乘法就同时完成了所有卷积核在所有位置上的计算。

reshape 和 add bias 的操作在此就不再赘述了。

所以回头再看

“多通道输入是为了匹配输入张量,多通道输出相当于控制卷积核数量”

这句话就相当好理解了

$1\times 1$ 卷积层

d2l 书中在这块还提及了一个东东,就是 $1\times 1$ 的卷积层,在分析 MLP 和 CNN 的差别(见仓库的 Solution.md )时我们就曾分析过,得出的结论是相当于做了一次全连接,那么$1\times 1$ 的卷积层功能就只有这些嘛?

显然是废话,要真只有这些拿出来说干什么,对于 $1\times 1$ 的卷积层,其最常用,也是最重要的功能是——允许网络灵活地减少或增加输出通道数

首先是降维:

  • 假设上一层的输出是$[256, H, W]$(256个通道)。但下一层可能不需要这么多特征图,或者为了减少计算量。
  • 我们使用一个 $1\times 1$ 卷积核,其数量(即输出通道数 $C_{out})设置为 $64$。这个卷积核的形状是$[64, 256, 1, 1]$。
  • 效果:输出变为$[64, H, W]$。计算量大幅降低,因为后续的卷积层(如 $3×3$)现在只需要在64个通道上操作,而不是256个。这就像是一个“瓶颈层”(Bottleneck Layer),是ResNet等模型减少参数和计算量的关键技巧。

升维同理,例子里面 64 和 256 换个位置就成

所以 1×1 卷积实际上是在每个像素点的位置上(共 $H\cdot W$ 个位置),对所有的 $C_{in}$ 个通道的值进行了一次加权求和(线性组合),从而生成一个新的通道值。一个 1×1 卷积核就定义了一套权重,来生成一个新的特征图。$C_{out}$ 个核就定义了 $C_{out}$ 套权重,从而生成 $C_{out}$ 个新的特征图

此外 $1\times 1$ 卷积层还有一个作用:引入非线性层,实现这个我们只需要在它后面跟一个激活函数就行

写在最后 – CNN 的信息丢失和保留

关于 CNN 中频繁的升降维操作,我曾经有一个疑问,频繁的维度调整是否意味着信息特征的丢失和陷入幻想?,查了些资料问了下几个 ai 汇总了一下得到了如下结果:

首先,简单的降维(比如直接丢弃一些通道)肯定会丢失信息。CNN之所以能成功,是因为它并非“盲目”地降维,而是通过学习结构设计来尝试智能地处理这个问题。

但我们同时需要明晰——CNN并不能绝对保证信息不丢失,但它通过以下几种强大的机制来优化信息保留与压缩的权衡,让丢失的信息尽可能多是“无用”或“冗余”的,而补充的信息是“有意义”的。

核心机制:学习得到的加权融合

这是最核心的一点。回想一下 1x1 卷积进行降维的过程:

  • 简单丢弃:如果我们想从256维降到64维,最笨的方法是直接扔掉192个通道。这无疑会丢失大量信息。
  • CNN的做法:它使用一个 1x1x256 的卷积核来生成1个输出通道。这个核的256个权重,学习了如何将256个输入通道的信息进行最优的线性组合。它可能会学到:
    • “这三个通道都表示同一种横向边缘,我可以把它们合并成一个。”
    • “这个通道噪音很大,我给它一个很小的权重,几乎忽略它。”
    • “这两个通道的特征是互补的,一个激活时另一个不激活,我需要把它们加起来。”

最终,输出通道不是某个原始输入通道的副本,而是所有输入通道信息的一个“摘要”或“精华浓缩版”。 它丢失的是冗余和噪音,试图保留的是最有用的综合信息。

这不像从一本书中撕掉几页(丢弃通道),而是像让一位专家(网络通过训练学习)阅读整本书后,写出一份更精炼的摘要报告(降维后的特征)。摘要肯定会丢失细节,但目标是保留核心思想。

非线性激活函数(ReLU等)的引入

单纯的 1x1 卷积只是线性组合。在其后立即添加非线性激活函数(如ReLU)至关重要。

  • 作用:它允许网络学习非线性的跨通道交互关系
  • 为什么重要:特征之间的关系往往不是线性的。例如,“只有当通道A激活度高通道B激活度低时,这个特征才重要”。ReLU等函数让网络能够模拟这种复杂的、非线的“与/或”逻辑,从而在降维的同时创造出更丰富、更具判别性的新特征。它不是在压缩信息,而是在转化和精炼信息

分布式表征与冗余性

神经网络本质上是一个分布式表征系统。

  • 含义:任何单一概念(如“猫耳朵”)都不是由某一个特定的神经元(或通道)编码的,而是由大量神经元的活动模式共同编码的。
  • 好处:这意味着信息在通道之间是高度冗余的。同一个特征可能会被多个通道轻微地、从不同角度检测到。因此,通过加权融合将多个冗余通道合并成一个,可以很大程度上保留原始信息,同时消除冗余。

关于升维:补充的是“潜力”而非信息

升维(增加通道数)本身并不会凭空“创造”新的信息。它补充的是表征能力的潜力

  • 操作:升维可以看作是对现有信息的一种“扩展”或“投影”。它将当前信息投影到一个更高维的空间中。
  • 为什么有效:在高维空间中,数据更容易被线性分离(这是机器学习的核心思想之一,核方法也基于此)。新增加的通道提供了更多的“维度”或“角度”,让后续的层能够更容易地从中学习并构建出更复杂的特征。
  • 后续过滤:新增加的通道在初始时可能包含很多无效信息,但通过训练和反向传播,网络会学会抑制那些无用的通道(通过权重调整),并增强有用的通道。ReLU函数在这个过程中会直接将许多无效激活置为零,实现了动态的“补充有益信息,抑制无用信息”。

5. 跳跃连接(ResNet等):信息无损传输的“高速公路”

这是解决信息丢失问题的最革命性架构创新之一。跳跃连接(Skip Connection)直接将被降维之前的原始输入,绕过低维的“瓶颈”层,加到降维再升维后的结果上。

  • 公式Output = F(x) + x
    • x:原始输入(256维)。
    • F(x):经过一系列操作(如降维->卷积->升维)后的输出(256维)。
  • 保证:即使降维-升维过程 F(x) 丢失了大量信息(最坏情况是 F(x) = 0),最终的输出仍然至少包含原始输入 x 的全部信息。这从根本上杜绝了信息丢失的风险。
  • 角色分配:跳跃连接保证了基础信息的无损传输,而中间的“瓶颈”层 F(x) 则只需要学习在基础信息之上需要添加或修改的残差(Residual)。这极大地降低了学习的难度。

最终,网络通过端到端的训练,利用梯度下降和反向传播,自动学习出一套在“计算效率”和“信息保留”之间达到最佳平衡的降维/升维策略。它不是完美的,但是在实践中被证明极其有效的方案。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注