卷积神经网络(CNN)

参考资料:CS231n Deep Learning for Computer Vision

CNN 核心思想与架构

CNN与普通神经网络的异同:

  • 相同点:
    • 由带可学习权重 (weights)偏置 (biases) 的神经元组成。
    • 整个网络是一个可微的得分函数
    • 最后一层(全连接层)拥有损失函数(如 SVM/Softmax)。
    • 所有普通神经网络的训练技巧和窍门都适用。
  • 不同点:
    • CNN 明确假设输入是图像,并基于此对网络架构进行了优化。
    • 相比于普通神经网络,CNN 更高效,且参数数量大大减少,有效防止过拟合。

架构概述

普通神经网络的局限性

参数量巨大。对于图像输入,普通神经网络的全连接方式会导致参数量爆炸。如, 一个 200x200x3 的图像,在第一个隐藏层的一个神经元就需要 200 * 200 * 3 = 120,000 个权重。这使得网络难以训练且容易过拟合 (overfitting)

CNN 中神经元的 3D 结构

在普通神经网络(或者前面说的全连接神经网络)中,神经元通常以一维的形式排列,而CNN 将其层中的神经元排列成三维结构:宽度 (width)高度 (height)深度 (depth)。每一层都是将一个 3D 的输入数据卷(input volume)转换为一个 3D 的输出数据卷(output volume)。在CNN中,一个神经元只与前一层的一个小区域相连,而不是全连接,这极大地减少了参数。

  • 输入层: 一张 32×32 的彩色图片,其输入卷的维度是 32x32x3 (宽x高x深度/颜色通道)。
  • 输出层: 对于 CIFAR-10 (10分类) 任务,输出卷的维度是 1x1x10,代表10个类别的得分。

卷积神经网络(神经元以3D形式排列)


常规神经网络

构建 CNN 的核心层 (Layers)

一个 CNN 架构就是一系列层的组合,主要有以下四种类型:

卷积层 (Convolutional Layer – CONV)

  • 功能: 从输入卷中计算局部区域的特征。
  • 工作方式: 使用一组滤波器 (filters)卷积核 (kernels),每个滤波器与输入卷的一个局部区域进行点积运算,生成一个二维的特征图 (feature map)
  • 输出: 如果使用 12 个滤波器处理 32x32x3 的输入,输出卷的维度将是 32x32x12。输出的深度由滤波器的数量决定。
  • 包含参数: 是(滤波器的权重和偏置)。
  • 有超参数: 是(如滤波器大小、步长 stride、填充 padding)。

归一化层 (Normalization Layer)

此处以批归一化(Batch Normalization, BN)为例

  • 功能: 稳定并标准化每一层网络接收到的输入数据分布,从而加快模型训练速度、提升训练稳定性,并允许使用更高的学习率。
  • 工作方式: 在训练和推理时行为不同,具体见后面。
  • 输出: 不改变输入卷的尺寸,只改变其数值的分布。
  • 包含参数: 是。包含可学习的缩放因子 $\gamma$ 和平移因子 $\beta$ 。
  • 有超参数: 是。包含动量因子momentum和用于防止出现除零错误的小数值 $\epsilon$ 。

池化层 (Pooling Layer – POOL)

  • 功能: 对空间维度(宽度和高度)进行下采样 (downsampling),以减少数据量和计算量,并提供一定程度的平移不变性。
  • 工作方式: 常见的有最大池化 (Max Pooling),即在局部区域内取最大值。
  • 输出: 减小输入卷的宽度和高度。例如,对 32x32x12 的输入进行 2×2 的池化,输出变为 16x16x12
  • 包含参数: 否。
  • 有超参数: 是(如池化窗口大小、步长)。

全连接层 (Fully-Connected Layer – FC)

  • 功能: 通常位于网络的末端,用于整合前面卷积和池化层提取的特征,并计算最终的类别得分 (class scores)
  • 工作方式: 与普通神经网络中的层完全相同,每个神经元都与前一层的所有神经元相连。
  • 输出: 将高维特征图展平 (flatten) 为一维向量,并最终输出为 1x1xN 的形式,其中 N 是类别数。
  • 包含参数: 是(权重和偏置)。
  • 有超参数: 是(如输出神经元数量)。

一个简单的 CNN 架构流程

以 CIFAR-10 图像分类任务为例:

