Skip to content

CNN

MLP到CNN

为什么不用MLP

在MLP的基础上,我们继续学习CNN。对于图像处理任务,MLP有一个先天性的缺陷:单一层的参数量太大了。假设图片有1M像素,那么输入维度就得有1M维。如果我们设置1000个神经元,那么我们可以算一下总参数量:

  • 输入维度是100万
  • 输出维度是1000

总参数量=100万x1000+1000≈10亿。

深度学习通常使用 float32(单精度浮点数),每个参数占 4 字节 (Bytes) 。我们用10亿x4字节,可以得到大约是4GB。这仅仅是一层的权重文件大小,如果是10层的话光权重就要40GB显存,这显然是不可接受的。

总结来说,虽然根据万能近似定理,MLP可以捕捉到特征并学会识图,但是MLP太过于自由的架构,会导致模型去尝试学习所有可能的特征,这会导致模型不仅学习猫的形状,还会关注不同的噪声。此外,没有局部性的指引,在海量的参数空间中寻找最优解如同大海捞针。

因此,CNN与MLP的本质区别并不在于是否能找到特征,而在于参数的效率。我们可以把CNN看作是一种特殊的、受限的MLP:其本质上就是把MLP的权重矩阵中的大部分位置强行抹成0,并让剩下的非零位置共用一个数值。这种参数的简化换来了一个巨大的优势:为堆叠更多层创造了基础,从而让深层网络的实现成为了可能。

核心假设

除了上述MLP的过大参数问题外,我们还要引入一个核心的致命问题:空间结构。MLP需要把图片拉平(Flatten)为向量,对于全连接层来说,不同的像素点之间并无关系,他们是完全平等的。

因此,在上述基础上,我们提出了卷积神经网络的架构。