[输入] -> [卷积层] -> [激活层] -> [池化层] -> [全连接层] -> [输出]

  • INPUT [32x32x3]: 原始图像像素。
  • CONV [32x32x12]: 使用12个滤波器提取特征。
  • RELU [32x32x12]: 应用非线性激活,尺寸不变。
  • POOL [16x16x12]: 空间下采样,尺寸减半。
  • FC [1x1x10]: 计算10个类别的最终得分。

通过这一系列操作,CNN 成功地将原始的、高维的像素数据,逐层转换为抽象的特征表示,并最终得到分类结果。

构成CNN的层

卷积层(Convolutional Layer)

卷积层到底在做什么?下面是一个简单的动画演示。

卷积层 (Convolutional Layer) 是卷积神经网络中最为核心的模块,它承担了绝大部分的计算任务。其核心思想是通过一组可学习的滤波器 (filters) 或称为卷积核 (kernels) 来提取输入特征。

每个滤波器在空间维度上(宽度和高度)通常很小,但它一定会贯穿输入数据卷的整个深度。例如,对于一个 32x32x3 的彩色图像输入,一个典型的滤波器尺寸可能是 5x5x3。在前向传播过程中,这个滤波器会滑动 (或称之为“卷积”) 遍历输入数据卷的整个空间范围,在每一个位置,计算滤波器自身的权重与输入数据在该区域对应值的点积。这个过程会为该滤波器生成一张二维的激活图 (activation map),图中的每一个值代表了滤波器在对应位置的响应强度。

直观地看,网络在训练过程中会学习到能够识别特定视觉特征的滤波器。例如,在网络的底层,滤波器可能对某些方向的边缘、特定的颜色斑块或纹理产生强烈激活;而在更高层,滤波器则能识别出更复杂的组合模式,如眼睛、车轮或蜂窝状结构。一个卷积层通常包含多组滤波器(例如 12 个),每个滤波器都会生成一张独立的激活图。最后,这些激活图会沿着深度方向堆叠起来,形成一个三维的输出数据卷。

卷积层有两个重要的特征:

  • 局部连接 (Local Connectivity) :输出数据卷中的每个神经元只与输入数据卷的一个局部小区域相连接,这个局部区域被称为该神经元的感受野 (receptive field),其大小由卷积核尺寸(F)决定。这种连接方式与全连接层形成鲜明对比,极大地减少了连接数量。
  • 参数共享 (Parameter Sharing): 使CNN能够大幅减少参数量。它基于一个合理的假设:如果一个特征(比如一个垂直边缘)在图像的某个位置被检测到是有用的,那么它在其他位置也同样有用。因此,在同一张激活图(即一个深度切片)内的所有神经元,都共享同一套卷积核权重和偏置。这样,网络就不再需要为输出的每一个位置都学习一套独立的权重,而只需要为每一个激活图学习一套权重即可。

示例:在某次训练中学习到的96个卷积核

从上图中可以看到,不同的卷积核负责捕捉不同的特征。

卷积层有四个可调整的超参数(hyperparameter),它们共同决定了输出数据卷的尺寸。

  1. 卷积核的数量 $K$ :决定了输出数据的深度(depth)
  2. 卷积核的尺寸 $F$ :即感受野的大小,例如 3x35x5
  3. 步长( $S$ , stride):卷积核在输入数据上滑动的步长。
  4. 零填充( $P$ , padding):在输入数据卷的边界周围填充零,可以用来控制输出的空间尺寸。一个常见的做法是当步长 $S=1$ 时,设置 $P=\frac{F-1}{2}$ ,这样可以确保输出数据卷的宽度和高度与输入完全相同。

若输入数据尺寸为 $W_{in}$ ,输出数据尺寸为 $W_{out}$,则可以证明:$$W_{out}=\frac{W_{in}-F+2P}{S}+1$$例如,对于一个 7×7 的输入和一个 3×3 的滤波器,步长为 1 且填充为 0 时,我们会得到一个 5×5 的输出。步长为 2 时,我们会得到一个 3×3 的输出。
需要注意的是,根据上述公式算出的 $W_{out}$ 必须是整数,否则会认为是无效的超参数设置。

卷积层的梯度计算

卷积层的反向传播也是一个计算卷积的过程,详细推导过程见下面的资料。

CNN_Backprop_Recitation_5_F21.pdf

卷积层的前向传播可以表示为:

$$Z = X * W + b$$

其中:

  • X: 输入特征图
  • W: 卷积核(权重)
  • b: 偏置
  • *: 卷积操作
  • Z: 层的输出(由于没有激活函数,Z就是最终输出)

反向传播的目标是:已知损失函数 $L$ 对本层输出 $Z$ 的梯度 $\frac{\partial L}{\partial Z}$,计算损失函数对输入 $X$、权重 $W$ 和偏置 $b$ 的梯度。

这个梯度 $\frac{\partial L}{\partial Z}$ 是由下一层计算并传递过来的,是我们进行反向传播的起点。

下面分别计算三个梯度:$\frac{\partial L}{\partial W}$、$\frac{\partial L}{\partial X}$ 和 $\frac{\partial L}{\partial b}$。

1. 计算对权重(卷积核)的梯度 $\frac{\partial L}{\partial W}$

公式:

$$\frac{\partial L}{\partial W} = X * \frac{\partial L}{\partial Z}$$
这个计算相当于执了一次新的卷积,其中:

  • 输入 是前向传播时的原始输入 X
  • 卷积核 是我们已知的输出梯度 $\frac{\partial L}{\partial Z}$。

直观理解:
在前向传播时,卷积核 W 的某个权重 $w_{ij}$ 会滑过整个输入 X,与对应的输入像素相乘,并将结果累加到输出 Z 的不同位置。根据链式法则,要计算 $L$ 对 $w_{ij}$ 的梯度,就需要把所有这些贡献路径上的梯度都加起来。这恰好等同于用输入 X 和输出梯度 $\frac{\partial L}{\partial Z}$ 做卷积。

2. 计算对输入的梯度 $\frac{\partial L}{\partial X}$

公式:

$$\frac{\partial L}{\partial X} = \frac{\partial L}{\partial Z} *_{full} W_{rot180}$$
这个计算稍微复杂一些,它也像一次卷积,但有两点不同:

  • 卷积核 是前向传播的卷积核 W 旋转180度 后的结果,记为 $W_{rot180}$。
  • 卷积类型“全卷积”(Full Convolution),这在实践中通常通过对输入(即 $\frac{\partial L}{\partial Z}$)进行特定的零填充(padding)来实现。

直观理解:
在前向传播时,输入 X 中的一个像素会影响输出 Z 中的一个区域(这个区域大小与卷积核大小相同)。因此,在反向传播时,这个区域的所有梯度都应该被“贡献”回这一个输入像素的梯度上。将卷积核翻转,并用它对输出梯度图进行卷积,正好能实现这种“贡献”的正确映射。如果前向传播有步长(stride),这一步还需要在 $\frac{\partial L}{\partial Z}$ 的元素间插入0,这也是转置卷积的一部分。

3. 计算对偏置的梯度 $\frac{\partial L}{\partial b}$

公式:

$$\frac{\partial L}{\partial b} = \sum_{i,j} \left( \frac{\partial L}{\partial Z} \right)_{i,j}$$

解释:
这是最简单的。在前向传播时,偏置 b 的值被加到了输出 Z 的每一个元素上。因此,它的梯度就是输出梯度 $\frac{\partial L}{\partial Z}$ 中所有元素之和。

若不考虑激活函数,卷积层的反向传播可以总结为以下三步:

目标梯度计算方式解释
对权重 $\frac{\partial L}{\partial W}$输入 卷积 输出梯度X 作为输入,用 $\frac{\partial L}{\partial Z}$ 作为卷积核。
对输入 $\frac{\partial L}{\partial X}$输出梯度 全卷积 权重用 $\frac{\partial L}{\partial Z}$ 作为输入,用旋转180度的 W 作为卷积核。
对偏置 $\frac{\partial L}{\partial b}$输出梯度 求和将 $\frac{\partial L}{\partial Z}$ 的所有元素相加。

卷积层的反向传播在数学上依然是卷积运算,只是输入和卷积核的角色发生了互换或转换。

池化层(Pooling Layer)