CNN有两个最为核心的假设

  1. 像素和它周围的像素关系最密切(局部性,Locality
  2. 一只猫从左上角移动到右下角,它还是一只猫(平移不变性,Translation Invariance

上述先验假设是CNN最核心的两个归纳偏置,甚至可以说CNN架构本身就是为了把这两个偏见硬编码到数学公式里而发明的。这两个重要假设是CNN在视觉识别任务上打败MLP的重要原因。

这里提到归纳偏置,归纳就是说从个别推导出一般,比如如果你看了1万张猫的照片,发现猫都是白的,即可总结出猫是白的这一法则,或者说一般规律;偏置则是指一种倾向性,或者说预设约束,比如你去找相亲对象的时候设定身高必须要180cm以上,这就是偏置。为了让模型从有限的经验(归纳)中更有效地学到规律,我们预先给它设置的某种约束或偏好,就是归纳偏置。

如果没有这种偏置,对于同一个数据集,模型就会变成一个只会死记硬背的学生。

当下大模型时代更流行的ViT的偏置比CNN更少,但是能捕捉到比CNN更加复杂、更加灵活的特征,因此上限超过了CNN。

数学原理

在普通的多层感知机(MLP)里,输入通常是一维向量。但图像是二维的(有行 \(i\) 和列 \(j\))。

  • 输入 \(X\) 一个矩阵,位置是 \((k, l)\)
  • 输出 \(H\) 也是一个矩阵,位置是 \((i, j)\)

在全连接的情况下,输出图像的每一个像素 \((i, j)\),就是一个独立的神经元 。如果输出图像是 \(n \times n\),那么我们就拥有 \(n^2\) 个神经元。每一个输出神经元(输出像素),都要伸出“触手”去连接每一个输入像素。既然输入图像有 \(n^2\) 个像素,那么每一个输出神经元身上都会连着 \(n^2\) 根线(权重)。

我们用数学公式来表示上面的连接,就是:

\[ [\mathbf{H}]_{i,j} = [\mathbf{U}]_{i,j} + \sum_k \sum_l [\mathbf{W}]_{i,j,k,l} [\mathbf{X}]_{k,l} \]

在上式中,权重W是一个四阶张量,\(W_{i,j,k,l}\)表示输入位置\((k,l)\)对输出位置\((i,j)\)的贡献权重。这里参数量巨大,在计算上并不现实。

那么我们来做一个变量替换:\(k = i + a\)\(l = j + b\)**** :

\[ [\mathbf{H}]_{i,j} = [\mathbf{U}]_{i,j} + \sum_a \sum_b [\mathbf{V}]_{i,j,a,b} [\mathbf{X}]_{i+a,j+b} \]

此时变量 \(a, b\) 的意义是不再考察图像上的绝对坐标,而是考察相对于当前中心点的偏移量。\([\mathbf{V}]_{i,j,a,b}\) 现在表示“在输出位置为 \((i,j)\) 时,距离它偏移量为 \((a,b)\) 的输入像素的权重”。

虽然数学上 \(W\)\(V\) 是等价的(只是索引方式变了),但这个转换是为了引出卷积神经网络(CNN)的两个核心假设:

第一个就是平移不变性(Translation Invariance)。

如果我们认为“不管在图像的哪个位置 \((i, j)\),检测特征的方法应该是相同的”,那么权重 \(V\) 就不应该依赖于 \((i, j)\)。这意味着在位置 \((i, j)\) 使用的权重,和在其他任何位置使用的权重应该是 同一套 。从而原本依赖于具体位置的四维张量 \([\mathbf{V}]_{i,j,a,b}\) 变成了只依赖于偏移量的二维张量 \([\mathbf{V}]_{a,b}\)。这就是 参数共享

\[ [\mathbf{H}]_{i,j} = u + \sum_{a} \sum_{b} [\mathbf{V}]_{a,b} [\mathbf{X}]_{i+a, j+b} \]

换句话说,无论你在看图片的哪个像素点 \((i, j)\),你都用同样的“放大镜”(卷积核 \(\mathbf{V}\))去扫描它周围的像素。

第二个就是局部性(Locality)。

为了判断位置 \((i, j)\) 处是什么东西,你通常只需要看它周围的一小块像素。你不需要看图片最左边的像素来判断最右边的一颗螺丝钉。原本的求和 \(\sum\) 是针对整张图的,现在规定当偏移量 \(a\)\(b\) 超过一定范围(比如 \(\Delta\))时,权重直接设为 0。

如此一来,我们只需要在一个很小的窗口(即卷积核的大小,如 \(3 \times 3\)\(5 \times 5\))内进行计算。

\[ [\mathbf{H}]_{i,j} = u + \sum_{a=-\Delta}^{\Delta} \sum_{b=-\Delta}^{\Delta} [\mathbf{V}]_{a,b} [\mathbf{X}]_{i+a, j+b} \]

上面在数学上说明了卷积核(Kernel)存在的必然性。换句话说,上面的数学推到说明了,在处理图像时,最理想的权重结构就应该是卷积核这种形式。


那么接下来我们看一下卷积在数学上的定义。

  • \(f\) :你可以把它看作输入数据(比如一张图片)。
  • \(g\) :你可以把它看作一个过滤器/核(比如提取边缘的算子)。
  • 操作过程 :就是让过滤器在图片上滑动,把重叠部分的数值相乘再相加,最终得到一张特征图(Feature Map)。

首先给出了连续和离散情况下的标准数学定义:

  • 连续形式
\[ (f * g)(\mathbf{x}) = \int f(\mathbf{z})g(\mathbf{x} - \mathbf{z})d\mathbf{z} \]
  • 离散形式 (一维):
\[ (f * g)(i) = \sum_{a} f(a)g(i - a) \]
  • 二维形式(常用于图像处理):
\[ (f * g)(i, j) = \sum_{a} \sum_{b} f(a, b)g(i - a, j - b) \]

卷积实际上是把其中一个函数(通常是核函数 \(g\))进行翻转,然后在另一个函数 \(f\)滑动测量它们在不同位移下的重叠程度。

在深度学习的代码实现中,我们通常做的操作其实叫 互相关(Cross-correlation)互相关的索引方式 通常是 \((i + a, j + b)\),而非卷积的\((i - a, j - b)\)。在严格的数学卷积中,必须对核进行翻转(那个负号的由来)。但在深度学习中,卷积核的参数是模型自动学习出来的。如果翻转核能得到更好的效果,模型自然会学出一个“翻转后”的权重。因此,为了实现上的简洁,深度学习框架(如 PyTorch, TensorFlow)大多直接实现“互相关”,但在术语上依然统称为“卷积”。


最后,我们还要说明“通道”(Channels)的概念。

由于输入图像通常是三维的(如 RGB 图像有红、绿、蓝 3 个通道),网络内部的隐藏层也采用三维张量。每一层隐藏表示由多个二维网格堆叠而成,这些网格被称为“通道”或“特征映射”。

直观上,靠近输入的底层通道专门识别简单的几何特征(如 边缘 ),而更高层的通道则识别更复杂的模式(如纹理或物体的局部)。

为了支持多个输入和输出通道,权重 \(\mathsf{V}\) 引入了第四个维度:

\[ [\mathbf{H}]_{i,j,d} = \sum_{a=-\Delta}^{\Delta} \sum_{b=-\Delta}^{\Delta} \sum_{c} [\mathsf{V}]_{a,b,c,d} [\mathbf{X}]_{i+a, j+b, c} \]
  • 索引 \(d\)* :表示 *输出通道 。每一个 \(d\) 都对应一个独特的卷积核,负责生成输出张量中的一层。
  • 索引 \(c\)* :表示 *输入通道 。卷积核会同时对输入的所有通道进行卷积并求和。
  • 权重 \(\mathsf{V}\)**** :在这里是一个四维张量,形状通常为 (核高, 核宽, 输入通道数, 输出通道数)

卷积核

对于一张黑白照片,我们可以用3x3的卷积核。这个时候,我们把卷积的一次操作就可以想象成盖章,一次印章会盖在9个点上,然后我们可以得到一个数字。

对于一张RGB图像,由于RGB图像有3个通道(红绿蓝),因此为了匹配这三个通道,每一个卷积核本身也必须有3层。这个“ \(3 \times 3 \times 3\) ”的卷积核方块在图像上滑动。它会同时看红、绿、蓝三个层,把这 27 个数字(\(3 \times 3 \times 3\))与对应像素相乘并 全部相加 ,最后只产生一个数值。

这里要注意,彩色照片本质上相当于三张照片,分别是RGB三张网格。我们可以想象这三张照片堆叠在一起,然后我们用盖章插进去,那么它就像是一个厚饼干同时盖住了RGB三种颜色各自的9个点。我们把这9x3=27个点所对应的权重全部相乘然后加在一起,最后依然只得到了一个数。

换句话说,无论核是如何设计的,我们的目标就是让一个核只吐出一个数。

我们把这个核在图像上滑动,滑动完毕后这些由核产生的数字就排列成了一张平面的特征映射(feature map)。

一个核可以得到一张特征图,在深度学习中,我们希望一个卷积层能 同时提取多种特征 。因此,我们一般会使用多个不同的核,来得到多个不同的特征图。

比方说,我们用64个核来分别学习识别不同的特征,然后这 64 个不同的核 每一个都会产生一张特征图。把这 64 张特征图叠在一起,就形成了前面章节中提到的 隐藏表示 \(\mathbf{H}\)**** ,它拥有 64 个通道。


我们暂时忽略第三维(通道)的情况,以二维图形为例。下面是二维互相关运算:1770785839570

我们在这里的计算过程是让核所盖住的区域的点分别和核区域中所对应的数字相乘,然后把结果加起来,即:0x0+1x1+3x2+3x4=19。

卷积层

我们在上面已经了解了卷积核是什么。我们也提到,想要识别、提取多个不同的特征,我们需要多个卷积核。

在神经网络中,我们把多个卷积核放在一层中,组成卷积层。

在实际搭建网络时,卷积层的设置通常遵循以下三个逻辑:

  1. 深度增加,宽度增加。越靠近输入(底层)的地方,卷积核数量一般越少,因为这个时候图片还很大。越靠近输出的地方,卷积核一般越多,此时图片尺寸被压缩的也很小,因此此时主要目的是组合出更加复杂的抽象语义。
  2. 感受野(Receptive Field):通过堆叠多个 \(3 \times 3\) 的卷积层,网络可以看到越来越大的图像区域。两层 \(3 \times 3\) 卷积合起来的效果,在视野上相当于一层 \(5 \times 5\),但参数更少,非线性表达更强。
  3. 一般我们使用GPU进行运算,因此通道数设置为8的倍数能让GPU跑的更快,因为这更加符合NVIDIA显卡的内存对齐和Tensor Core的运算逻辑。

表征与涌现

表征就是把“原始数据”(Raw Data)转换成“机器容易理解的形式”(Features/Vectors)。

表征

让我们用一个直观的例子: 识别一张“猫”的照片

原始数据(Input)

  • 对计算机来说,这就是一堆数字矩阵(比如 \(224 \times 224 \times 3\) 的像素点)。
  • 这些数字非常 混乱 。稍微移动一下猫的位置,或者调暗一点光线,所有的像素值都会变。
  • 在这个层面上,计算机根本看不出“猫”和“狗”的区别。

良好的表征(Good Representation)

  • 假设经过一个 CNN 网络,到了最后一层,这堆像素变成了一个 向量 (比如 128 维的数组)。
  • 这个向量里的每一个数字,不再代表像素亮度,而是代表“语义特征”
    • 第 1 位:有没有尖耳朵?(1=有,0=无)
    • 第 2 位:是不是毛茸茸?
    • 第 3 位:有没有胡须?
  • 这个向量,就是这张图的“表征”。

核心逻辑:

  • 像素空间 :猫和狗的图片混在一起,无法用一条直线分开(线性不可分)。
  • 表征空间 :猫的向量都聚在一起,狗的向量都聚在一起。很容易分开(线性可分)。

如果你理解了表征,你就理解了深度学习相对于传统机器学习的 降维打击

传统机器学习(Old School)

  • 做法手工设计表征 (Hand-crafted Features)
  • 痛苦点 :想识别猫?你需要自己写算法提取边缘(Sobel)、提取纹理(Gabor)、提取关键点(SIFT)。这些特征提取器是人设计的,很僵化。一旦换成识别“鸟”,你得重新设计一套。
  • 瓶颈人类的智慧限制了模型的上限。

深度学习(Deep Learning)

  • 做法端到端学习 (End-to-End Representation Learning)
  • 革命性 :我们不再告诉模型“去找边缘”或“去找纹理”。我们只给它原始像素和标签(猫/狗)。网络自己去寻找最好的表征方式(即我们之前讨论的 CNN 层级涌现)。
  • 优势机器学到的表征,往往比人类设计的更抽象、更鲁棒、更好用。

可以把“表征”看作是网络中间层的 输出向量 。常见的表征表示方式包括:

Embedding (嵌入)

  • 在 NLP 里,把一个单词("Apple")变成一个向量。这个向量就是它的表征。
  • 如果表征学得好,"Apple" 和 "Orange" 的向量距离会很近,而 "Apple" 和 "Car" 会很远。

Latent Code (潜在编码)

  • 在 VAE 或 Autoencoder 里,中间那个被压缩的向量 \(z\) 就是表征。
  • 它代表了数据的“本质”。

Feature Map (特征图)

  • 在 CNN 中,每一层的输出都是一种表征。
  • 浅层表征:线条、颜色。
  • 深层表征:眼睛、耳朵。

表征学习

简单来说,表征学习(Representation Learning) 就是让机器自动寻找“如何描述数据”最好的过程。

为了让机器识别图片里的“猫”,有两种做法:

  • 传统做法(人工特征工程): 科学家告诉计算机:“猫有尖耳朵、长胡须、圆眼睛”。程序员需要写复杂的算法去定义什么是“尖”、什么是“圆”。这就是 人工设计表征 。缺点是极其僵硬,换个角度或光线,程序就失效了。
  • 表征学习(深度学习): 你不告诉计算机猫长什么样,只给它看几万张猫的照片。计算机通过隐藏层的权重调整,自发发现了“边缘”、“弧线”、“毛发纹理”等特征。它自己学会了如何把一张复杂的像素图,浓缩成一组高效的数学向量。

表征学习本质上是在做特征提取降维

  • 原始数据(低级表征): 是一堆毫无意义的像素点(RGB数值),数据量极大且杂乱。
  • 学习后的数据(高级表征): 是神经网络高层的一串数字。这串数字可能代表了“这里有一个圆形”或“这里有轮廓”。

1770171499668

虽然CNN的表征学习最为明显,但其实所有深度神经网络(DNN)都在遵循该哲学。

在深度学习流行之前,机器学习主要依靠Shallow Models(浅层模型),它们的特点是层数非常少(通常只有 1-2 层隐藏层,甚至没有) 。

虽然通用近似定理在数学上证明了一个包含单个隐藏层且具有足够多神经元的反馈神经网络,可以以任意精度近似任何闭区间内的连续函数,但在现实中,自然界的逻辑本身就是多层递进、层层抽象的。正如著名学者Yoshua Bengio所说:“深度架构具有一种能够更好表达自然界复杂性的先验(Prior)。” 这种先验就是图中所见的层次结构。

这就好比我们用加法可以代替所有的乘法,但是我们依然需要乘法。不同的工具在实践中作用差别是很大的。

内部表示的涌现

在CNN中,我们预先设计了成长规则,即浅层卷积核很小,并逐步由浅到深。CNN架构本身并没有设计不同层级的发现目标,但是我们设计出的CNN架构却后验地发现了组合型(Compositionality)。换句话说,CNN自己总结出了物理世界的一些普遍规律。如果我们把CNN内部学到的特征反画出来,我们会发现浅层卷积提取的是边缘、线条、纹理等,而深层卷积是别的事眼睛、轮子、完整的鸟等。

CNN通过大量的图片学会了生物眼睛神经系统的能力——这是一种涌现(Emergence) 现象。因为我们只设计了一层一层的堆叠结构,然而CNN自己却后验地发现一层层识别可以让Loss最小并且这是唯一可行的路径——这是神经网络发展史上第一次出现显著的组合涌现(Compositional Emergence),即层级化(Hierarchy)

  • 1987 (NetTalk) :涌现出了“元音/辅音”这种 单一概念
  • 2012 (AlexNet) :涌现出了一个 完整的进化树
    • Layer 1: 物理层(线条)。
    • Layer 2: 几何层(圆圈、纹理)。
    • Layer 3: 部件层(眼睛、轮子)。
    • Layer 4: 语义层(猫、车)。

这种 从物理到语义的连续进化过程 ,是人类第一次在机器里清晰地看到“认知过程”的具象化。

CNN核心组件

经过多年的发展,从LeNet-5到ResNet,现代CNN核心组件基本确定了下来。我们对这些内容进行总结。

Conv Layer

Convolution Layer是核心计算单元,是CNN的基础,负责局部特征提取。

卷积层的本质作用:

  • 空间特征提取
  • 权值共享
  • 局部感受野建模

Activation

ReLU 的引入极大提升了深层网络的可训练性(如在 AlexNet 中)。

常见形式:

  • ReLU
  • Leaky ReLU
  • GELU
  • SiLU / Swish

Batch Normalization

ResNet引入了BatchNorm并证明了BN的重要性。

常见类型:

  • BatchNorm(BN)
  • LayerNorm
  • GroupNorm

用于:

  • 稳定训练
  • 加速收敛
  • 缓解梯度爆炸/消失

Downsampling

用于扩大感受野和降低计算量:

  • MaxPooling
  • AveragePooling
  • Stride > 1 的卷积

现代架构更倾向用 stride convolution 代替 pooling。

Skip Connection

ResNet中提出的残差网络彻底改变了CNN,证明了网络可以很深。

优势:

  • 改善梯度流动
  • 允许构建极深网络(>100层)

在ResNet之前,过深但无残差的堆叠结构会导致梯度消失和性能退化的问题,比如:20+ 层纯卷积 + ReLU(无 skip)。在ResNet之后这种结构几乎消失。

现代 CNN 很少使用大规模全连接层,而是:

  • Global Average Pooling (GAP)
  • 全连接层(小规模)
  • Softmax / 任务输出层

典型结构:

Feature Map
   ↓
Global Avg Pool
   ↓
Linear
   ↓
Softmax

退役组件

在CNN的发展中,一些引领一时的组件已经基本宣告退役。

Sigmoid/Tanh

这两个激活函数是最早退役的,并且ReLU的出现才算是现代CNN的开始。

大规模全连接层 Large Fully Connected Layers

代表结构包括AlexNet, VGGNet,特点是

  • 4096 × 4096 级别 FC
  • 参数占比极高(>80%)
  • 强依赖 Dropout 防止过拟合

为什么退役:

  • 参数效率极低
  • 易过拟合
  • 不利于迁移学习

现代替代:

  • Global Average Pooling(GAP)
  • 轻量级线性层

Dropout

Dropout 在:

  • FC 层非常有效
  • 小数据场景有效

但在现代 CNN 中:

  • 有 BatchNorm
  • 有数据增强
  • 有残差结构
  • 有更强正则化(如 weight decay)

Dropout 反而:

  • 破坏 BN 统计
  • 降低收敛速度

所以在 CNN 主干中常被移除,只在:

  • 小数据
  • Transformer
  • Head 层

仍然常见。

LRN

Local Response Normalization主要出现在:

  • AlexNet

作用:

  • 模拟神经元抑制
  • 早期稳定训练

为什么退役:

  • BatchNorm 效果更好
  • 收敛更快
  • 理论基础更清晰

现在几乎完全被 BatchNorm / GroupNorm 替代。

Pooling主导的下采样结构

例如:

  • VGG 风格大量 MaxPooling
  • 固定位置降采样

问题:

  • 信息损失不可逆
  • 不可学习

现代趋势:

  • Strided Convolution 替代 pooling
  • 有时直接移除 pooling

评论 #