在卷积神经网络(ConvNet)架构中,通常会在连续的卷积层之间周期性地插入一个池化层。它的作用是逐步减小表示的空间尺寸,以减少网络中的参数和计算量,从而也有助于控制过拟合。池化层独立地对输入的每个深度切片进行操作,通常使用最大池化(max pooling)对输入数据进行空间尺寸调整。最常见的池化层形式是使用 2×2 的过滤器,步长为 2,这种池化层会沿输入的宽度和高度方向对每个深度切片进行下采样,每次下采样会丢弃 75%的激活值,也就是参数数量变为原来的1/4。在这种情况下,每个最大值操作会从某个深度切片中的一个小 2×2 区域中取 4 个数字的最大值。深度维度保持不变。下面是一个最大池化操作的示例。

可以看到,在每个2×2方格中,我们只保留最大值,把剩下的三个值丢弃。
池化层中需要两个hyper parameter,分别是空间范围 $F$ 和步长$S$ 。空间范围就是上图中小方格的尺寸,步长的含义与卷积层中相同。

归一化层(Normalization Layer)

批归一化(Batch Normalization, BN)

核心思想

Batch Normalization的出现是为了解决内部协变量偏移(Internal Covariate Shift, ICS)问题。这个问题的大致意思是,在训练过程中,由于前面网络层的参数在不断更新,导致后面网络层接收到的输入数据的分布在持续不断地剧烈变化。这种变化带来了几个严重的问题:

  • 训练速度慢:后面几层网络需要不断去适应这种变化,导致学习效率低下,收敛速度很慢。
  • 对参数敏感:需要非常小心地选择学习率和参数的初始值,否则网络很容易梯度爆炸或消失,训练过程极不稳定。
  • 梯度饱和问题:对于像Sigmoid这样的激活函数,如果输入数据偏移到了函数两端的饱和区,梯度会变得非常小,网络几乎无法学习。

BN的思想是在网络的每一层插入一个归一化步骤,在每 一层都强行把数据分布稳定下来。BN层最常见的放置位置是在卷积层(Conv)或全连接层(FC)之后,非线性激活函数(如ReLU)之前。这样可以对即将进入激活函数的分布进行归一化,以防数据落入激活函数的饱和区(如ReLU的负半轴),从而最大化地保留信息,让梯度流动更顺畅。

BN带来的主要优势有:

  1. 显著加快训练速度:通过稳定数据分布,使得网络可以使用更高的学习率,收敛速度可以提升数倍甚至数十倍。
  2. 提升模型稳定性:大大降低了对参数初始化的敏感度,让训练过程更加稳定,更容易复现。
  3. 自带正则化效果:由于每一批的均值和方差都略有不同,这为网络的学习过程引入了轻微的噪声,这种噪声在一定程度上起到了类似Dropout的正则化作用,有助于防止模型过拟合。

BN在训练和测试(推理)过程的行为是不同的。

训练模式 (Training Mode)

在训练时,数据是成批(mini-batch)送入网络的。BN对当前这一小批数据进行如下操作:
第一步:计算批内统计量
对于当前批次的数据,计算出每一个特征维度(对于图像,就是每一个通道,也就是同一depth的值组成的矩阵)的均值 $\mu$ 和方差 $\sigma^2$。
第二步:归一化
使用上面计算出的均值和方差,对数据进行标准化,使其分布近似为均值为0,方差为1。其中$$\hat{x}_i = \frac{x_i – \mu}{\sqrt{\sigma^2 + \epsilon}}$$
第三步:缩放与平移(Scale and Shift)
如果强行把每层的数据都变成标准正态分布,可能会破坏网络从上一层学到的特征信息。因此,BN引入 $\gamma$(缩放因子)和 $\beta$(平移因子)来给网络一个“反悔”的机会。归一化后的数据再经过一次线性变换:$$y_i=\gamma\hat{x_i}+\beta$$ $\gamma$ 和 $\beta$ 是这个BN层的参数,它们会像网络的权重一样,通过反向传播不断地被学习和更新。
第四步:更新全局统计量
在训练的每一步,BN层还会维护一个全局的均值和方差(running_mean 和 running_var)。这个全局值是通过动量(momentum)不断平滑更新每一批的统计量得到的,它并不参与训练时的计算,而是为测试时做准备。
这个过程由一个超参数——动量(momentum)来控制。momentum通常是一个接近1的数,比如0.9, 0.99。
更新公式如下:

  • running_var = momentum * running_var + (1 - momentum) * batch_var
  • running_mean = momentum * running_mean + (1 - momentum) * batch_mean

其中

  • running_mean:BN层内部维护的全局均值(旧值)。
  • batch_mean当前这个小批次数据计算出的均值。
  • running_var:BN层内部维护的全局方差(旧值)。
  • batch_var当前这个小批次数据计算出的方差。
  • momentum:可以理解为“惯性因子”。

每一次更新,新的全局均值 running_mean 都是由 绝大部分的旧全局均值一小部分的当前批次均值 组合而成的。它不会因为某一个批次的数据分布很奇特就发生剧烈改变,而是会缓慢、稳定地逼近整个数据集的真实分布。

测试/推理模式 (Inference/Test Mode)

在测试或实际使用模型时,我们通常一次只处理一个样本,没有“批”的概念,无法计算均值和方差。这时,训练时保存的全局统计量就派上用场了。BN层会直接使用这些固定的、在整个训练过程中学到的全局 running_meanrunning_var 来对测试数据进行归一化,然后再进行同样的缩放和平移(使用训练好的 $\gamma$ 和 $\beta$ )。

反向传播与梯度计算
计算图法

红色为前向传播路径,绿色为反向传播路径,可以看到BN的计算图非常复杂

已知与目标

  • 已知 (上游梯度):损失函数 L 对BN层输出 Y 的梯度,对于每一个样本 i,我们有 $\frac{\partial L}{\partial y_i}$。
  • 目标 (待求梯度)
    1. 对可学习参数的梯度:$\frac{\partial L}{\partial \gamma}$ 和 $\frac{\partial L}{\partial \beta}$。
    2. 对输入的梯度(需要继续反向传播):$\frac{\partial L}{\partial x_i}$。

推导过程

首先明确BN的前向传播过程:
$$\mu = \frac{1}{N} \sum_{i=1}^{N} x_i$$$$v=\sigma^2 = \frac{1}{N} \sum_{i=1}^{N} (x_i – \mu)^2$$$$\sigma=\sqrt{\sigma^2 + \epsilon}$$$$\hat{x}_i = \frac{x_i – \mu}{\sigma}$$$$y_i = \gamma \cdot \hat{x}_i + \beta$$

我们从输出端开始,沿着计算图一步步向输入端反向计算。

第一步:计算对 $\gamma$, $\beta$ 和 $\hat{x}_i$ 的梯度

基于前向传播的最后一步:$y_i = \gamma \hat{x}_i + \beta$

  1. 对平移参数 $\beta$ 的梯度
    $$
    \frac{\partial L}{\partial \beta} = \sum_{i=1}^{N} \frac{\partial L}{\partial y_i} \frac{\partial y_i}{\partial \beta} = \sum_{i=1}^{N} \frac{\partial L}{\partial y_i} \cdot 1 = \sum_{i=1}^{N} \frac{\partial L}{\partial y_i}
    $$
  2. 对缩放参数 $\gamma$ 的梯度
    $$
    \frac{\partial L}{\partial \gamma} = \sum_{i=1}^{N} \frac{\partial L}{\partial y_i} \frac{\partial y_i}{\partial \gamma} = \sum_{i=1}^{N} \frac{\partial L}{\partial y_i} \hat{x}_i
    $$
  3. 对归一化数据 $\hat{x}_i$ 的梯度(中间变量,用于后续计算)
    $$
    \frac{\partial L}{\partial \hat{x}_i} = \frac{\partial L}{\partial y_i} \frac{\partial y_i}{\partial \hat{x}_i} = \frac{\partial L}{\partial y_i} \gamma
    $$

第二步:计算对标准差 $\sigma$ 的梯度

基于前向传播公式:$\hat{x}_i = \frac{x_i – \mu}{\sigma}$

由于标准差 $\sigma$ 影响了所有的 $\hat{x}_i$,我们需要将所有路径的梯度累加起来。 $$ \frac{\partial L}{\partial \sigma} = \sum_{i=1}^{N} \frac{\partial L}{\partial \hat{x}_i} \frac{\partial \hat{x}_i}{\partial \sigma} = \sum_{i=1}^{N} \frac{\partial L}{\partial \hat{x}_i} \left( -\frac{x_i – \mu}{\sigma^2} \right)
$$

第三步:计算对方差 $\sigma^2$ 的梯度

基于前向传播公式:$\sigma = \sqrt{\sigma^2 + \epsilon}$
$$
\frac{\partial L}{\partial \sigma^2} = \frac{\partial L}{\partial \sigma} \frac{\partial \sigma}{\partial \sigma^2} = \frac{\partial L}{\partial \sigma} \cdot \frac{1}{2\sqrt{\sigma^2 + \epsilon}} = \frac{\partial L}{\partial \sigma} \cdot \frac{1}{2\sigma}
$$

第四步:计算对均值 $\mu$ 的梯度

这是一个关键的梯度汇集点,均值 $\mu$ 通过两条路径影响了最终的损失(直接影响 $\hat{x}_i$,以及通过影响 $\sigma^2$ 间接影响 $\hat{x}_i$)。

  1. 来自 $\hat{x}_i$ 的路径
    $$
    \left( \frac{\partial L}{\partial \mu} \right)_1 = \sum_{i=1}^{N} \frac{\partial L}{\partial \hat{x}_i} \frac{\partial \hat{x}_i}{\partial \mu} = \sum_{i=1}^{N} \frac{\partial L}{\partial \hat{x}_i} \left( \frac{-1}{\sigma} \right)
    $$
  2. 来自 $\sigma^2$ 的路径
    $$
    \left( \frac{\partial L}{\partial \mu} \right)_2 = \frac{\partial L}{\partial \sigma^2} \frac{\partial \sigma^2}{\partial \mu} = \frac{\partial L}{\partial \sigma^2} \left( \frac{1}{N} \sum_{i=1}^{N} 2(x_i – \mu)(-1) \right) = \frac{\partial L}{\partial \sigma^2} \left( -\frac{2}{N} \sum_{i=1}^{N} (x_i – \mu) \right)
    $$
    因为 $\sum(x_i – \mu) = 0$,所以这条路径的梯度为0。
  3. 总梯度
    $$
    \frac{\partial L}{\partial \mu} = \sum_{i=1}^{N} \frac{\partial L}{\partial \hat{x}_i} \left( \frac{-1}{\sigma} \right)
    $$

第五步:计算对输入 $x_i$ 的梯度

这是最终的、最复杂的汇集点。xᵢ 通过三条路径影响最终损失:直接影响 x̂ᵢ,间接影响 μ,间接影响 σ²

$$
\frac{\partial L}{\partial x_i} = \frac{\partial L}{\partial \hat{x}_i} \frac{\partial \hat{x}_i}{\partial x_i} + \frac{\partial L}{\partial \mu} \frac{\partial \mu}{\partial x_i} + \frac{\partial L}{\partial \sigma^2} \frac{\partial \sigma^2}{\partial x_i}
$$

  1. 来自 $\hat{x}_i$ 的路径
    $$
    \frac{\partial L}{\partial \hat{x}_i} \cdot \frac{1}{\sigma}
    $$
  2. 来自 $\mu$ 的路径
    $$
    \frac{\partial L}{\partial \mu} \cdot \frac{1}{N}
    $$
  3. 来自 $\sigma^2$ 的路径
    $$
    \frac{\partial L}{\partial \sigma^2} \cdot \frac{2(x_i – \mu)}{N}
    $$

将三者相加,即可得到最终对输入 xᵢ 的梯度。

公式推导法

用公式推导的方法计算批归一化(BN)的梯度,其核心思想是将BN层视为一个整体的、复杂的数学函数,然后运用微积分(特别是多元链式法则)直接求解最终的梯度表达式,而不是分步进行。

我们的目标是,在已知上游梯度 $\frac{\partial L}{\partial y_i}$ 的情况下,直接推导出关于我们关心的变量的梯度公式,即 $\frac{\partial L}{\partial x_i}$、$\frac{\partial L}{\partial \gamma}$ 和 $\frac{\partial L}{\partial \beta}$。

整个推导的数学基础是多元链式法则。因为每一个输入 $x_i$ 都会通过影响整个批次的均值 $\mu$ 和方差 $\sigma^2$ 来影响到所有的输出 $y_j$,所以为了计算损失 $L$ 对某一个输入 $x_i$ 的总梯度,我们必须将 $x_i$ 对所有输出 $y_j$ 产生的影响全部累加起来。其公式为:

$$
\frac{\partial L}{\partial x_i} = \sum_{j=1}^{N} \frac{\partial L}{\partial y_j} \frac{\partial y_j}{\partial x_i}
$$

推导过程最困难的部分就是求解并化简 $\frac{\partial y_j}{\partial x_i}$。这个过程需要考虑当 $i=j$ 和 $i \neq j$ 时的不同情况。经过一系列复杂的化简后,可以得到一组简洁的封闭表达式:

对输入 $x_i$ 的梯度:
$$
\frac{\partial L}{\partial x_i} = \frac{1}{N \sigma} \gamma \left( N \frac{\partial L}{\partial y_i} – \sum_{j=1}^{N} \frac{\partial L}{\partial y_j} – \hat{x}_i \sum_{j=1}^{N} \frac{\partial L}{\partial y_j} \hat{x}_j \right)
$$

对可学习参数 $\gamma$ 和 $\beta$ 的梯度:
$$
\frac{\partial L}{\partial \gamma} = \sum_{i=1}^{N} \frac{\partial L}{\partial y_i} \hat{x}_i $$ $$ \frac{\partial L}{\partial \beta} = \sum_{i=1}^{N} \frac{\partial L}{\partial y_i}
$$

这组公式的优势在于其计算效率。它将计算图法中许多零碎的、需要存储中间变量的步骤,融合成为了几次高效的向量或矩阵运算,因此在内存使用和计算速度上都远胜于朴素的实现方法。所有主流的深度学习框架底层对BN层的实现,采用的都是这种经过数学优化的方法。

空间批归一化(Spatial Batch Normalization)

先回顾一下标准批归一化(BN)在全连接层(FC)上的工作方式:输入一个2D张量,形状为 (N, D),其中 N 是批次大小,D 是特征数量。它会独立地计算每一个特征维度的均值和方差。也就是说,我们会得到 D 个均值和 D 个方差。它会学习 D 个 $\gamma$ 和 D 个 $\beta$ 参数,分别对应每一个特征。但是卷积层的输出是一个 4D张量,形状为 (N, C, H, W),其中

  • N:批次大小
  • C:通道数(Channel),可以理解为特征图的数量
  • H:特征图的高度
  • W:特征图的宽度

为了解决这个问题,Spatial BN 提出:我们不应该为每个像素学习归一化参数,而应该为每一个通道(Channel)学习一套归一化参数,并在该通道内的所有空间位置上共享这套参数。这个思想与卷积核(Filter)在整个空间位置上共享权重完全一致。

计算步骤:

  1. 选定一个通道:我们从 C 个通道中选出第 k 个通道(也就是depth相同的某一层)。
  2. 汇集该通道的所有数据:我们将当前批次 N 个样本中,所有属于第 k 个通道的特征图抽取出来。现在,我们得到了 N 张大小为 H × W 的二维特征图。
  3. 计算单个通道的均值和方差:我们将这 N × H × W 个像素值视为一个整体,计算它们的总均值 $\mu_k$ 和总方差 $\sigma^2_k$。
  4. 这样,我们就为第 k 个通道得到了一个均值和一个方差。
  5. 对所有通道重复此操作:我们对所有 C 个通道独立地重复以上步骤,最终会得到一个长度为 C 的均值向量和一个长度为 C 的方差向量。
  6. 归一化:我们使用刚刚得到的 C 个均值和方差,来对整个 (N, C, H, W) 的输入张量进行归一化。对于第 k 个通道中的每一个像素,都使用相同的 $\mu_k$ 和 $\sigma^2_k$ 来进行计算。
  7. 缩放与平移:最后,我们用长度为 C 的可学习参数 $\gamma$ 和 $\beta$ 来进行缩放和平移。第 k 个通道归一化后的所有像素,都会被同一个 $\gamma_k$ 缩放,并被同一个 $\beta_k$ 平移。

全连接层(Fully-connected layer)

在典型的CNN架构中,全连接层几乎总是位于网络的末端,跟在一系列卷积层和池化层之后。它的任务是接收前半部分提取出的高级特征,并基于这些特征做出最终的判断。在进入第一个全连接层之前,最后一个卷积层或池化层输出的三维特征图(形状为 [Height, Width, Channels])会被“拉直”,变成一个一维的长向量。这个一维向量被送入全连接层,后续的计算过程与常规神经网络中相同。

CNN 架构模式

最常见的CNN架构遵循一个重复的模式:INPUT -> [CONV -> RELU]*N -> [POOL?]*M -> [FC -> RELU]*K -> FC

不断堆叠 “卷积-激活 -> (可选)池化” 模块,逐步提取特征并减小空间尺寸,最后由全连接层完成分类任务。
一个关键的设计原则是:用多个3×3卷积层替代一个大尺寸卷积层(堆叠小卷积核)。

各层尺寸设计的法则

  • 输入层 (Input Layer)
    • 尺寸: 最好是2的N次方,能被2整除多次。常见的有 32, 64, 224
  • 卷积层 (CONV Layer)
    • 核心任务: 只改变深度(通道数),不改变空间尺寸(高和宽)。
    • 标准配置:
      • 小卷积核: 3x3 (最多5x5)。
      • 步长 (Stride): S=1
      • 零填充 (Padding): 使用 P=(F-1)/2 来确保输入输出尺寸不变 (例如,F=3时, P=1)。
  • 池化层 (POOL Layer)
    • 核心任务: 只负责将空间尺寸减半(空间降采样)。
    • 标准配置:
      • 类型: 2x2 的最大池化 (Max-pooling)。
      • 步长 (Stride): S=2
    • 这会将特征图的宽高各缩小一半,总激活值减少75%。
  • 关于妥协
    • 上述规则(尤其是在CONV层保持尺寸不变)可能会在网络初期导致巨大的内存消耗。
    • 因此,在实践中,一种常见的妥协是仅在第一个卷积层使用较大的卷积核(如7x7)和大于1的步长(如S=2),以在网络早期快速减小特征图尺寸。

CNN 架构案例

只是简单的介绍一下各种CNN的发展历程,若感兴趣可以自行了解。

1990s
LeNet
LeNet是卷积神经网络的“开山鼻祖”,由Yann LeCun在1990年代开发。它的结构非常经典,通过交替堆叠卷积层和池化层来提取特征,最后由全连接层完成分类。虽然以今天的标准来看它非常浅,但LeNet首次成功地将CNN应用于实际任务(如手写数字识别),完整地奠定了现代卷积网络的基本架构。
2012
AlexNet
AlexNet是引爆深度学习革命的里程碑式模型,它在2012年以碾压性优势赢得了ImageNet图像识别挑战赛。相比LeNet,它的网络更深、更大,并引入了几个关键创新:它成功地堆叠了多个卷积层,并首次大规模应用了ReLU激活函数来解决梯度消失问题,同时使用Dropout来抑制过拟合,这些技术都成为了后续网络的标准配置。
2013
ZFNet
作为2013年的ImageNet冠军,ZFNet可以被看作是AlexNet的一个精细调优版本。它的贡献不在于提出全新的结构,而在于通过可视化等方法证明了对网络超参数的精细调整能带来显著的性能提升。其关键改动包括使用更小的卷积核和步长在网络的第一个卷积层,并增加了中间层的通道数,展示了更优的特征提取策略。
2014
GoogLeNet
GoogLeNet是2014年的ImageNet冠军,它的核心贡献在于设计了创新的“Inception模块”来构建一个计算效率极高的网络。该模块在一个层内并行地使用多种不同尺寸的卷积核,使网络能以很低的参数量(仅400万)捕捉多尺度的特征。此外,它在网络末端用全局平均池化(Global Average Pooling)取代了庞大的全连接层,这一设计极大地减少了参数并成为现代网络设计的常用技巧。
2014
VGGNet
VGGNet是2014年的亚军,它用一种极致简洁和“暴力”的方式证明了网络深度是提升性能的关键。其结构非常规整,从头到尾只使用3x3的小卷积核和2x2的池化层,通过不断重复堆叠这些基础模块将网络深度推向了16-19层。VGGNet的设计哲学——“越深越好”和模块化堆叠的思想影响深远,但其缺点是参数量巨大,计算成本高昂。
2015
ResNet
由Kaiming He 等人开发的ResNet是2015年的冠军,也是CNN发展史上一个革命性的里程碑,它使得训练超过100层甚至1000层的超深网络成为可能。其核心创新是“残差连接”(或称“跳跃连接”),它允许输入信号可以“抄近道”跨越多层直接与后面层的输出相加。这个优雅的设计极大地缓解了深度网络的梯度消失和“退化”问题,让网络可以专注于学习残差,从而释放了“深度”所能带来的全部潜力。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