Class 2 - Neural Networks

1. NN Basics

1.1. The Perceptron

1.1.1. 单输出感知机

感知机是神经网络的基本构建模块,一个感知机的工作流程可以分三步来看:

  • 输入与权重 (Inputs & Weights):感知机接收多个输入信号 $x_1,x_2,\ldots,x_N$​ 。每一个输入信号都伴随着一个权重 (weight) $w_1,w_2,\ldots,w_N$​ 。这个权重代表了对应输入信号的重要性。权重越大,说明该输入对最终结果的影响也越大。此外,还有一个特殊的输入“1”,它的权重是 $b$,我们称之为偏置 (bias) 。
  • 加权求和 (Summation):感知机的第一步工作是把所有的输入信号乘以它们各自的权重,然后全部加起来,得到一个总的聚合信号 $s$ 。其计算公式为: $$s=b+(w_1x_1+w_2x_2+\cdots+w_Nx_N)$$
  • 激活函数 (Activation Function):这个聚合信号 $s$ 并不会直接作为输出。它还需要经过一个关键的步骤——通过一个激活函数 $g$ 。这个函数会根据 $s$ 的值来决定这个“神经元”最终的输出是什么。 完整的输出公式为: $$g(s)=g(b+(w_1x_1+w_2x_2+\cdots+w_Nx_N))$$ ![[Pasted image 20250913204440.png]]

需要注意的一个联系是,感知机是一个通用的模型结构,而逻辑回归是感知机的一个实例。

当我们为感知机选择Sigmoid函数作为其激活函数 $g$ 时,这个感知机就变成了一个逻辑回归分类器。如果选择的是简单的阶跃函数(即如果 $s>0$ 则输出1,否则输出0),那就是最原始的感知机模型。在现代神经网络中,这个 $g$ 还可以是 ReLUTanh 等其他函数。

接着,下文中我们将统一以下几条:

  • 符号变更:之前在逻辑回归中使用的参数符号 $θ$ 现在统一称为权重 $w$ 。这在神经网络的文献中是更常见的用法。
  • 偏置的处理:偏置项 $b$ 在数学上可以等价地看作一个权重 $w_0$​,它对应一个恒为1的输入 $x_0​$。即 $θ_0​=w_0​=b$ 。这是一种简化表达和计算的常用技巧。
  • 简化图:当我们需要绘制由成百上千个感知机组成的复杂网络时,为了图形的简洁,通常会使用左下角的简化图 ,它将权重、求和、激活等内部细节都隐藏在一个节点 $o$ 内部 。

1.1.2. 多输出感知机

![[Pasted image 20250913204856.png]]

输出层中的每一个蓝色节点,其本身就是完整的感知机。

  • 它接收所有来自输入层的输入信号。
  • 它拥有一套自己独立的权重 ($w$) 和偏置 ($b$)。
  • 它独立地进行加权求和,并通过自己的激活函数计算出最终的输出值。

如果一个输出层有 $K$ 个节点,那就意味着这里有 $K$ 个感知机在并行工作,它们共享相同的输入,但各自拥有不同的参数,最终产生 $K$ 个独立的输出。

这个结构非常有用,是解决很多现实问题的基础。主要关系到多输出问题,可以直接联系到我们之前讨论的MNIST手写数字识别任务。要识别0到9这10个数字,我们就可以设计一个拥有10个输出节点的全连接层。

  • 第1个输出节点(感知机)负责判断输入图像“有多像0”。
  • 第2个输出节点负责判断“有多像1”。
  • ...
  • 第10个输出节点负责判断“有多像9”。 这10个节点会分别输出10个分数(scores),然后我们就可以将这10个分数一起送入Softmax函数,来计算出输入图像属于每个数字的最终概率。

1.2. The Neural Network (NN)

1.2.1. 结构

神经网络的核心结构就是将“层”的概念堆叠起来,形成一个多层感知机 (MLP)。一个标准的神经网络划分为三个部分 :

  • 输入层 (Input layer, Layer 0) :这是网络的入口。它本身不进行计算,唯一的任务是接收最原始的数据。例如,在MNIST任务中,输入层就负责接收一张图像展平后的784个像素值。
  • 隐藏层 (Hidden layer, Layer 1 to L-1) :这是网络的核心部分,位于输入层和输出层之间。之所以称为“隐藏”层,是因为它们不直接与外部数据打交道。网络可以有一个或多个隐藏层,每一层都由若干个神经元(感知机)组成,负责对前一层传来的信息进行计算和转换。
  • 输出层 (Output layer, Layer L) :这是网络的出口。它接收来自最后一个隐藏层的信息,并计算出最终的预测结果。例如,在10分类的MNIST任务中,输出层会输出10个类别的概率。

当一个神经网络只包含一个隐藏层时,我们称之为“浅层神经网络”,而当它包含多个隐藏层时,我们就称之为深度神经网络 (Deep Neural Network, DNN)。我们今天所说的“深度学习”,指的就是设计和训练这种多层(即“深度”)结构模型的技术。

堆叠多个隐藏层的意义远不止增加网络的复杂性,其核心价值在于能够学习到层次化的特征表示。可以用一个直观的图像识别例子来理解这个概念:

  • 靠近输入层的第一个隐藏层 (Layer 1):可能会学习到一些非常基础、简单的特征,比如从原始像素中识别出边缘、角落、颜色块等。
  • 中间的隐藏层 (e.g., Layer 2):它会把前一层学到的简单特征进行组合,形成更复杂、更具意义的特征,比如由边缘和角落组合成眼睛、鼻子、耳朵等部件。
  • 靠近输出层的隐藏层 (e.g., Layer L-1):它会进一步组合,将眼睛、鼻子等部件组合成更抽象的概念,比如人脸、猫脸等。

这种从简单到复杂、从具体到抽象的特征学习能力,是深度学习如此强大的根本原因。网络越深,理论上就能学习到越复杂的特征模式。

另外,在这种MLP结构中,数据严格地从输入层单向流向输出层,中间不会回跳或形成环路。这种网络结构被称为前馈神经网络 (Feedforward Neural Network)

1.2.2. 前向传播过程

我们先引入一套标准符号:

  • $x^{(l)}$:表示第 $l$ 层的输出向量。$x^{(0)}$ 就是整个网络的初始输入数据。
  • $s^{(l)}$:表示在第 $l$ 层,神经元经过加权求和后、但在激活函数作用之前的中间值向量。
  • $W(l)$:表示连接第 $l−1$ 层和第 $l$ 层的权重矩阵
  • $b^{(l)}$:表示第 l 层的偏置向量
  • $g$:代表激活函数,例如 Sigmoid 或 ReLU。
  • $h$:代表整个网络的最终输出

那么,如图:

![[Pasted image 20250913205918.png]]

对于从输入层开始的任意一层 $l$(从1到L),计算过程都遵循一个固定的两步模式:

  1. 线性变换:将上一层 $l−1$ 的输出 $x^{(l−1)}$ 与当前层 $l$ 的权重矩阵 $W^{(l)}$ 进行矩阵相乘,然后加上偏置向量 $b^{(l)}$。 $$s^{(l)}=x^{(l-1)}W^{(l)}+b^{(l)}$$
  2. 非线性激活:将上一步得到的线性结果 $s^{(l)}$,通过激活函数 $g$ 进行非线性映射,得到当前层 $l$ 的最终输出 $x^{(l)}$。 $$x^{(l)}=g(s^{(l)})$$

这个过程从 Layer 1 开始,将计算结果 $x^{(1)}$ 传递给 Layer 2 ,再将 $x^{(2)}$ 传递给下一层,如此循环往复,直到最后的 Layer L 产出整个网络的最终结果 $h=x^{(L)}$。

这些公式都是以矩阵和向量的形式书写的,而不是单个数字的加和。这在深度学习中被称为向量化,也是深度学习能够高效计算的基石。

前向传播是一个高度结构化的程序,输入一个 $x^{(0)}$,程序就按固定流程输出一个 $h$。而训练 的过程,就相当于一个“元程序”,它的目标是自动寻找最优的一组参数($W$ 和 $b$),使得我们这个“前向传播程序”能够对任何输入都给出正确的输出。

1.3. Neural Networks vs Linear Models

我们知道,监督学习的思想是,现实世界中存在一个我们未知的、隐含的规律称之为未知函数 $f: X → Y$ 。这个函数能够根据问题($X$)给出答案($Y$)。

我们无法得知 $f$ 的真实样貌,但我们可以观测到它产生的一系列数据,即训练数据集。而我们的目标是,利用一个学习算法 ,从这些数据中学习出一个假设函数 $G$,让它尽可能地去逼近真实的 $f$ (即 $G≈f$) 。

到目前为止,这套流程对于线性模型和神经网络是完全通用的。唯一的区别在于我们为假设函数 $G$ 选择什么样的模型。之前我们讨论的是让 $G$ 成为一个线性模型,而现在我们可以让 $G$ 成为一个神经网络。

因为线性模型的能力是有天花板的,它只能学习直线、平面这样的线性关系。如果数据背后的真实规律是弯曲的、非线性的,线性模型就无能为力,但关于神经网络,有一个通用逼近定理 (Universal Approximation Theorem)

这个定理告诉我们,一个包含至少一个隐藏层并且使用了非线性激活函数的神经网络,只要其隐藏层中有足够多的神经元,它就可以以任意精度去逼近任何一个连续函数。

但是神经网络也有缺点,相比于线性模型,神经网络通常:

  • 需要更多的数据:模型越复杂,就越容易在数据量不足时发生“过拟合”。
  • 计算成本更高:训练神经网络需要消耗巨大的计算资源和时间。
  • 可解释性更差:神经网络的决策过程通常像一个“黑箱”,很难像线性模型那样直观地解释每个特征的权重和意义。

现在,我们可以进一步地想,首先,许多分类任务的本质都可以归结为逻辑判断,比如“是/否”、“真/假”、“批准/不批准”等 。分类与逻辑是有紧密联系的。

那么,显然的,只有当神经网络有能力执行这些基本的逻辑功能时,它才能被训练来解决这些复杂的分类问题。因为既然现实世界中的许多决策(分类)可以被分解为ANDORNOT等基本逻辑运算的组合,那么一个模型如果想要模仿甚至超越人类的决策能力,它至少应该具备这些基础的逻辑推理能力。

如果我们能证明NN有能力完成基础的逻辑问题,那么我们就不需要手动去设计一个实现 (x₁ AND x₂) OR x₃ 规则的程序。在理想情况下,我们只需要向神经网络提供大量的历史数据,神经网络就应该能够通过梯度下降算法,自动地学习出隐藏在数据背后的、近似于这个逻辑规则的决策模型。

1.4. NN and LOGIC functions

1.4.1. Computing OR

我们有两样工具:

  1. 计算单元:一个感知机。它接收两个二进制输入 x₁ 和 x₂(0或1),并计算一个加权和: $$s=w_0+w_1x_1+w_2x_2$$ 其中 w₀ 是偏置项的权重(偏置项的输入恒为1)。
  2. 决策函数:一个阶跃函数 (Step Function) 作为激活函数。这是最简单的决策函数,它的规则是: $$f(x)=\begin{cases}0&\mathrm{if~}x<0\1&\mathrm{if~}x\geq0&&\end{cases}$$ 也就是说,如果输入的加权和 s 大于等于0,它就“激活”并输出1;否则就输出0。

这里需要注意的是,阶跃函数可以被Sigmoid近似,因为在实际训练中,阶跃函数在0点不可导,导致梯度下降算法无法使用。而一个参数调整得当的Sigmoid函数(权重很大时)的形状会非常接近阶跃函数,并且它处处可导,因此可以在实际的神经网络训练中使用。

下面参照 OR 运算的真值表,逐条分析来推导这些值:

  • 情况一: x₁ = 0x₂ = 0
    • 根据真值表,0 OR 0 的结果应该是 0
    • 此时,感知机计算的加权和是 $s=w_0+w_1(0)+w_2(0)=w_0$​。
    • 为了让阶跃函数输出 0,它的输入 $s$ (也就是 $w_0$​) 必须小于0。
    • 结论 1: $w_0​<0$。我们可以选择 $w_0​=−50$。
  • 情况二: x₁ = 1x₂ = 0
    • 根据真值表,1 OR 0 的结果应该是 1
    • 此时,加权和是 $s=w_0+w_1(1)+w_2(0)=w_0+w_1$。
    • 为了让阶跃函数输出 1,它的输入 s 必须大于等于0,即 $w_0​+w_1​≥0$。
    • 因为我们已经选了 $w_0​=−50$,所以不等式变为 $−50+w_1​≥0$,即 $w_1​≥50$。
    • 结论 2: $w_1​$ 必须是一个不小于50的数。我们选择 $w_1​=100$。
  • 情况三: x₁ = 0x₂ = 1
    • 根据真值表,0 OR 1 的结果也应该是 1
    • 此时,加权和是 $s=w_0+w_2(1)=w_0+w_2$。
    • 同理,我们需要 $w_0​+w_2​≥0$。
    • 代入 $w_0​=−50$,得到 $w_2​≥50$。
    • 结论 3: $w_2$​ 也必须是一个不小于50的数。我们也选择 w$_2​=100$。

感知机的工作方式也可以从几何角度来理解,这会提供一个非常直观的视角。

  • 感知机的决策“临界点”发生在加权和 s 等于 0 的时候,即: $w_0+w_1x_1+w_2x_2=0$
  • 将我们求得的权重代入: $-50+100x_1+100x_2=0$
  • 整理后可以得到一个直线方程: $x_1+x_2=0.5$

这条直线就是在输入空间(一个以 $x_1​$ 为横轴,$x_2$​ 为纵轴的二维平面)中的决策边界 (Decision Boundary)

  • 所有落在这条直线上方或正好在线上的点(满足 $x_1​+x_2​≥0.5$),都会被分类为 1
  • 所有落在这条直线下方的点(满足 $x_1​+x_2​<0.5$),都会被分类为 0

1.4.2. Computing AND

使用的工具和上一节完全相同:

  • 计算单元:一个计算加权和 $s=w_0+w_1x_1+w_2x_2$​ 的感知机。
  • 决策函数:一个阶跃函数 (Step Function),当输入 $s≥0$ 时输出1,否则输出0。 $$f(x)=\begin{cases}0&\mathrm{if~}x<0\1&\mathrm{if~}x\geq0&&\end{cases}$$ 为简化问题,我们假设两个输入的权重是相同的,即 w$_1​=w_2​=w$。
  • 情况一: x₁ = 0x₂ = 0
    • 真值表结果应为 0
    • 加权和 $s=w_0$​。
    • 要让输出为0,必须满足 $s<0$,即 $w_0​<0$。
    • 我们采纳PPT的建议,选择一个足够负的值,比如 $w_0​=−150$。
  • 情况二: x₁ = 0x₂ = 1 或 x₁ = 1x₂ = 0
    • 真值表结果都应为 0
    • 加权和 $s=w_0​+w$。
    • 要让输出为0,必须满足 $s<0$,即 $w_0​+w<0$。
    • 代入 $w_0​=−150$,我们得到 $−150+w<0$,这意味着 $w<150$。
  • 情况三: x₁ = 1x₂ = 1
    • 真值表结果应为 1
    • 加权和 $s=w_0+w_1(1)+w_2(1)=w_0+2w$。
    • 要让输出为1,必须满足 $s≥0$,即 $w_0​+2w≥0$。
    • 代入 $w_0​=−150$,我们得到 $−150+2w≥0$,这意味着 $2w≥150$,即 $w≥75$。

AND运算的感知机也可以从几何角度来理解。它的决策边界同样是 $w_0+w_1x_1+w_2x_2=0$ 这条直线。

  • 代入我们求得的权重: $-150+100x_1+100x_2=0$
  • 整理后得到直线方程: $x_1​+x_2​=1.5$

这条直线就是AND感知机的决策边界

  • 所有落在这条直线上方或正好在线上的点(满足 $x_1​+x_2​≥1.5$),都会被分类为 1
  • 所有落在这条直线下方的点(满足 $x_1​+x_2​<1.5$),都会被分类为 0

1.4.3. Computing NOT

在成功构建了AND和OR之后,我们还需要一个最基本的逻辑单元逻辑非 (NOT)。NOT门的功能非常简单,就是将输入反转(0变成1,1变成0)。

一个实现NOT功能的感知机配置如下,这里它只有一个输入$x_1$。

  • 权重配置为:偏置权重$w_0​=100$,输入权重 $w_1​=−200$ 。
  • 权重向量为$W_{NOT}​=(100,−200)$ 。​

我们已经证明了单个感知机可以分别实现 ANDOR 和 NOT。这三者的组合引出了一个计算机科学中的概念,即逻辑完备性

一个逻辑运算符的集合如果被称为“逻辑完备”的,意味着宇宙中任何可能的布尔逻辑函数(无论它多么复杂,对应多么复杂的真值表)都可以仅通过这个集合中的运算符组合而成。

1.4.4. The XOR Problem

在成功构建了AND和OR门之后,我们很自然地会尝试用同样的方法来构建XOR门再次尝试“反向工程”这些参数,假设 $w_1​=w_2​=w$:

  1. 情况一 (0, 0) -> 0:$s=w_0​$。要输出0,则 $w_0​<0$。我们采纳建议,令 $w_0​=−50$ 。
  2. 情况二 (0, 1) -> 1 和 (1, 0) -> 1:$s=w_0​+w$。要输出1,则 $w_0​+w≥0$ 。代入$w_0​=−50$,得到 $w≥50$。我们采纳建议,令 $w=100$ 。

到目前为止,一切顺利。但现在,我们面临一个无法解决的矛盾:

  1. 情况三 (1, 1) -> 0:$s=w_0​+w_1​+w_2​=w_0​+2w$。要输出0,则 $w_0​+2w<0$ 。但是,将我们已经求得的 $w_0​=−50$ 和 $w=100$ 代入这个不等式,得到: $−50+2(100)=150$。 我们需要$150<0$,这显然是一个矛盾

所以,不存在任何一组权重 w 和偏置 w₀,能够同时满足XOR真值表的所有四个条件。因此,单个感知机无法实现XOR功能。这里的本质原因是:单个感知机的功能就是在空间中画一条直线(决策边界)来进行分类。如果一个问题本身就无法用一条直线来解决,那么单个感知机就注定会失败。AND和OR都是线性可分的,而XOR是典型的线性不可分问题。

1.4.5. Multilayer Perceptron

上一节我们得出了一个结论:单个感知机无法解决XOR问题,因为它是一个线性分类器,而XOR是线性不可分的。

那么一个自然的想法就是:把多个感知机堆叠成一个多层网络(MLP),是不是就能解决XOR了呢?

  • 一个“看似正确”的错误论证

感知机的核心计算是一个加权和,例如 $w_0+w_1x_1+w_2x_2+\cdots+w_nx_n+b$ ,而多个线性函数的组合(或嵌套)其结果依然是一个线性函数。因此,多层神经网络在解决(非线性的)XOR问题上,并不比单个感知机更强大。

这个论证的链条看起来很完美,但是,激活函数是非线性的。一个完整的感知机包含两个步骤:

  1. 线性的加权求和。
  2. 非线性的激活函数(如Step, Sigmoid, ReLU等)。

一个多层神经网络,是多个非线性函数的反复嵌套和组合。而多个非线性函数的组合,可以创造出极其复杂的、新的非线性函数。多层感知机之所以能解决XOR问题,关键就发生在隐藏层

  • 我们可以把XOR的四个输入点想象成在一个二维平面上,它们是线性不可分的。
  • 当这些点通过第一个隐藏层时,隐藏层的非线性激活函数会对这个二维空间进行扭曲和变换,将其映射到一个新的、更高维度的空间中。
  • 在这个新的空间里,原来线性不可分的四个点,变得可以被线性分开
  • 最后,输出层的工作就变得非常简单了:它只需要在这个新的、已经被变换过的空间里,画一条直线(或一个平面),就能完美地将XOR的10分开了。

而上一节的逻辑完备性,也根本上回答了神经网络能否解决XOR问题。

1.4.6. Computing XOR

XOR的逻辑表达式如下: $$(\bar{x}_1\mathrm{~AND~}x_2)\mathrm{~OR~}(x_1\mathrm{~AND~}\bar{x}_2)$$ 所以,想要得到XOR的结果,需要三步:

  1. 计算一些NOT操作。
  2. 计算两个AND操作。
  3. 对上面两个AND的结果再进行一次OR操作。

一个实现如图。我们可以把它看作一个有一个隐藏层一个输出层的两层网络。

  • 输入层:最左边的 x₁ 和 x₂ (以及隐含的偏置节点 1)。
  • 隐藏层(中间层):这一层是解决问题的关键,它包含两个神经元(图中红色和蓝色的椭圆)。
    • 第一个隐藏神经元 (红色):它的任务是识别 (x₁, x₂) 是否为 (0, 1) 这种特定模式。它被设计用来计算逻辑函数 $A=(\bar{x}_1\mathrm{~AND~}x_2)$。只有当 x₁=0 且 x₂=1 时,这个神经元才会输出1,否则输出0。
    • 第二个隐藏神经元 (蓝色):它的任务是识别 (x₁, x₂) 是否为 (1, 0) 这种特定模式。它被设计用来计算逻辑函数 $B=(x_1\mathrm{~AND~}\bar{x}_2)$。只有当 x₁=1 且 x₂=0 时,这个神经元才会输出1,否则输出0。
  • 输出层(最右侧):这一层只有一个神经元 f
    • 输出神经元:它的任务非常简单,就是对来自隐藏层的两个信号A和B进行OR运算,即计算 $f=A~OR~B$。

![[Pasted image 20250914145920.png]]

这个例子展示了隐藏层的核心作用:进行特征空间的变换

  • 原始问题:在原始的 (x₁, x₂) 空间中,XOR问题是线性不可分的。
  • 隐藏层:隐藏层将原始的、线性不可分的输入 (x₁, x₂),加工并变换成了一组新的、更高层次的特征 (A, B)
  • 新空间中的问题:在隐藏层创造的这个新的 (A, B) 特征空间里,问题变得线性可分
    • 输入 (0,0) 变成了 (A,B)=(0,0),期望输出0。
    • 输入 (1,1) 变成了 (A,B)=(0,0),期望输出0。
    • 输入 (0,1) 变成了 (A,B)=(1,0),期望输出1。
    • 输入 (1,0) 变成了 (A,B)=(0,1),期望输出1。 在这个新的特征空间里,我们只需要画一条直线就可以把 (0,0) 和 (1,0)(0,1) 分开。而输出层的那个OR门,正是在执行这个简单的线性分割任务。

1.4.7. Linearly-Separable Classification

我们首先定义线性可分问题:

如果一个数据集中的两类点,可以被一条直线(在二维空间中)、一个平面(在三维中)或一个超平面 (hyperplane)(在更高维中)完美地分离开,那么这个问题就是线性可分的 。

一个感知机的权重和偏置,在几何上就定义了它在输入空间中的决策边界超平面。那么对于线性可分的分类问题,一个最简单的神经网络单个感知机就足以解决。

而对于非线性可分的分类问题,可以通过拥有更多层的神经网络,利用其“AND”、“OR”、“NOT”功能来解决。这意味着,即使每个神经元本身只能画一条直线,但通过将这些直线(决策)进行逻辑组合,整个网络就能勾勒出任意复杂的、弯曲的决策区域。

![[Pasted image 20250914150950.png]]

在左上角的“走廊”状数据中,红色数据点分布在一个由两条斜线 L1 和 L2 构成的“走廊”里。用一条直线无法将红色和蓝色数据点分开,但我们可以训练有隐藏层的NN:

  • 隐藏层: 包含两个神经元 L1 和 L2。
    • 神经元 L1 被训练来充当直线L1的分类器。例如,它可以在数据点低于L1时输出1,高于L1时输出0。
    • 神经元 L2 被训练来充当直线L2的分类器。例如,它可以在数据点高于L2时输出1,低于L2时输出0。
  • 输出层: 包含一个神经元。它的任务就是学习一个 AND 逻辑。只有当它同时从神经元L1和L2接收到信号“1”时,它才会最终输出1(判定为红色)。

而在右下角的甜甜圈”状数据中,红色的方块被蓝色的圆点完全包围。要将红点分离出来,我们需要一个封闭的决策边界。我们只需要一个三角形。

  • 这个三角形的边界也是由三条直线构成的。一个拥有三个隐藏神经元的隐藏层就可以实现这个功能:
    • 隐藏神经元1:学习三角形顶边所在的直线。
    • 隐藏神经元2:学习三角形左下方所在的直线。
    • 隐藏神经元3:学习三角形右下方所在的直线。
  • 输出层的逻辑: 输出层的神经元学习一个组合逻辑:一个点是红色的,当且仅当它同时满足低于顶边 AND 高于左下边 AND 高于右下边

所以层神经网络的强大之处,是一种分而治之、组合创造的策略:

  1. 第一层(隐藏层):充当简单特征检测器。每个神经元学习一个简单的线性边界(画一条线)。
  2. 后续层(输出层):充当逻辑组合器。它将第一层检测到的简单特征(例如,“低于这条线”、“高于那条线”)进行ANDOR等逻辑组合,从而形成复杂的决策区域。

1.5. NN as a Function Approximator

回归是另一大类机器学习问题,这里我们以尽可能地拟合这条曲线为例:

![[Pasted image 20250914153142.png]]

为了后续方便,我们假设所有输入 x 的值都落在 [0, 1] 这个区间内。这并不会让我们的结论变得狭隘,因为任何一个定义在有限区间上的连续函数,都可以通过简单的数学变换(缩放和平移)将其输入域调整到 [0, 1] 范围内,而函数本身的复杂性和逼近难度等核心问题并不会改变。因此,如果能证明神经网络可以逼近[0,1]区间内的任意函数,就等于证明了它可以逼近任何有限区间内的任意函数。

首先,我们的手上有Sigmoid激活函数 $\sigma(x)=\frac{1}{1+e^{-x}}$ 。它具有“S”形曲线,能将任何实数输入平滑地映射到(0, 1)的区间内。

而一个具体的感知机(神经元)结构,可以将标准的Sigmoid函数改造成我们需要的特定形状。Sigmoid神经元 $y=σ(wx+b)$ 的通用控制法则如下:

  1. 权重 $w$ 的绝对值:控制着S形曲线(或阶跃)的陡峭程度。$|w|$ 越大,曲线越陡。
  2. 偏置 $b$ 和权重 $w$ 的比值:控制着曲线的中心位置。中心位置在 $x=−b/w$。
  3. 同时,通过调整从隐藏层到输出层的连接权重,我们可以将阶跃的高度设置为任意我们想要的值。(比率乘法)

但我们把 $b$ 和 $w$ 同比率增大很多的时候,可以把S形函数变成阶跃函数,而一个正和一个负的阶跃函数组合,可以形成一段方波

  • 我们可以通过调整两个隐藏神经元的参数,来随心所欲地控制这个“方波”的位置宽度高度
  • 既然我们可以创造一个“方波”,我们就可以通过增加更多的隐藏神经元对,来创造任意多个这样的“方波”。
  • 任何复杂的连续函数曲线,我们都可以想象成是由无数个非常窄的、不同高度的“小方波”(就像柱状图的每一个柱子)拼接而成的。因此,只要我们的神经网络有足够多的隐藏神经元,我们就可以创造出足够多的“小方波”,并将它们相加,从而以任意精度逼近任何我们想要的目标函数。

这就叫做通用逼近定理

具有足够复杂性(或容量)的神经网络,可以以任意期望的精度,逼近任何可测函数。

这个定理要求:

  1. 隐藏层需要有足够多的神经元。我们看到,每一个“凸起”或“尖峰”都需要一对隐藏神经元来构建。因此,目标函数越复杂、越“扭曲”,我们就需要越多的“尖峰”去拟合它,也就意味着隐藏层需要越“宽”(包含越多的神经元)。
  2. 可测函数是一个数学术语,它涵盖的函数范围非常广,远远超出了我们通常接触的连续函数。对于工程应用来说,可以简单地理解为几乎所有你能想到的、有意义的函数,神经网络都有能力去逼近它
  3. 只要愿意增加隐藏层神经元的数量,就可以让神经网络的输出与真实函数的输出之间的误差变得无限小。

同时,需要说明的是- 要达到这种“万能”的逼近能力,一个足够宽的隐藏层就足够了。理论上,我们并不需要一个非常的网络(即不需要很多个隐藏层)。一个“又宽又浅”的网络在理论上就具备了通用逼近的能力。

但许多复杂问题,深度网络(多隐藏层)通常比浅层网络更有效率,深度网络也更能够学习到特征的层次结构,在学习效率和泛化能力上表现得更为出色。

1.6. Backpropagation (BP)

首先,训练一个神经网络的过程,指的是调整神经网络(NN)中成千上万个权重的过程,其目的是为了让网络的预测误差最小化

为了实现预测误差最小化,我们经常用梯度下降法来实现。为了实施梯度下降策略,我们需要一个工具来计算梯度。对于一个拥有数百万甚至数十亿权重的大型神经网络,这个计算量是惊人的。所以我们引入方向传播算法。

反向传播 (BP):BP并非学习策略本身,而是一个极其高效的算法,其唯一目的就是快速地计算出损失函数 $J(W)$ 相对于网络中每一个权重 $w_{ij}^{(l)}$ 的梯度(即偏导数): $$\frac{\partial J(W)}{\partial w_{ij}^{(l)}}$$ 我们先回顾一下信息是如何“向前”流动的:第 $l$ 层的每个神经元 $j$,首先计算其输入的加权和 $s_j^{(l)}$ $$s_j^{(l)}=\left(\sum_iw_{ij}^{(l)}x_i^{(l-1)}\right)+b_j^{(l)}$$ 然后,将加权和通过激活函数 $g$,得到该神经元的输出 $x_j^{(l)}$​: $$x_j^{(l)}=g\left(s_j^{(l)}\right)$$ 这个过程从输入层(Layer 0)开始,一路向右,直到输出层(Layer L)。

为了实现反向传播,我们需要引入一个新的变量,用 $δ$ 表示。

$\delta_j^{(l)}$代表的是第 $l$ 层第 $j$ 个神经元的误差信号 (error signal)。它的严格数学定义是最终的总损失函数 $J$ 对该神经元激活前的加权和 $s_j^{(l)}$ 的偏导数: $$\delta_j^{(l)}\equiv\frac{\partial J}{\partial s_j^{(l)}}$$ 可以把 $\delta_j^{(l)}$ 理解为 blame。它衡量了在最终的巨大误差中,第 $l$ 层第 $j$ 个神经元(在激活前的那个瞬间)应该“背负”多少责任。这个值越大,说明它对最终的误差贡献越大,也就越需要调整。

反向传播的核心机制,就是根据这个blame,逐层向后传递误差,具体是根据第 $l$ 层的 $\delta_j^{(l)}$​,计算出第 $l−1$ 层的 $\delta_i^{(l-1)}$

BP算法是一个递归的过程,其信息流动方向与前向传播正好相反,为从右向左:

  • 起点 (Layer L):算法从网络的输出层开始。在这里,我们可以将网络的预测值 $(h_1​,h_2​,…)$ 与真实标签 $(y_1​,y_2​)$ 直接比较,从而根据损失函数计算出输出层每个神经元的初始误差信号 $δ(L)$。
  • 递推:一旦我们知道了第 $l$ 层的全部误差信号 $δ(l)$,我们就可以利用它们和连接这两层的权重矩阵 $W(l)$,通过一个固定的数学公式,计算出前一层(第 $l−1$ 层)的全部误差信号 $δ(l−1)$。
  • 终点:这个过程不断地向左重复,一层一层地反向计算,直到计算出所有隐藏层的误差信号。

逐层反向计算所有神经元的误差信号 $δ$ 的目的,为了得到损失函数对权重 $w$ 的梯度 $\frac{\partial J}{\partial w}$ ,$δ$ 是一个至关重要的中间变量。一旦我们求出了 $\delta_j^{(l)}$ ,计算相应权重的梯度就变得异常简单: $$\frac{\partial J}{\partial w_{ij}^{(l)}}=x_i^{(l-1)}\delta_j^{(l)}$$ 即:梯度 = 前一层的输出 × 当前层的误差信号,我们将在下一节证明。

那么,对于误差的计算,我们分为输出层的 $δ$ 和隐藏层的 $δ$。

  1. 输出层(Layer L)的误差 $δ$

P算法从网络的末端开始,所以我们首先要计算输出层每个神经元的误差信号。其公式为: $$\delta_i^{(L)}=g^{\prime}(s_i^{(L)})\left(\frac{\partial J}{\partial h(x)_i}\right)$$ 这个这个公式分解为两个部分的乘积:

第一部分 $(\frac{\partial J}{\partial h(x)_i})$ :损失函数对输出的梯度

  • 含义:这个偏导数衡量的是“如果我稍微改变一下第 $i$ 个输出神经元的最终预测值,总损失会改变多少?”。
  • 角色:这是最原始的、源头的误差信号,直接来自于损失函数的定义。

第二部分 $g'(s_i^{(L)})$ :激活函数的导数

  • 含义:这个导数衡量的是“如果我稍微改变一下第 $i$ 个输出神经元的加权和 $s$,它的输出值 $x$ 会改变多少?”。
  • 角色:它代表了误差信号在流经这个神经元时,被这个神经元自身的激活状态所做的“调节”或“缩放”。

输出层神经元的误差 = (总误差的变化率) × (神经元自身的激活变化率)。

  1. 计算任意隐藏层(Layer l)的误差 $δ$

一旦我们有了输出层 $L$ 的误差信号 $δ^(L)$,我们就可以用一个递归的公式来计算任意前置隐藏层 $l$ 的误差信号。其公式为: $$\delta_i^{(l)}=g^{\prime}(s_i^{(l)})\left(\sum_jw_{ij}^{(l+1)}\delta_j^{(l+1)}\right)$$ 这个公式同样可以分解成两部分来理解:

第一部分 $( \sum_j w_{ij}^{(l+1)} \delta_j^{(l+1)} )$ :来自后一层的误差的加权和

  • 含义:这是最关键的一步,要计算第 $l$ 层神经元 $i$ 的误差 $\delta_i^{(l)}$,我们需要看向它的下一层(第 $l+1$ 层)。我们将下一层所有神经元 $j$ 的误差信号 $\delta_j^{(l+1)}$​,乘以连接它们与当前神经元 $i$ 的权重 $w_{ij}^{(l+1)}$​,然后将这些结果全部加起来。
  • 角色:“责任分摊”。这相当于把后一层的所有“责任”(误差信号),按照连接的权重(贡献度)分配回当前层的这个神经元上。

第二部分 $g'(s_i^{(l)})$:激活函数的导数

  • 含义和角色:与输出层的作用完全相同,它代表当前神经元 $i$ 对反向传回来的误差信号所做的“本地调节”。

隐藏层神经元的误差 = (来自后一层所有神经元的“责任”总和) × (神经元自身的激活变化率)。

1.7. Framework for Training the NN

训练神经网络的框架可以分为七个步骤,是一个包含初始化和核心循环的算法:

  1. 权重初始化 (Initialization)

随机初始化所有权重 $w_{ij}^{(l)}$ ,这是一个非常关键的细节。如果将所有权重都初始化为0,那么在一个隐藏层中的所有神经元都会进行完全相同的计算,得到完全相同的梯度,并进行完全相同的更新。它们会永远保持一致,从而无法学习到不同的特征。这个现象被称为对称性问题 (Symmetry Problem)。随机初始化正是为了“打破对称”,让每个神经元在训练开始时就有不同的起点。

第二步至第六步是一个会重复执行成千上万次的核心循环

  1. 输入样本:将一个或一批训练样本“喂”给网络的输入层(Layer 0)。
  2. 前向传播 (Forward Pass):从输入层开始,逐层向前计算,直到得到网络在输出层的最终预测结果 $x_j^{(l)}​$。
  3. 检查误差:将预测结果与真实标签进行比较,计算损失。如果误差已经足够小,可以接受,则跳到第8步,结束训练。
  4. 反向传播 (Backward Pass):如果误差仍然很大,则启动BP算法。从输出层开始,逐层向后计算出网络中每一个神经元的误差信号 $\delta_j^{(l)}$​。
  5. 更新权重 (Update Weights):这是“学习”真正发生的一步。我们使用梯度下降法来更新网络中的每一个权重。
  6. 当训练循环结束后,返回最终学习到的权重 $w_{ij}^{(l)}$。这一整套权重就是我们训练好的、可以用来做预测的模型。

第六步的权重更新是整个框架的核心。其公式为: $$w_{ij}^{(l)}\to w_{ij}^{(l)}-\alpha x_i^{(l-1)}\delta_j^{(l)}$$ $w_{ij}^{(l)}$:我们当前要更新的权重。 $α$:学习率 (Learning Rate)。需要我们手动设置的超参数,它控制着每次更新权重时“步子”的大小。如果 $\alpha$ 太大,可能会导致“步子”迈得太大,越过最低点;如果 $\alpha$ 太小,则会导致学习速度过慢。 $x_i^{(l-1)}$:在前向传播中,发出这个连接信号的前一层神经元 $i$ 的输出值。 $\delta_j^{(l)}$:在反向传播中,接收这个连接信号的当前层神经元 $j$  的误差信号

实际上,根据之前的公式,这里乘的就是权重,接下来进行梯度公式的证明。

证明目标: $$\frac{\partial J(w)}{\partial w_{ij}^{(l)}}=\delta_j^{(l)}\times x_i^{(l-1)}$$ 我们首先使用一次链式法则 (Chain Rule),将这个梯度分解为两部分: $$\frac{\partial J(w)}{\partial w_{ij}^{(l)}}=\frac{\partial J(w)}{\partial s_j^{(l)}}\times\frac{\partial s_j^{(l)}}{\partial w_{ij}^{(l)}}$$ 其中,第二项 $\frac{\partial s_j^{(l)}}{\partial w_{ij}^{(l)}}$​​ 非常容易计算。根据前向传播的公式 $s_j^{(l)}=(\sum_kw_{kj}^{(l)}x_k^{(l-1)})+b_j^{(l)}$​,我们知道 $s_j^{(l)}$ ​对其中一个特定权重 $w_{ij}^{(l)}$​ 的偏导数,就等于与该权重相乘的那个输入,即 $x_i^{(l-1)}$​。

所以,原式变成了: $$\frac{\partial J(w)}{\partial w_{ij}^{(l)}}=\frac{\partial J(w)}{\partial s_j^{(l)}}\times x_i^{(l-1)}$$ 我们将这个结果与我们的大目标公式对比,会发现我们只需要证明剩下的部分是相等的就可以了,所以我们的证明目标变成: $$\delta_j^{(l)}=\frac{\partial J(w)}{\partial s_j^{(l)}}$$ 这正是我们对误差信号 $δ$ 的数学定义。接下来的证明就是要验证这个定义是符合链式法则的。

  1. 我们想知道损失 $J$ 是如何被第 $l$ 层的加权和 $s_i^{(l)}$ 影响的。根据链式法则,这个影响需要通过下一层($l+1$层)来传递。完整的链条是: $$\frac{\partial J(w)}{\partial s_i^{(l)}}=\sum_j\frac{\partial J(w)}{\partial s_j^{(l+1)}}\times\frac{\partial s_j^{(l+1)}}{\partial x_i^{(l)}}\times\frac{\partial x_i^{(l)}}{\partial s_i^{(l)}}$$
  2. 分别求解:
    • $\frac{\partial J(w)}{\partial s_j^{(l+1)}}$:根据我们的“新目标”,这一项正是下一层的误差信号 $\delta_j^{(l+1)}$。
    • $\frac{\partial s_j^{(l+1)}}{\partial x_i^{(l)}}$:根据前向传播公式,$l+1$ 层的加权和对 $l$ 层的某个输入 $x_i^{(l)}$ 的偏导数,正好就是连接它们的权重 $w_{ij}^{(l+1)}$​。
    • $\frac{\partial x_i^{(l)}}{\partial s_i^{(l)}}$:根据前向传播公式 $x_i^{(l)}=g(s_i^{(l)})$,这一项就是激活函数 $g$ 的导数 $g^{\prime}(s_i^{(l)})$。
  3. 将上面三项的结果代回到长链条中:$$\frac{\partial J(w)}{\partial s_i^{(l)}}=\sum_j\delta_j^{(l+1)}\times w_{ij}^{(l+1)}\times g^{\prime}(s_i^{(l)})$$ 将 $g′(s_i(l)​)$ 提到求和符号的外面(因为它与 $j$ 无关),我们就得到了: $$\frac{\partial J(w)}{\partial s_i^{(l)}}=g^{\prime}(s_i^{(l)})\left(\sum_jw_{ij}^{(l+1)}\delta_j^{(l+1)}\right)$$ 这个等式的右边,正是计算隐藏层 $\delta_i^{(l)}$​ 的递推公式,这证明了 $\frac{\partial J(w)}{\partial s_i^{(l)}}=\delta_i^{(l)}$ 成立。所以我们初始的目标也证明完毕。

1.8. Example of Simple NN Classifier

1.8.1. 正向传播过程

![[Pasted image 20250914170154.png]]

我们用一个用一个具体、量化的例子来解释一下NN:

我们可以根据每层神经元的数量来描述这个网络。这是一个 2-5-1 的结构:

  • 输入层 (Layer 0):有2个神经元,接收一个二维的输入向量 $x$ 。
  • 隐藏层 (Layer 1):有5个神经元。
  • 输出层 (Layer 2):只有1个神经元。

因为输出层只有一个神经元,并且其激活函数是 Sigmoid ($σ$),这意味着它的输出是一个介于0和1之间的单一概率值。因此,这个网络是为一个二元分类 (binary classification) 任务设计的。

进行一次完整前向传播所需要的所有计算公式如下:

  • 计算隐藏层的加权和:$s_1​=xW_1​+b_1​$
  • 计算隐藏层的激活值:$x_1​=σ(s_1​)$
  • 计算输出层的加权和:$s_2​=x_1​W_2​+b_2​$
  • 计算最终输出/预测:$h=x_2​=σ(s_2​)$

假设我们一次只处理一个输入样本,这里还牵扯到矩阵维度的匹配。具体如下:

从输入层到隐藏层:

  • 输入 x 的维度是 1 x 2 (1个样本, 2个特征)。
  • 为了将2个输入节点连接到5个隐藏节点,权重矩阵 W₁ 的维度必须是 2 x 5
  • 计算 xW₁(1 x 2) @ (2 x 5),内部维度 2 匹配,结果维度是 1 x 5
  • 隐藏层的偏置 b₁ 作用于5个神经元,所以其维度是 1 x 5。因此,加权和 s₁ 和激活值 x₁ 的维度都是 1 x 5

从隐藏层到输出层:

  • 隐藏层输出 x₁ 的维度是 1 x 5
  • 为了将5个隐藏节点连接到1个输出节点,权重矩阵 W₂ 的维度必须是 5 x 1
  • 计算 x₁W₂(1 x 5) @ (5 x 1),内部维度 5 匹配,结果维度是 1 x 1
  • 输出层的偏置 b₂ 只作用于1个神经元,所以维度是 1 x 1。最终,加权和 s₂ 和输出 x₂ 的维度都是 1 x 1(一个标量值)。

在实际的神经网络中,我们会从处理单个样本到处理一批 (a batch) 样本,即引入批量大小M

这个M的引入,会像涟漪一样传递到网络中的所有计算环节:

  • 输入层 x 的维度是 M x 2
  • 隐藏层的输出 x₁ 的维度变成了 M x 5
  • 最终输出 h=x₂ 的维度变成了 M x 1

权重矩阵 W₁ (2x5) 和 W₂ (5x1) 的维度则保持不变,因为它们是模型的参数,与一次处理多少数据无关。

在理论上,我们可以一次只处理一个样本来训练网络,但在实践中,几乎所有的深度学习训练都是以“批”为单位进行的。这主要有两个原因:

  • 计算效率 (Computational Efficiency)
    • 现代的GPU(图形处理器)被设计用来执行大规模的并行计算,尤其是矩阵乘法。
    • 一次性处理一个 M x 2 的大矩阵,其计算效率远高于执行 M 次 1 x 2 的小矩阵计算。这极大地加速了模型的训练过程。
  • 梯度稳定性 (Gradient Stability)
    • 我们的目标是通过计算损失的梯度来更新权重。如果每次只根据单个样本来计算梯度,这个梯度可能会因为样本的特殊性或噪声而产生剧烈波动,使得训练过程非常不稳定。
    • 通过计算一个批次(M个样本)的平均梯度,我们可以得到一个更稳定、更可靠的对真实梯度方向的估计,这有助于模型更平滑、更快速地收敛。

广播机制 (Broadcasting)

在实际的编程框架(如TensorFlow, PyTorch)中,我们通常只需要定义 b₁ 为一个 1 x 5 的向量,b₂ 为一个 1 x 1的标量。在执行加法运算时(例如 s₁ = xW₁ + b₁),框架会自动使用一种叫做广播 (Broadcasting) 的机制。

  • 广播会自动将 b₁(维度 1 x 5)“复制”M行,使其维度在逻辑上变为 M x 5,从而能够与 xW₁ 的结果(维度 M x 5)进行逐元素的相加。这是一种非常高效的机制,避免了在内存中创建不必要的巨大偏置矩阵。

1.8.2. 反向传播过程1

据我们的通用公式,输出层的误差信号 $δ$ 的计算方法是: $$\delta_i^{(L)} = g'(s_i^{(L)}) \left( \frac{\partial J}{\partial h(x)} \right)$$对于我们这个具体的例子,因为输出层是第2层,激活函数是Sigmoid($σ$),并且只有一个输出神经元,所以公式具体化为:$$\delta_2 = \sigma'(s_2) \left( \frac{\partial J}{\partial h} \right)$$

我们的任务就是分别求出这个乘法的两项:损失函数的导数 $\frac{\partial J}{\partial h}$​ 和 Sigmoid函数的导数 $σ′(s_2​)$。

  1. 求损失函数的导数 $\frac{\partial J}{\partial h}$​

对于二元分类任务,我们使用的损失函数是二元交叉熵:$$J = -y \log h - (1 - y)\log(1-h)$$利用对数函数的求导法则 $(log x)' = 1/x$ ,我们对 $h$ 求偏导:$$\frac{\partial J}{\partial h} = \frac{-y}{h} - \frac{(1-y)}{-(1-h)} \times (-1) = \frac{-y}{h} + \frac{1-y}{1-h}$$将两项通分合并,得到:$$\frac{\partial J}{\partial h} = \frac{-y(1-h) + h(1-y)}{h(1-h)} = \frac{-y+yh+h-hy}{h(1-h)} = \frac{h-y}{h(1-h)}$$ 2. 求Sigmoid函数的导数 $σ'(s_2)$

Sigmoid函数的公式是 $\sigma(s)=\frac{1}{1+e^{-s}}$。求导会得到一个恒等式: $$\sigma'(s) = \sigma(s)(1-\sigma(s))$$因为在我们的网络中,最终的输出 $h$ 就是 $x_2$,而 $x_2 = \sigma(s_2)$,所以我们可以将这个导数用 $h$ 来表示:$$\sigma'(s_2) = h(1-h)$$ 3. 将前两部分求得的结果代回到我们最初的 $δ_2$ 公式中: $$\delta_2=\sigma^{\prime}(s_2)\times\frac{\partial J}{\partial h}$$ $$\delta_2=\left(h(1-h)\right)\times\frac{h-y}{h(1-h)}$$ 分子和分母中的 $h(1-h)$ 项可以相互抵消。 最终,我们得到了一个极其简洁和直观的结果: $$\delta_2=h-y$$ 即误差信号 = 预测值 - 真实值,是深度学习中最漂亮的结果之一。

  • 它的含义非常直观:输出神经元应该承担的“责任”,就等于它的预测值与真实值之间的差距。
    • 如果网络预测 h=0.9 而真实值 y=1,那么 δ₂ = -0.1,是一个负值,意味着网络需要增大激活前的加权和 s₂ 来让 h 更接近1。
    • 如果网络预测 h=0.9 而真实值 y=0,那么 δ₂ = 0.9,是一个大的正值,意味着网络需要大幅减小 s₂ 来让 h更接近0。
  • 这个优雅的结果,是二元交叉熵损失函数Sigmoid激活函数这对共同作用产生的。它们在数学上就互相匹配,共同确保了梯度计算的简洁和高效。这也是为什么这个组合在二元分类任务中如此经典的原因。

1.8.3. 反向传播过程2

下面利用 $δ_2$ 来进一步反向计算出隐藏层(Layer 1)的误差信号 $δ_1$

在进入 $δ_1$ 的计算之前,我们那有一个核心结论,即对于最常见的分类任务,如果我们选择“正确”的损失函数与激活函数配对,那么在输出层计算初始误差信号 $δ$ 的公式会简化为同一个形式 $$\delta^{(L)}=h(x)-y\quad(\text{即,预测值 - 真实值})$$

  • 二元分类Sigmoid 激活函数 + 二元交叉熵 损失函数。
  • 多元分类Softmax 激活函数 + 交叉熵 损失函数。

我们的目标是使用在前几页学到的通用递推公式来计算 $δ_1$: $$\delta_i^{(l)} = g'(s_i^{(l)}) \left( \sum_j w_{ij}^{(l+1)} \delta_j^{(l+1)} \right)$$将这个公式具体化到我们的例子中$(l=1, g=σ)$,我们需要计算:$$\delta_1 = \sigma'(s_1) \times (\text{来自 } \delta_2 \text{ 的误差加权和})$$ 这里的难点在于如何高效地计算括号里的“误差加权和”。如果用循环来计算,效率会很低。因此,我们需要把它转换成矩阵运算

  • 我们先回顾一下相关变量的维度:
    • 我们要求解的 δ₁ 的维度是 M x 5
    • 我们已知的 δ₂ 的维度是 M x 1
    • 连接这两层的权重 W₂ 的维度是 5 x 1
  • 我们的目标是组合 δ₂ 和 W₂,得到一个 M x 5 的矩阵。简单的相乘 δ₂ @ W₂ ((M x 1) @ (5 x 1)) 维度上是不匹配的。
  • 关键技巧:转置 (Transpose)。如果我们对权重矩阵 W₂ 进行转置,其维度就从 5 x 1 变成了 1 x 5
  • 现在我们来计算矩阵乘法 δ₂ @ W₂ᵀ:其维度是 (M x 1) @ (1 x 5)。根据矩阵乘法法则,内部维度1匹配,最终结果的维度是 M x 5
  • 恰好是我们需要的 δ₁ 的维度,因此,复杂的求和运算 $\sum_jw_{ij}^{(l+1)}\delta_j^{(l+1)}$​ 在数学上完全等价于简洁高效的矩阵运算 $\delta_2W_2^T$。

将上面向量化的结果代回到原始公式中,我们就得到了最终公式: $$\delta_1=\sigma^{\prime}(s_1)\circ\delta_2W_2^T$$

  • $\delta_2W_2^T$:这是我们刚刚推导出的、从输出层反向传播回来的误差,是一个 M x 5 的矩阵。
  • $σ'(s_1)$:这是隐藏层激活函数的导数,根据上一页的结论,它可以写作 $(1-\sigma(s_1))\sigma(s_1)$。由于 $s_1$ 的维度是 M x 5,这一项的维度也是 M x 5
  • $∘$:这个符号代表 Hadamard 乘积,即逐元素乘法 (element-wise multiplication)。它表示将 $σ'(s_1)$ 和  $\delta_2W_2^T$ 这两个维度相同的矩阵中对应位置的元素逐个相乘。

1.8.4. 完整迭代过程

  1. 前向传播 (Forward Pass):进行预测

这是网络根据当前权重进行预测的过程。

  • $s_1​=xW_1​+b_1​$
  • $x_1​=σ(s_1​)$
  • $s_2​=x_1​W_2​+b_2​$
  • $h=x_2​=σ(s_2​)$

给定输入 $x$ ,通过这一系列计算,我们得到最终的预测值 $h$。

  1. 反向传播 (Backward Pass):计算误差

这是在得到预测值并计算出损失后,反向追溯每个神经元“责任”的过程。

  • $\delta_2=h-y$
  • $\delta_1=(1-\sigma(s_1))\sigma(s_1)\circ\delta_2W_2^T$

通过这一系列计算,我们得到了网络中每一层(输出层和隐藏层)的误差信号 $δ_2$ 和 $δ_1$ 。

  1. 权重更新 (Update Weights):进行学习

这是学习真正发生的地方。我们使用前向传播的激活值和反向传播的误差信号,来更新网络中的所有参数。

  • 基本法则:我们之前学过,任何一个权重 $w_{ij}^{(l)}$ 的梯度,都可以通过 $(x_i^{(l-1)}\delta_j^{(l)})$ 来计算。我们的更新就是这个公式的向量版。

更新 $W_2$: $W_2=W_2-\alpha x_1^T\delta_2$

  • 这里的梯度项是 $x_1^T\delta_2$。我们来验证维度:x₁M x 5,转置后x₁ᵀ5 x Mδ₂M x 1。矩阵乘法 (5 x M) @ (M x 1) 的结果是 5 x 1,这正好是 $W_2$ 的维度

更新 $W_1$: $W_1=W_1-\alpha x^T\delta_1$

  • 这里的梯度项是 $x^T\delta_1$。我们来验证维度:xM x 2,转置后xᵀ2 x Mδ₁M x 5。矩阵乘法 (2 x M) @ (M x 5) 的结果是 2 x 5,这也正好是 $W_1$ 的维度

更新偏置 $b_2$ 和 $b_1$: $b_2=b_2-\alpha\delta_2$ 和 $b_1=b_1-\alpha\delta_1$

  • 偏置项的梯度就是其所在层的误差信号 $δ$。在处理一批数据时,因为偏置向量一般是向量形式,所以这里的 $δ$ 通常需要沿着批次维度($M$)进行求和或求平均,以得到对偏置向量的整体更新值。

2. NN Training

2.1. Gradient Descent, SGD and Mini-batch SGD

2.1.1. Gradient Descent (GD)

想象一下,你站在一座连绵起伏的山上(这座山就是我们的损失函数 J(W)),你的目标是走到山的最低谷(也就是让损失函数最小)。由于你闭着眼睛,看不到全局的地形,你唯一能做的就是感受脚下哪一个方向是坡度最陡的下坡方向,然后朝着这个方向迈出一步。不断重复这个过程,你就能一步步地接近山谷的底部。

我们的优化目标: $$J(W)=\frac{1}{M}\sum_{i=1}^Me(h(x_i),y_i)$$

  • $J(W)$: 这是损失函数(Cost Function 或 Loss Function),也就是我们说的那座“山”的高度。J 的值越小,说明我们的模型预测得越准,反之则越差。W 代表模型中所有的权重参数(weights),调整 W 就能改变模型,从而改变 J 的值。
  • $M$: 训练集中样本的总数量 。
  • $h(x_i​)$: 对于第 i 个输入样本 $x_i​$,我们模型的预测输出。
  • $y_i​$: 第 i 个样本的真实标签(正确答案)。
  • $e(h(x_i​),y_i​)$: 这是单个样本的误差(error),它用来衡量模型一次预测的好坏(比如可以是预测值和真实值的差的平方),这里常用的是我们之前说的交叉熵。
  • $\frac{1}{M}\sum_{i=1}^M$: 这个符号表示,我们要计算出训练集中所有 M 个样本的误差,然后把它们加起来求一个平均值 。

我们具体“走一步”的方法: $$\Delta W=-\alpha\nabla J(W)$$

  • $∇J(W)$: 梯度 (Gradient)。在我们的比喻里,它就是你脚下坡度最陡的上坡方向。它是一个向量,指向函数值增长最快的方向。
  • $−∇J(W)$: 既然梯度指向最陡的上坡方向,那么负梯度自然就指向了最陡的下坡方向 。这就是我们每次要走的方向。
  • $α$: 学习率 (Learning Rate),也就是我们下山时每一步的步长。它控制着我们每一步走多远。如果步长太小,下山会非常慢;如果步长太大,我们可能会一步迈过最低点,甚至跑到对面的山坡上,导致无法收敛。
  • $ΔW$: 这代表权重的更新量 。这个公式告诉我们,权重的调整方向是负梯度方向,调整的幅度由学习率 $α$ 控制。权重的更新可以写成:$W_{new}=W_{old}+\Delta W=W_{old}-\alpha\nabla J(W)$

这里需要注意的是,按照公式,$∇J$ 是基于所有 M 个样本计算的。这意味着,为了算出那个“最陡的下坡方向”,我们必须把整个训练集的所有数据都过一遍,计算出平均梯度。然后再根据这个平均梯度,更新一次权重。这就像是你看遍了整座山的地图才决定走一小步,方向非常准,但也很耗时。

优点:

  • 收敛路径稳定:由于每次都使用全部数据,梯度的方向被计算得非常准确,因此参数的更新路径非常平滑,会稳定地朝着极小值点前进。
  • 理论保证:对于凸函数,BGD 保证能收敛到全局最小值;对于非凸函数(如神经网络的损失函数),它也能收敛到一个局部最小值。

缺点:

  • 计算成本极高:这是它最致命的缺点。在深度学习中,训练集通常有数百万甚至上亿个样本。每更新一次参数,就要遍历所有数据,这会消耗巨大的计算资源和时间,尤其是在模型很大时。
  • 内存需求大:需要将整个数据集加载到内存中以计算梯度。
  • 不适合在线学习:如果数据是流式地、动态增加的,BGD 无法很好地处理,因为它需要一个固定的、完整的训练集。

2.1.2. Stochastic Gradient Descent (SGD)

SGD和BGD的基本思路是一致的,依然是要最小化整个训练集的平均损失函数 $J(W)$。更新权重的基本思路也一样:沿着负梯度方向调整,即$ΔW=−α∇J(W)$。

真正的区别在于第四点,这也是SGD的核心思想:

  • 随机梯度下降 (Stochastic GD): 随机选择一个样本 $(x_i​,y_i​)$ 来进行计算 。

BGD是计算所有M个样本的平均梯度,而SGD则简单粗暴得多:在每次更新权重时,它只随机地从数据集中拿一个样本,用这一个样本的误差来计算梯度,并立刻更新权重。这个过程重复进行,每次都随机换一个新的样本。这样一来,原来BGD需要计算M个样本才能更新一次,现在SGD更新M次所花费的时间约等于BGD更新一次的时间。速度得到了极大的提升。

这样做是可行的,可以看这个公式: $$E[-\nabla e(h(x_i),y_i)]=\frac{1}{M}\sum_{i=1}^M-\nabla e(h(x_i),y_i)=-\nabla J$$ 单个随机样本计算出的负梯度 $-\nabla e(h(x_i),y_i)$ 确实是一个有噪声的、不准确的估计 。但是,如果我们把所有可能的随机选择都考虑进来,计算这个“噪声梯度”的期望值,我们得到的期望值恰好就等于我们真正想要的、基于全部数据的“真实梯度” $−∇J$ 。

SGD也可以选择少数几个样本 。这其实是为我们后面要讲的“小批量梯度下降 (Mini-batch SGD)”埋下了伏笔。在实践中,我们发现每次只用1个样本有点太“随机”了,所以一个折中的办法是每次用一小批样本(比如32个或64个),这也就是现在最主流的做法。

优点:

  • 速度快,效率高: 这是SGD最大的优点。每次更新的计算成本是固定的,和数据集大小无关。这使得我们能够处理海量数据。
  • 有助于跳出局部最优: 由于每次更新都带有随机性(噪声),这种“不稳定”的更新路径反而可能帮助模型跳出一些比较差的局部最小值(local minima),从而有机会找到一个更好的最优解。这在非凸的深层网络中尤其有用。
  • 适合在线学习: SGD“来一个样本就更新一次”的特性,天然适合数据流持续到来的在线学习场景。

缺点:

  • 更新方向不稳定,收敛过程震荡: “噪声”是一把双刃剑。它虽然有助于跳出局部最优,但也导致了收敛过程非常“颠簸”,参数更新的路径呈Z字形,不稳定。
  • 最终收敛困难: 正是因为震荡,SGD在接近最优点时很难完全收敛,而是在最优点附近徘徊。通常需要配合动态调整学习率(后期减小学习率)的策略来解决。
  • 不利于硬件并行计算: 每次只处理一个样本,无法充分利用现代GPU等硬件强大的并行计算能力。硬件处理一小批数据(如64个)和处理1个数据的时间差不了太多。

2.1.3. Mini-batch SGD

数学定义: $$J(W) = \frac{1}{B}\sum_{i=1}^{B} e(h(x_i), y_i)$$ 权重的更新方式依然是: $$\Delta W = -\alpha \nabla J(W)$$ 这里的关键在于,梯度 $∇J$ 是基于一个大小为 B 的小批量 (mini-batch) 样本计算的 。

  • $B$: 批量大小 (Batch Size),这是一个需要我们手动设置的超参数(比如 32, 64, 256)。
  • 我们不再是计算全部 M 个样本(BGD)或单个样本(SGD)的梯度,而是在每次更新权重时,从数据集中随机抽取 B 个样本组成一个“小批量”,然后计算这个小批量的平均梯度来更新权重 。
  • 整个训练过程就是不断地重复:取下一个小批量 -> 计算梯度 -> 更新权重,直到遍历完所有数据。我们把“遍历一遍完整数据集”的过程称为一个世代 (Epoch)

几大优势 :

  • 更准确的梯度估计 (More accurate estimation of gradient)
    • 相比于SGD只用一个样本,Mini-batch用B个样本的平均梯度,极大地降低了随机性带来的噪声,因此得到的梯度方向更接近真实的梯度方向。
  • 更平滑的收敛 (Smoother convergence)
    • 由于梯度估计更准确、更稳定,损失函数的下降过程比SGD要平滑得多,减少了剧烈的震荡,使得收敛过程更加稳定。
  • 训练速度快 (Mini-batches lead to fast training!)
    • 它远比BGD快,因为不需要等待整个数据集计算完毕才能更新一次。同时,由于下面要讲的并行计算优势,其处理B个样本的速度远快于SGD处理B个样本的总和。
  • 可以并行化,充分利用GPU (Can parallelize & achieve speed increases on GPUs)
    • 这是最关键的实际优势之一。 现代计算硬件(尤其是GPU)是为并行计算(即矩阵和向量运算)设计的。处理一个大小为64的矩阵,和处理一个大小为1的向量,GPU花费的时间可能差别不大。Mini-batch SGD的数据形式正好是矩阵,可以充分发挥硬件的并行计算能力,极大地加速了训练过程。而SGD一次一个样本,则完全浪费了这种能力。

在今天的深度学习实践中,当我们提到“SGD”时,通常默认指的就是“Mini-batch SGD”,它已经成为训练神经网络的标配。各种先进的优化器,如Adam, RMSProp等,也都是在Mini-batch的框架下运作的。

B 是一个重要的超参数,它的选择会影响训练的动态和最终结果:

  • 大小: 通常选择 2的幂次方,如 32, 64, 128, 256, 512。这是因为这样的尺寸能更好地配合GPU的内存结构,带来更高的计算效率。
  • 影响:
    • 较小的Batch Size (如32, 64): 引入的噪声更多,类似于SGD,有助于模型跳出局部最优,通常有更好的泛化能力。但训练过程会更慢一些(因为更新次数更多),且梯度震荡会更剧烈。
    • 较大的Batch Size (如256, 512): 梯度更稳定,收敛方向更明确,每个Epoch的训练时间更短。但可能会陷入“尖锐”的局部最小值,泛化能力可能稍差,并且需要更多的内存

2.2. Vanishing Gradient Problem

我们回顾一下,模型权重的更新依赖于反向传播计算出的梯度,而梯度的核心是误差项 $δ_i^{(l)}​$。如果这个误差项 $δ$ 变得非常小,那么梯度也会非常小,导致权重几乎不更新,模型也就学不到东西了。

我们可以把反向传播想象成一个“信息传递”或“责任分配”的过程。输出层首先根据预测错误产生一个初始的“误差信息”($δ^{(L)}$),然后这个信息需要逐层向网络深处(靠近输入层的方向)传递,告诉每一层的权重“你们需要为这个最终的错误负多少责任,应该如何修正自己”。梯度消失问题,就是这个“误差信息”在传递过程中,信号越来越弱,最后几乎衰减为零的现象。

输出层的误差项的计算: $$\delta_i^{(L)}=g^{\prime}(s_i^{(L)})(\frac{\partial J}{\partial h(x)})$$ 最外层的误差信号强度,取决于两个因素:

  1. 损失函数对输出的导数 $(\frac{\partial J}{\partial h(x)})$ : 代表了最终的预测错得有多离谱。
  2. 激活函数的导数 $g^{\prime}(s_i^{(L)})$ : 代表了激活函数在当前点的敏感度。

传递层误差项的计算,从后一层($l+1$)传递到前一层($l$): $$\delta_i^{(l)}=g^{\prime}(s_i^{(l)})(\sum_jw_{ij}^{(l+1)}\delta_j^{(l+1)})$$ 这个公式是递推的。它告诉我们,第 $l$ 层的误差项 $δ^{(l)}$,是由第 $l+1$ 层的误差项 $δ(l+1)$ 加权求和后,再乘以当前层激活函数的导数 $g^{\prime}(s_i^{(l)})$ 得到的。

这里的 $g^{\prime}(s_i^{(l)})$ 就像一个信号衰减器。每次信息向前传递一层,都要经过一次这个衰减器。那么我们展开这个递推公式: $$\begin{aligned}&\delta_i^{(l)}=g^{\prime}(s_i^{(l)})(\sum w_{ij}^{(l+1)}\delta_j^{(l+1)})\&=g^{\prime}(s_i^{(l)})(\sum w_{ij}^{(l+1)}g^{\prime}(s_j^{(l+1)})(\sum w_{jk}^{(l+2)}\delta_k^{(l+2)}))\&=g^{\prime}(s_i^{(l)})(\sum w_{ij}^{(l+1)}g^{\prime}(s_j^{(l+1)})(\sum w_{jk}^{(l+2)}\delta_k^{(l+2)}))\&=g^{\prime}(~)...g^{\prime}(~)...g^{\prime}(~).........\end{aligned}$$ 我们把这个链条一直展开到最开始的输入层,就会发现,一个深层网络的较早层(比如第1层)的误差项 $δ(1)$,会约等于: $$\delta^{(1)}\approx g^{\prime}(s^{(1)})\cdot g^{\prime}(s^{(2)})\cdot\cdots\cdot g^{\prime}(s^{(L)})\cdot(\text{初始误差})$$ 一个深层节点的误差信号,是一长串激活函数导数的连乘积。这个连乘积为什么会导致梯度“消失”,因为早期神经网络最喜欢用Sigmoid激活函数。

Sigmoid 函数的导数最大值只有 0.25。这意味着,在上面的连乘公式中,每一项 $g′()$ 的值都小于等于 0.25。因此,误差信号在反向传播的路上,每经过一层,其强度就至少被削弱为原来的1/4。经过多层传播后,传到浅层网络的信号已经微弱到可以忽略不计。

梯度消失的后果

  • 浅层网络权重更新极其缓慢:靠近输入层的神经元,它们的梯度几乎为零。根据权重更新公式 $W_{new}=W_{old}-\alpha\cdot\mathrm{gradient}$,这些层的权重几乎不会被更新。
  • 模型学不到复杂的特征:神经网络的强大之处在于,浅层网络学习简单的特征(如边缘、颜色),深层网络基于这些简单特征组合成更复杂的特征(如眼睛、轮廓)。如果浅层网络不学习,整个层次化的特征学习过程就崩溃了,深度网络的优势也荡然无存。模型就退化成了一个“浅层网络”。

梯度消失问题的本质是深度网络中反向传播的链式法则,与“会饱和的”激活函数(如Sigmoid)导数过小特性相结合的必然结果。

与梯度消失相对的,是梯度爆炸 (Exploding Gradient Problem)

  • 成因: 如果在反向传播的链式法则中,连乘项的绝对值持续大于1(例如,权重 w 的值很大),那么梯度信号在回传时就会被指数级放大,最终变成一个巨大的数值(甚至溢出变成 NaN)。
  • 后果: 巨大的梯度会导致权重更新的步子迈得极大,使得模型参数瞬间飞出最优解区域,导致损失函数剧烈震荡,模型完全无法收敛。

![[Pasted image 20250916143944.png]]

2.3. Not Zero-Centered Problem

我们考虑一个问题,比如 Sigmoid 函数,它的输出范围是 (0, 1),永远是正数。而上一层神经元的输出(即激活值),同时也是下一层神经元的输入。

权重更新的核心,即损失对权重的梯度 $\frac{\partial J}{\partial w_i}$ 。这个梯度可以被计算为 $x_i​⋅δ$,其中 $x_i$​ 是输入信号,$δ$ 是从后层传来的误差信号。当一个神经元的所有输入 $x_1​,x_2​,...,x_i​$ 都为正数时,该神经元所有输入权重的梯度向量 (x_1δ, ..., x_iδ, ...)中,所有分量的符号将完全取决于 δ 的符号

  • 如果 δ 是正的,那么所有梯度分量都是正的。
  • 如果 δ 是负的,那么所有梯度分量都是负的。
  • 结论: 这意味着,对于这个神经元,它的所有权重在一次更新中,要么全部一起增加,要么全部一起减小。它们无法做到“一部分权重增加,另一部分权重减小”这样更精细的调整。

![[Pasted image 20250916144343.png]]

假设我们只有两个权重 w1 和 w2。图中的蓝色向量代表了能让损失下降最快的理想更新方向。在这个例子中,理想的方向是 w1 减小(向下),w2 增加(向右)。

然而,由于我们上面分析出的“梯度符号一致性”问题,我们的实际更新方向只能是“两个权重都增加”(指向右上第一象限)或“两个权重都减小”(指向左下第三象限)。模型根本无法直接朝着理想的蓝色方向前进。

为了近似地走向最终的目标,优化器被迫采取一种非常低效的策略:先朝着左下角走一步,让两个权重都减小;再朝着右上角走一步,让两个权重都增加... 如此反复,形成了一条“Z”字形的曲折路径。

因此,我们希望激活函数是零中心的 (zero-centered)。也就是说,我们希望激活函数的输出值能够有正有负,分布在0的周围。

  • 非零中心:
    • Sigmoid: 输出范围 $(0, 1)$,典型的非零中心。
    • ReLU: 输出范围 $[0, +∞)$,也是非零中心。虽然ReLU有这个问题,但它凭借着计算简单、有效防止梯度消失等巨大优势,成为了目前最主流的选择。这个效率问题相对而言可以接受。
  • 零中心:
    • Tanh (双曲正切): 输出范围 $(-1, 1)$,是很好的零中心函数。这也是在ReLU流行之前,Tanh在隐藏层中的性能通常优于Sigmoid的一个重要原因。
    • Leaky ReLU / ELU: 它们通过给负数输入一个小的斜率,使得输出值也可以为负,从而比标准的ReLU更接近零中心。
  • 这个问题主要影响的是收敛效率,而不是像梯度消失那样可能导致训练完全失败。
  • 现代优化算法,如 Adam 或 RMSProp,它们为每个参数维护独立的学习率和动量,可以在一定程度上缓解这种Z字形更新带来的低效问题。
  • 批标准化 (Batch Normalization) 技术通过对每一层的输入进行标准化(使其均值为0,方差为1),从根本上解决了这个问题,因为无论上一层的激活函数是什么,经过Batch Norm处理后,输入到下一层的数据都是近似零中心的。

2.4. Choice of Activation Function

2.4.1. sigmoid

![[Pasted image 20250916154556.png]]

  • 函数:$g(s)=\sigma(s)=\frac{1}{1+e^{-s}}$
  • 导数:$g^{\prime}(s)=(1-g(s))g(s)$

优点:

  • 优秀的概率解释: Sigmoid函数能将数值“挤压”到 $[0, 1]$ 区间,这个特性使得它的输出可以被很自然地看作一个概率值
  • 在分类任务中的应用: 正因为这一点,它在历史上被广泛用于分类任务,尤其是二元分类的输出层。例如,要判断一封邮件是否为垃圾邮件,可以在输出层使用Sigmoid函数,若输出0.9,则表示模型预测有90%的概率是垃圾邮件。

缺点:

致命缺陷:梯度消失 (Vanishing Gradient)

  • PPT左下角指出了问题的两个层面:
    1. 当输入 s 非常大或非常小时(即进入了S形曲线的两端平坦区域),导数 g'(s) 会变得非常小,趋近于0。这种情况被称为“神经元饱和”,一旦发生,梯度信号就几乎无法通过这个神经元回传,导致学习停滞。
    2. 即便输入 s 处在中间最敏感的区域,导数的最大值也只有 0.25。在深层网络中,反向传播需要将这些导数连乘起来。多个小于等于0.25的数相乘,会使梯度信号以指数级速度衰减,当网络层数一多,传到浅层的梯度信号就变得微乎其微,这就是梯度消失的根源。
  • 缺点1:输出非零中心 (Not Zero-Centered)
    • Sigmoid的输出值恒大于0(范围是(0, 1)),这意味着它的输出不是以0为中心的。
    • 正如我们之前讨论的,这会导致后一层网络的权重在更新时,梯度方向受限,只能“同增同减”,从而引发低效的“Z字形”更新路径,拖慢收敛速度。
  • 缺点2:计算成本高 (Computationally Expensive)
    • 函数中包含的指数运算 exp() 相对于ReLU函数中的简单比较运算,计算上要更耗时。当网络巨大、训练数据繁多时,这种累积的计算开销是不可忽视的。

2.4.2. Tanh

![[Pasted image 20250916154722.png]]

两种等价的数学形式: $$g(s)=\frac{2}{1+e^{-2s}}-1=\frac{e^s-e^{-s}}{e^s+e^{-s}}$$ 从第一个公式可以看出,Tanh本质上是Sigmoid函数经过线性变换(拉伸和平移)得到的。

Tanh函数也是一个S形曲线,但与Sigmoid不同的是,它将任意实数输入“挤压”到 $[-1, 1]$ 的区间内。

导数公式: $$g^{\prime}(s)=\frac{dg(s)}{ds}=1-g(s)^2$$ 虽然也是一个钟形曲线,当时Tanh导数的最大值为1.0(在输入s=0时取到),比Sigmoid的0.25要大得多。

优点:

零中心 (Zero centered)

  • 这是Tanh相对于Sigmoid最大的优势。它的输出范围是 [-1, 1],均值在0附近,是零中心的。
  • 补充: 这意味着它解决了Sigmoid的“非零中心问题”。在实际训练中,零中心的特性使得梯度下降的路径更加直接,减少了不必要的“Z字形”震荡,从而收敛速度通常比Sigmoid更快

缺点:

  • 缺点1:梯度消失问题依然存在 (Vanishing gradient problem still exists)
    • 虽然Tanh的导数峰值达到了1.0,比Sigmoid强,但从导数图像可以看出,当输入 s 的绝对值较大时(进入饱和区),其导数 g'(s) 同样会迅速趋近于0。
    • 补充: 在深度网络中,这种饱和特性意味着梯度消失的风险依然存在。虽然问题有所缓解,但并未被根除。
  • 缺点2:计算成本高 (Still exp())
    • Tanh的计算同样依赖于指数函数 exp()
    • 补充: 这使得它的计算成本与Sigmoid一样,都比后来的ReLU要高。

2.4.3. ReLU

![[Pasted image 20250916155000.png]]

函数公式: $$g(s)=\max(0,s)=\begin{cases}0&\mathrm{if~}s<0\s&\mathrm{if~}s\geq0&&\end{cases}$$ 导数公式: $$g^{\prime}(s)=\begin{cases}0&\mathrm{if~}s<0\1&\mathrm{if~}s>0&\end{cases}$$ 严格来说,ReLU在 s=0 点是不可导的。但在实际的软件实现中,通常会将其在该点的导数设定为0或1,这并不影响算法的运作。

优点:

  • 有效解决梯度消失问题 (No vanishing gradient problem when positive)
    • 这是ReLU最核心的优势。当神经元的输入为正时,其导数恒为1。这意味着在反向传播过程中,梯度信号可以原封不动地通过这些激活的神经元,不会像通过Sigmoid/Tanh那样发生衰减。这使得我们能够成功训练非常深的网络。
  • 极高的计算效率 (Computationally efficient)
    • ReLU的计算非常简单,只需要一个 max(0, s) 操作,不涉及任何复杂的指数运算。这使得神经网络的训练和推理速度都大大加快。
  • 更快的收敛速度 (Converges much faster)
    • 实践表明,由于其线性和非饱和的形式,使用ReLU的随机梯度下降法比使用Sigmoid或Tanh的收敛速度快得多。
  • 引入稀疏性 (Dropout effect)
    • 这是ReLU一个很有趣的副产品。由于负数输入的输出为0,这意味着在任何时候,网络中都有一部分神经元是“未激活”的。这种稀疏性使得网络的表示能力更强,并且在一定程度上可以减轻过拟合,类似于正则化方法Dropout的效果。

缺点:

  • 非零中心 (Not zero-centered)
    • ReLU的输出值总是大于等于0,因此它不是零中心的,存在我们之前讨论过的“Z字形”更新效率问题。但在实践中,它的巨大优点往往能弥补这个缺陷。
  • “Dying ReLU” (神经元死亡问题)
    • 这与第一点 “...but still exists when negative (gradient = 0)” 相关。如果一个神经元的权重在更新后,使得它对于整个训练数据集的所有输入,其加权和 s 始终为负数,那么这个神经元的输出将永远是0。
    • 结果就是,流经这个神经元的梯度将永远是0,它的权重再也无法得到任何更新。这个神经元就“死亡”了,不再对网络有任何贡献。
    • 补充: 为了解决这个问题,研究者们提出了ReLU的多种变体,如 Leaky ReLU (在负数区引入一个极小的固定斜率,如0.01)、PReLU (将负数区的斜率作为一个可学习的参数) 和 ELU 等。

2.4.4. Dying ReLU Problem

  • 根本原因: 梯度在负数区为0 (Gradient is 0 for negative s)。
    • 我们知道,当输入 s 为负时,ReLU的输出为0,其导数也为0。
  • 直接后果: 权重无法更新 (Weights will not get adjusted when gradient is 0)。
    • 因为权重的更新量与梯度成正比,梯度为0意味着权重更新量也为0。
  • “死亡”的恶性循环: 这就是Dying ReLU Problem的核心机制。
    1. 某个神经元的权重和偏置在某次更新后(例如,一个过大的学习率导致的更新),使得它对于所有输入数据,其加权和 s 始终为负。
    2. 于是,这个神经元的输出将永远是0。
    3. 在反向传播时,流经这个神经元的梯度将永远是0。
    4. 因此,这个神经元的权重再也无法得到任何更新
    • 它陷入了一个“s 恒为负 → 梯度恒为0 → 无法更新 → s 恒为负”的死循环。这个神经元就等于“死亡”了,对整个网络不再有任何贡献。
  • 问题的严重性: 在一个NN中,“高达40%的网络可能是‘死的’”。这意味着网络中可能有近一半的神经元在整个训练过程中从未被激活过,这极大地浪费了模型的计算能力和表达能力。

2.4.5. Leaky ReLU

![[Pasted image 20250916155518.png]]

函数定义: $$f(x)=\max(0.01x,x)$$ Leaky ReLU在正数区间的行为与ReLU完全一样。但在负数区间,它不再是输出一个恒定的0,而是赋予其一个非常小的正斜率(这里是0.01)。这个微小的斜率就像一个“漏洞 (leak)”,让一些信息得以“泄露”过去。

  • Leaky ReLU 的优势:
    1. 解决了神经元死亡问题: 这是最关键的改进。因为它在负数区有了一个非零的梯度 (Non-zero gradient when negative)。这个微小的梯度(0.01)虽然很小,但足以保证“死亡”的神经元也能接收到梯度信号,从而有机会更新其权重,“复活”并重新参与到学习过程中。
    2. 保留了ReLU的所有优点 (All benefits of ReLU)。它依然计算高效,并且在正数区不存在梯度消失问题。
    3. 输出更接近零中心 (Closer to zero-centered outputs)。由于输出可以是负值,其输出的均值比标准ReLU更接近0,这有助于缓解“非零中心”问题,可能带来更快的收敛。

2.4.6. Parametric ReLU

  • 公式: $f(x)=max(ax,x)$
  • 核心思想: 这是Leaky ReLU的“智能”版本。它不再使用一个固定的0.01作为负数区的斜率,而是将斜率 a 作为一个可以学习的参数。在训练过程中,网络会通过反向传播自动学习出最适合当前任务的 a 值。
  • 补充: PReLU通常比Leaky ReLU表现更好,因为它更灵活,但代价是为模型增加了一些需要学习的参数。

2.4.7. ReLU6

  • 公式: $f(x)=min(max(x,0),6)$
  • 核心思想: 这个函数是标准ReLU,但是增加了一个上限,即输出值最大不超过6。从图像上看,就是ReLU的斜坡在 y=6 的地方被“削平”了。
  • 补充 (应用场景): ReLU6最初是为移动端或嵌入式设备的部署而设计的。在这些设备上,为了加速计算,通常会使用低精度(如8位整型)的数值表示。ReLU的输出没有上限,可能会变得非常大,而大数值在低精度下表示会产生很大的误差。ReLU6通过将输出限制在 [0, 6] 这个较小的范围内,使得模型对量化 (quantization) 更加鲁棒,性能也更稳定。

2.4.8. ELU

  • 公式:$$f(x)=\begin{cases}x,&x\geq0\\alpha(e^x-1),&x<0&&\end{cases}$$
  • 核心思想: ELU也旨在解决Dying ReLU问题,但它在负数区使用了一条平滑的指数曲线来代替直线。
  • 优点:
    • 和Leaky ReLU一样,它在负数区有非零输出和梯度,可以避免神经元死亡。
    • 当参数 $α=1$ 时,函数在 x=0 处是完全可导的,这使得函数曲线更加平滑。
    • 补充: 研究表明,ELU的输出均值更接近0,因此它也具有零中心的优点,且对噪声有一定的鲁棒性。
  • 缺点:
    • 计算中包含了指数运算 exp(),因此计算成本比ReLU高得多

2.4.9. Maxout

  • 公式: $max(w_1​x+b_1​,w_2​x+b_2​)$
  • 核心思想: Maxout是一种完全不同的思路。它不是一个固定的激活函数,而是一个可学习的、分段线性的激活函数。一个Maxout单元会计算 k 组不同的线性函数,然后取其中最大的一个作为输出。
  • 优点:
    • 非常强大和灵活。它推广并包含了ReLU和Leaky ReLU
      • 例如,当k=2时,如果我们让 $w_1​=1,b_1​=0$ 并且 $w_2​=0,b_2​=0$,那么 $max(x,0)$ 就是标准的ReLU。Maxout可以通过学习参数来拟合出任意的分段线性凸函数。
  • 缺点:
    • 参数数量激增。它会使每个神经元的参数数量翻倍(当k=2时)。这大大增加了模型的体积和计算量,也更容易导致过拟合。

2.5. Learning Rate Problem

![[Pasted image 20250916160606.png]]

  1. 学习率过小 (Small learning rate)

左边的图和文字描述了步长太小的情况。

  • 图像解读: 图中的损失函数 J(w) 存在一个“小山谷”(局部最小值)和一个“大峡谷”(全局最小值)。优化器的路径(虚线)显示它迈着非常细碎的步子缓慢下降,最终陷入了那个小山谷里。
  • 后果:
    • 收敛所需迭代次数过多 (Many iterations till convergence): 你的每一步都太小了,虽然方向是对的,但前进的距离微乎其微。要走到谷底,你需要走无数步,这导致训练时间极长
    • 容易陷入局部最小值 (Trapped in local minimum): 这是更严重的问题。当你陷入一个局部最小值的“坑”里时,这里的地势相对平缓,梯度本身就很小。因为你的步长又很小,所以计算出的下一步更新量 (α * 梯度) 就会小到不足以让你“爬出”这个坑,去探索更深、更好的全局最小值。模型找到了一个“还不错”但远非“最好”的解,然后就停滞不前了。
  1. 学习率过大 (Large learning rate)

右边的图和文字描述了步子迈得太大的情况。

  • 图像解读: 图中的损失函数是一个平滑的“U形山谷”。优化器从一侧山坡开始,因为步子太大,一脚直接迈过了谷底,跨到了对面的山坡上。在对面山坡,它又朝着反方向迈出一大步,结果又跨了回来。如此反复,在山谷两侧来回“横跳”,就是到不了最低点。
  • 后果:
    • 过冲 (Overshooting): 这是最直接的现象。你本来想往谷底走,但步子太大,用力过猛,直接“冲”过了目标点。
    • 无法收敛 (No convergence): 由于反复地过冲,损失函数的值可能会在最小值附近剧烈震荡,永远无法稳定下来。在更糟糕的情况下,每一步都可能让你落到比上一步更高的地方,导致损失值不降反升,最终发散到无穷大,导致训练彻底失败。

2.6. Momentum

在解决了学习率的问题后,我们面临一个新的挑战:如何让我们的优化过程更“聪明”,而不仅仅是“机械地”沿着当前梯度方向前进。

![[Pasted image 20250916160724.png]]

标准梯度下降法有一个窘境。当优化过程不幸进入一个局部最小值(一个小坑)或者一个平坦区域(梯度几乎为零的高原)时,会发生什么?

  • 在这些地方,计算出的梯度会非常小,甚至接近于零。
  • 由于权重更新量 $ΔW = -α * 梯度$ ,一个微小的梯度意味着一个微乎其微的更新。
  • 结果就是,模型的训练会变得极其缓慢,甚至完全停滞,被“困”在一个不理想的解上。

核心思想:引入“惯性”

  • 让过去的步伐影响现在: 为了解决这个问题,我们引入了动量的概念。核心思想是:“让过去的更新步伐影响当前的更新步伐,从而让你保持朝你本来前进的方向继续移动”。
  • 物理类比:“下坡的汽车”
    • 标准梯度下降:就像一个没有惯性的物体(比如一根羽毛)。一旦地面变平,它受到的“力”(梯度)消失,它就立刻停下了。
    • 动量梯度下降:则像一辆正在下坡的汽车或一个滚动的铅球。当它从陡坡上冲下来时,会积累起巨大的动量(速度和惯性)。
      • 当它遇到一块平地时,它不会马上停下,而是会因为惯性继续向前冲。
      • 当它遇到一个小土坑(局部最小值)时,强大的动量可能会帮助它直接“冲”过这个小坑,继续寻找更深的山谷(全局最小值)。

实现方式:权重的更新不再仅仅取决于当前的梯度,而是由一个函数决定,这个函数综合了过去的更新步伐(即动量)和当前的梯度

我们引入新变量:速度 (Velocity)

为了实现“记忆”或“惯性”,我们不能只依赖当前的梯度。因此,算法引入了一个新的变量 $v$:

  • $v_t$​ = 在时间点 $t$ 的速度 (velocity at time t)
  • 这个 $v_t$​ 变量就是我们用来捕捉历史步伐信息的载体。它会累积过去的梯度信息,代表着我们的优化器当前前进的“动量”。

速度的更新 (Update of velocity)

动量法的核心公式: $$v_t\leftarrow\mu v_{t-1}-\alpha\nabla J(w_{t-1})$$

这个公式可以被拆解为两个部分:

  • 动量步 (Momentum step): $\mu v_{t-1}$​
    • $v_t−1$​ 是上一步的速度
    • $μ$ 是动量系数,通常被设置为一个接近1的数,比如 0.9。这个系数可以理解为“摩擦力”或者“阻力”。它决定了有多少上一时刻的动量可以被“维持”到当前时刻。μ=0.9 就意味着保留90%的上一时刻速度。如果 $μ=0$,则完全没有动量,算法退化为标准梯度下降。
  • 梯度步 (Gradient step): $−α∇J(w_t−1​)$
    • 这部分我们非常熟悉,它就是标准梯度下降中由当前梯度产生的“推动力”。

综合理解: 当前的新速度 $v_t​$,是衰减后的旧速度(惯性)和当前梯度产生的加速度(新的力)的矢量和。

一旦我们计算出了包含动量信息的新速度 $v_t$​,权重的更新就变得非常简单: $$w_t\leftarrow w_{t-1}+v_t$$ 我们不再直接用梯度 $- \alpha \nabla J$ 来更新权重。用的是合成后的速度 vt​ 来更新权重。因为 $v_t​$ 已经包含了历史信息和当前梯度信息,所以用它来更新权重会比单独使用当前梯度更加稳定和高效。

动量是如何累积的? 让我们手动展开几步,看看“速度” v 是如何累积历史梯度的。为方便书写,令 $g_t=\nabla J(w_t)$。

  • 第1步: 初始速度 $v_0=0$。$v_1=\mu v_0-\alpha g_0=-\alpha g_0$
  • 第2步: $v_2=\mu v_1-\alpha g_1=\mu(-\alpha g_0)-\alpha g_1$
  • 第3步: $v_3=\mu v_2-\alpha g_2=\mu(\mu(-\alpha g_0)-\alpha g_1)-\alpha g_2=-\alpha(\mu^2g_0+\mu g_1+g_2)$

从展开式可以看出,$v_t$​ 实际上是过去所有梯度的指数加权移动平均值

  • 如果历次梯度 $g_0$​,$g_1$​,$g_2$​,... 的方向都差不多,那么它们会在 $v_t​$ 中不断叠加,使得速度越来越快,从而加速收敛
  • 如果历次梯度的方向来回摆动(比如在狭窄的山谷中震荡),那么一正一负的梯度项会在 $v_t​$ 中相互抵消,从而抑制震荡
  • $μ$ 是一个需要调节的超参数。

过冲 Overshooting 问题

动量法能更快地到达最小值点,但如果如果运行时间过长,动量法会过冲最小值点,它会导致优化路径在最小值附近来回震荡,难以精确地收敛到最低点。

一个想法是:如果我们能更早地察觉到过冲,即更早地发现梯度的方向即将改变,那岂不是更好吗

Nesterov Momentum

标准动量法 (Standard Momentum)的更新公式:

速度更新: $v_t\leftarrow\mu v_{t-1}-\alpha\nabla J(w_{t-1})$

权重更新: $w_t\leftarrow w_{t-1}+v_t$

这个方法的逻辑是:在当前位置 $w_t−1$​ 计算梯度,然后将这个梯度产生的力与之前的动量结合,来决定最终的前进方向。

Nesterov 动量法 (Nesterov Momentum)更新公式:

速度更新: $v_t\leftarrow\mu v_{t-1}-\alpha\nabla J(w_{t-1}+\mu v_{t-1})$

权重更新: $w_t\leftarrow w_{t-1}+v_t$

唯一的不同点在于计算梯度的位置。标准动量法在当前位置 $w_t−1​$ 计算梯度,而Nesterov动量法则在“预估的未来位置” $w_{t−1}​+μv_{t−1}​$ 计算梯度。

$w_{t−1}​+μv_{t−1}​$​ 这一项代表了我们仅凭当前的惯性(动量),下一步将要到达的大致位置。Nesterov动量法的思想就是:不急着在原地计算梯度,而是先“假装”按惯性往前走一步,看看那个未来位置的坡度是怎样的,然后再用那个“未来”的坡度来修正我当前的方向。这赋予了算法一种“预见性”或“前瞻性”。

![[Pasted image 20250916162542.png]]

  • 左图:标准动量法
    • 我们可以看到,优化路径在最小值附近发生了剧烈的震荡和过冲。这是因为它“刹车”不及时,总是冲过头才反应过来。
  • 右图:Nesterov 加速梯度法 (Nesterov's Method)
    • 这条优化路径明显平滑得多。它也能快速地冲向最小值,但在接近谷底时,过冲的幅度被显著抑制了。优化器更早地“减速”,从而能更稳定、更精确地收敛到最小值点。
  • 为什么会这样?
    • 当优化器即将越过谷底时,它的动量方向仍然指向谷底另一侧。
    • 此时,它预估的未来位置 $w_{t−1}​+μv_{t−1}$​ 已经处在了对面的上坡路段。
    • 在这个上坡路段计算出的梯度,其方向与动量方向是相反的。
    • 这个“反向”的梯度就像一个提前施加的刹车力,它会有效地减小速度 $v_t​$ 的大小,从而阻止了严重的过冲。

Nesterov动量法的地位

  • Nesterov动量法在理论上和实践中都被证明几乎总是优于标准动量法。它提供了更快的收敛速度和更好的稳定性。
  • 在自适应优化算法(如Adam)普及之前,带动量的SGD(特别是Nesterov动量法) 曾是训练深度神经网络(尤其是计算机视觉领域的CNN)的业界标准
  • 即使在今天,它依然是一个非常强大和常用的优化器,很多研究者在特定任务上仍然会选择它而不是Adam。

2.7. Adaptive Learning Rates

之前我们讨论的学习率 $α$ 都是一个全局的、固定的超参数。即使我们使用学习率策略(schedules)来动态调整它,在某一个时间点,这个 $α$ 对于网络中所有的权重都是一视同仁的。自适应学习率算法则认为,这种“一刀切”的方法不够智能。

神经网络的损失函数表面不是一个平滑的碗,而是一个极其复杂的地形 (Complex Terrain)。这里有陡峭的悬崖、广阔的平原、狭窄的峡谷。一个固定的步长显然不适用于所有地形

自适应优化的指导原则:

  • 对不同区域使用不同步长 (Large strides for some area and cautious steps for others)
    • 在平坦开阔的区域(梯度小且稳定),我们应该迈开大步,快速通过。
    • 在崎岖不平的区域(梯度大且多变),我们应该小心翼翼,迈着小步前进,以防“一脚踩空”。
  • 步长调整的具体规则:
    • 当梯度变化快且不可预测时,使用小步长。这通常对应于损失函数的“峡谷”地带,梯度方向在两侧来回快速变化,大步长会导致剧烈震荡。
    • 当梯度变化慢且可预测时,使用大步长。这对应于损失函数的“平原”地带,梯度很小,大步长可以帮助我们加速离开这片区域。
  • 实现这一策略的关键机制:
    • 为模型中的每个参数维持独立的学习率。这是自适应算法最核心的创新。我们不再只有一个全局学习率 $α$,而是为每一个权重 $w_i$ 都分配一个属于它自己的学习率 $α_i$ 。
    • 学习率是动态调整的: 这些独立的学习率不是一成不变的,它们会在训练过程中,根据遇到的数据(即历史梯度信息)进行动态调整

2.7.1. AdaGrad

AdaGrad的指导思想:

  • 对于某个权重w,如果它的历史梯度一直很大 (High),那么它的学习率就应该变小 (Low)
  • 反之,如果它的历史梯度一直很小 (Low),那么它的学习率就应该变大 (High)

为了实现上述思想,AdaGrad引入了一个关键的“记忆”变量。

  • 引入变量 G: 算法引入了一个变量 G,用来捕捉历史梯度信息。它捕捉的是二阶矩 (second moment),通俗地说,它累积的是梯度的平方
  • G的更新规则: $$G_t\leftarrow G_{t-1}+(\nabla J(w_{t-1}))^2$$
  • 这里的 $G$ 是一个向量,其维度与权重 $w$ 相同。在每次迭代 $t$ 时,我们计算出当前梯度 $∇J$,将其逐元素平方,然后加到 $G$ 的上一时刻的值 $G_{t-1}$ 上。
  • 平方有两个作用:1. 保证了累加项永远是正数,所以 $G$ 只会单调递增;2. 对大的梯度给予更大的“惩罚”。

有了历史梯度累加器 $G$ 之后,AdaGrad的权重更新规则变为: $$w_t\leftarrow w_{t-1}-\frac{\alpha}{\sqrt{G_{t-1}+\epsilon}}\nabla J(w_{t-1})$$

  • $α$: 这是一个全局的、初始设定的基础学习率。
  • $\frac{\alpha}{\sqrt{G_{t-1}+\epsilon}}$: 这是真正的、对每个参数独立的、动态变化的“有效学习率”
  • 工作原理:
    • 对于某个经常更新且梯度较大的参数,其对应的 $G$ 值会迅速增大。$G$ 越大,分母 $G$ 就越大,导致其有效学习率变得越小
    • 对于某个更新稀疏且梯度较小的参数,其对应的 $G$ 值会增长缓慢。$G$ 越小,分母 $G$​ 就越小,其有效学习率就变得越大
    • $ε$ (epsilon): 这是一个非常小的正数(如 $10^{−8}$),其唯一的作用是防止分母为零,保证数值稳定性。

AdaGrad虽然思想很巧妙,但它有一个致命的缺陷:

  • 学习率会急剧下降,并最终趋近于零
  • 原因: 因为 $G$ 是通过不断累加梯度的平方来计算的,所以 $G$ 的值在整个训练过程中只会单调递增,永不减小
  • 后果: 随着训练的进行,分母 $G$ 会变得越来越大,导致所有参数的有效学习率最终都会变得极其微小。这会使得模型在还远未达到最优解时,就提前停止了学习,权重不再更新。

1. AdaGrad的最佳应用场景

  • 尽管存在上述缺陷,AdaGrad在一个特定领域表现得非常出色:处理稀疏数据 (Sparse Data)
  • 例如,在自然语言处理(NLP)中,词向量的更新就是稀疏的。常用词(如“的”)的参数会频繁更新,而罕见词(如“饕餮”)的参数则很少更新。AdaGrad能够自动地为罕见词参数赋予较大的学习率,为常用词参数赋予较小的学习率,这种特性非常适合此类任务。

2. 承上启下的作用

  • AdaGrad是自适应学习率算法的“开山之作”。它的核心思想——用历史梯度来调节当前学习率——极具启发性。
  • 它的致命缺陷(学习率单调递减)也直接催生了后续的改进算法。接下来的 RMSProp 和 Adam 算法,它们的核心目标之一就是解决AdaGrad中梯度累加器 G 无限制增长的问题,它们通过引入“衰减”机制,使得算法既能自适应,又不会过早地停止学习。

2.7.2. RMSProp

快速回顾AdaGrad的问题:

  • 梯度累加器 G: $G\leftarrow G+(\nabla J(w))^2$
  • 权重更新:$$w\leftarrow w-\frac{\alpha}{\sqrt{G+\epsilon}}\nabla J(w)$$ 这里的核心问题在于 $G$ 的更新方式。它是一个只增不减的累加器,会把所有历史梯度的平方都加起来,导致 $G$ 会变得越来越大。最终,这使得有效学习率(即公式中的系数 $α/√G$)变得越来越小,导致学习过早停止。

RMSProp的权重更新和AdaGrad完全一样: $$w\leftarrow w-\frac{\alpha}{\sqrt{G+\epsilon}}\nabla J(w)$$ 梯度累加器 G (关键改进): $$G\leftarrow\beta G+(1-\beta)(\nabla J(w))^2$$ RMSProp修改了 $G$ 的计算方式,引入了一个新的超参数 $β$ 。这个公式实际上是一个指数移动平均 (Exponential Moving Average, EMA)

  • $βG$: 在累加新的梯度信息之前,我们先给旧的累加器 $G$ 乘以一个衰减率 β。$β$ 通常是一个接近1的数,比如0.9或0.99。这一步相当于让$G$“忘记”一部分历史梯度信息。
  • $(1-β)(∇J(w))^2$: 然后,我们再把当前梯度的平方信息加进来,但它只占了 $(1-β)$ 的权重。

这种机制阻止了 $G$ 无限地增长下去。$G$ 现在变成了一个反映近期梯度大小的“动态平均值”,而不是历史总和。因此,分母 $√G$ 会稳定在一个合理的范围内,学习率不会再单调递减至零。

2.7.3. Adam

Adam算法同时维护了两个“记忆”变量 $v$ 和 $m$。

  • 变量 $v$: 学习率的自适应调整 (来自RMSProp)
    • 公式: $v\leftarrow\beta v+(1-\beta)(\nabla J(w))^2$
    • 和RMSProp的思路完全一样 ,只是把变量名从 G 换成了 v。这个变量 v 负责计算梯度平方的指数移动平均值。它的作用和RMSProp中的G一样,都是用来对学习率进行缩放,实现每个参数学习率的自适应。
  • 变量 $m$: 梯度的平滑 (来自Momentum)
    • 公式: $m\leftarrow\gamma m+(1-\gamma)(\nabla J(w))$
    • 这是一个新引入的一阶变量 $m$ 。这个公式,它和我们之前学习的“动量”中的速度更新规则在数学形式上是完全一样的。它计算的是梯度本身(而不是梯度的平方)的指数移动平均值。
    • 这个变量 $m$ 就像动量法中的“速度”向量,它对梯度方向进行了平滑,累积了过去一段时间的梯度方向信息,有助于加速收敛并抑制震荡。
  • 最终的权重更新: $$w\leftarrow w-\frac{\alpha}{\sqrt{v}+\epsilon}m$$

最终的权重更新规则将 $v$ 和 $m$ 两者结合了起来。

  • 我们更新的方向,不再是原始的梯度 $∇J$ ,而是经过动量平滑后的梯度 $m$。
  • 我们前进的步长,由基础学习率 $α$ 决定,但这个步长又被 $α / (√v + ε)$ 进行了自适应的调整。

2.8. Overfitting Problem

![[Pasted image 20250916171220.png]]

  • 左图:欠拟合 (Underfitting)
    • 表现: 模型用一条简单的直线来分割数据,但这条直线无法很好地区分红点和蓝点,犯了大量错误。
    • 原因: 模型的复杂度太低,学习能力不足。正如PPT所说,这通常发生在“神经网络的神经元太少”的情况下。它过于简单,无法捕捉数据中蕴含的复杂模式。
    • 补充 (高偏差): 在机器学习理论中,这被称为高偏差 (High Bias)。模型对数据的内在规律做出了过于简化的错误假设。这种模型在训练集和测试集上都会表现得很差。
  • 中图:理想拟合 (Good Fit)
    • 表现: 模型用一条平滑的曲线,恰到好处地将绝大多数红点和蓝点分开了。它识别出了数据的大致趋势,而没有去纠结个别的噪声点。
    • 原因: 模型的复杂度与数据的复杂度完美匹配。它学到了数据的真实模式,并且拥有良好的泛化能力,即在未见过的新数据上也能表现良好。这是我们追求的理想状态。
  • 右图:过拟合 (Overfitting)
    • 表现: 模型用一条极其扭曲、复杂的曲线,完美地将训练集中的每一个数据点都正确分类了。为了迁就个别的“异常点”,这条分界线变得毫无规律可言。
    • 原因: 模型的复杂度过高,学习能力“过剩”。如PPT所说,这通常发生在“神经网络有太多层和神经元”的情况下。它不仅学习了数据的真实模式,还把数据中的噪声和随机波动也当作了真实模式一并“背”了下来。
    • 补充 (高方差): 这被称为高方差 (High Variance)。模型对训练数据极其敏感,任何微小的扰动都可能导致学习结果的巨大变化。这种模型在训练集上表现完美(损失极低),但在测试集上表现会非常糟糕。

在传统机器学习中,人们常常花费大量精力去寻找那个复杂度“刚刚好”的模型。但在深度学习中,我们采取了一种更实用的策略。

  • 困境: 我们很难事先知道一个任务到底需要多复杂的模型才是“刚刚好”的。
  • 深度学习的对策:
    1. 从一个足够复杂的模型开始 (Start with an overly complicated model)
      • 我们不再小心翼翼地尝试从小模型开始。相反,我们先构建一个远超实际需求的大型网络(层数深、神经元多),确保它的容量 (capacity) 足够大,大到足以学习最复杂的模式,甚至足以产生过拟合。
    2. 然后应用技术来防止过拟合 (Then apply certain techniques to prevent overfitting)
      • 这是关键一步。在拥有了一个“能力过剩”的模型后,我们再通过一系列被称为正则化 (Regularization) 的技术来对它进行约束和限制,防止它去学习噪声,引导它学习更具泛化性的模式。

偏差-方差权衡 (Bias-Variance Tradeoff) 欠拟合和过拟合背后是统计学中的一个经典概念:偏差-方差权衡。

  • 偏差 (Bias): 衡量模型预测值与真实值之间的差距,反映了模型的拟合能力。高偏差即欠拟合。
  • 方差 (Variance): 衡量模型在不同训练集上预测结果的变动程度,反映了模型对数据扰动的敏感度。高方差即过拟合。 理想的模型是低偏差和低方差的。深度学习的策略可以理解为:先构建一个低偏差/高方差的模型,然后用正则化技术在不显著增加偏差的前提下,有效降低其方差

2.9. Weight Regularization

2.9.1. Regression

一个观察结论:模型中出现过大的权重值,往往是过拟合的征兆,因为一个权重值很大,意味着模型对这个权重对应的输入特征非常敏感。即使输入特征只有微小的变化,也会被这个大权重放大,从而对输出产生巨大影响。这种高度敏感性使得模型能够学习到训练数据中的噪声和偶然性,从而在拟合训练数据时形成我们之前看到的那种极其扭曲、复杂的决策边界。相反,一个权重较小的模型通常更“平滑”,泛化能力更好。

基于这个观察,我们的策略就是在损失函数中加入对权重大小的惩罚项。我们的优化目标不再仅仅是最小化预测误差,而是变成了最小化 (预测误差 + 权重大小惩罚)。这就迫使模型在拟合数据的同时,必须努力保持其权重值尽可能小,从而达到一个平衡。

这个惩罚项的引入,由一个超参数 $λ$ 来控制其强度。$λ$ 越大,对大权重的惩罚就越重,模型就越趋向于选择更小的权重。

两种最常见的衡量“权重大小”并施加惩罚的方式:L1 正则化和 L2 正则化:

  1. L1 正则化 (Lasso Regression)

公式: $$\min\frac{1}{M}\sum_iJ(h_\theta(x_i),y_i)+\lambda\sum_j|\theta_j|$$ L1 正则化添加的惩罚项是所有权重 $θ_j​$ 的绝对值之和 (也称为 L1 范数)。

L1 正则化有一个非常独特的特性:它会倾向于将许多不那么重要的权重直接惩罚到恰好为 0。当一个权重变为0时,就相当于在网络中彻底“删除”了它对应的那个输入特征。因此,L1 正则化能够自动地筛选出对模型真正重要的特征,忽略掉无关的特征,从而实现自动的特征选择。这在特征维度非常高(比如成千上万个特征)而很多特征可能无用的场景中尤其有效。

  1. L2 正则化 (Ridge Regression)

公式: $$\min\frac{1}{M}\sum_iJ(h_\theta(x_i),y_i)+\lambda\sum_j\theta_j^2$$ 与 L1 不同,L2 惩罚项不会倾向于让权重变为 0,而是让它们都变得非常小。L2 惩罚项的导数与权重大小成正比。权重越大,受到的“惩罚”和“拉回”的力度就越大;权重越小,拉回的力度也越小,所以很难被精确地拉到0。

L2 正则化会使得模型的权重分布更加“平滑”和“分散”,它倾向于让网络中的所有权重都对结果做出一点贡献,而不是让少数几个权重占据主导地位。这能有效防止模型对任何单一特征的过度依赖,从而提升泛化能力。

L2 正则化“计算上更高效”,因为平方项在数学上是处处可导且平滑的,这使得优化过程更加稳定和简单。

2.9.2. Random Dropout

![[Pasted image 20250916172111.png]]

  • 左图:标准神经网络 (Standard Neural Network)
    • 这是一个全连接的网络,在每一次训练迭代中,所有的神经元和连接都参与计算。
  • 右图:应用Dropout后的网络 (After applying dropout)
    • 这是Dropout的核心操作。在训练过程中,对于网络的某一层,我们随机地“丢弃”一部分神经元
    • “丢弃”意味着这些神经元(图中打叉的)以及它们的所有输入和输出连接,在本次迭代中都暂时失效,不参与任何计算。
    • 这里我们可以随机选择 50% 的神经元丢弃。这个比例,即丢弃率 $p$,是一个我们可以自己设置的超参数。
    • 关键在于“随机”:在每一次新的训练迭代(即处理下一个mini-batch)中,我们都会重新随机选择一批神经元来丢弃。这意味着,模型在每次迭代中训练的都是一个略有不同的、“残缺”的子网络。

Dropout有效的核心思想是防止对任何一个神经元的依赖。可以从两个层面来深入理解:

  • 1. 强迫网络学习更鲁棒的特征
    • 在一个没有Dropout的标准网络中,神经元之间可能会产生复杂的“协同适应”关系。也就是说,某些神经元可能会过度依赖其他特定神经元的存在和输出,它们“合作”起来才能解决问题。这种“合作关系”可能非常脆弱,一旦某个环节出错,整个模型就可能失效,这正是过拟合的一种表现。
    • Dropout打破了这种可能性。因为任何一个神经元都可能在下一次迭代中“消失”,所以没有任何一个神经元敢于“信任”或“依赖”它的任何邻居。为了在每次迭代中都能对最终结果做出贡献,每个神经元都被迫学习一些更加独立、更加鲁棒的特征
  • 2. 集成学习的视角 (Ensemble Learning)
    • 这是对Dropout更深层次的理解。训练一个带Dropout的网络,可以被看作是同时训练成千上万个不同的、共享权重的“子网络”
    • 每一次迭代,我们都在训练一个随机采样的“子网络”(如右图所示)。
    • 集成学习是机器学习中一种非常强大的思想,它通过训练多个不同的模型并将它们的结果结合起来,通常能获得比单个模型好得多且泛化能力更强的性能。
    • Dropout提供了一种非常高效的方式来实现这种模型集成。我们只用训练一个网络的时间和参数,就近似达到了训练一个巨大模型集成的效果。

具体做法:

  • 训练时,我们随机丢弃神经元。
  • 测试或实际应用时,我们希望模型能发挥出它的全部实力,所以我们不会进行任何随机丢弃,而是使用完整的、未经“破坏”的网络。
  • 但是,如果在测试时直接使用所有神经元,会导致一个问题:每一层网络输出的“能量”(激活值的总和)会比训练时平均要高(因为训练时有一半神经元被关闭了)。
  • 解决方案 (Inverted Dropout):为了补偿这一点,现代深度学习框架普遍采用一种叫做“反向Dropout”的技巧。具体做法是,在训练阶段,当丢弃掉一部分神经元后,我们会将剩余“存活”下来的神经元的输出值,乘以一个系数 $1 / (1 - p)$
    • 例如,如果丢弃率 $p=0.5$,我们就将剩余神经元的输出乘以 $1 / (1 - 0.5) = 2$。
    • 这样做的好处是,无论丢弃率是多少,都能保证该层输出的期望值在训练和测试时是保持一致的。这样,在测试时我们就可以直接使用完整的网络,而无需做任何额外的操作,非常方便。

Dropout通常与大型网络(神经元数量多)配合使用效果最好。策略就是先构建一个容易过拟合的大模型,然后用Dropout来对其进行正则化。

2.9.3. not random Dropout(meDrop)

![[Pasted image 20250916172529.png]]

  • 随机Dropout的回顾: 随机Dropout就像一个公司在执行每个项目时,都随机地让一部分员工放假。这个过程旨在锻炼每个员工的独立工作能力,但最终公司的组织架构(完整的网络)并没有改变。
  • meProp的核心思想: meProp则像一个精明的项目经理。他先让整个团队(完整的网络)一起工作一段时间,仔细观察和评估每个员工(神经元)的“工作积极性”和“贡献度”。然后,他会裁掉那些“最不活跃”、“贡献最少”的员工,从而打造一个更精简、更高效的核心团队。

第一阶段:训练与“活跃度”追踪

  • 流程: 首先,让一个完整的、原始的网络模型进行数次迭代的训练。
  • 追踪: 在这个训练过程中,算法会持续追踪网络中每个路径和神经元的“活跃度” (activeness)。
  • 可视化: 图中的神经元颜色深浅就代表了这种“活跃度”。颜色越深,表示该神经元在计算中越重要、越活跃;颜色越浅,则表示该神经元贡献很小,处于“打酱油”的状态。
  • 补充: “活跃度”的衡量标准可以有很多种,例如神经元输出值的平均大小、流经该神经元的梯度大小等。

第二阶段:模型简化 (剪枝)

  • 流程: 在观察了一段时间后,算法开始进行模型简化
  • 淘汰: 它会识别出那些“最不活跃”的神经元(颜色最浅的),并将它们从网络中永久性地剔除
  • 结果: 如右侧的两张图所示,网络结构变得更小、更稀疏。
  • 迭代: 这个“训练-观察-剪枝”的过程可以根据需要重复进行,从而逐步得到一个最优的精简网络。

这实际上也可以叫做“网络剪枝”技术。它会永久性地改变网络结构,移除部分神经元和权重。最终得到的模型比原始模型更小、参数更少

这样的方法基于一个“彩票假设”。它指出,一个大型的、随机初始化的神经网络中,天然就隐藏着一个微小的“中奖彩票”子网络。如果我们能通过某种方法(如剪枝)找到这个“中奖”的子网络,并将其权重恢复到初始状态单独进行训练,它就能用远少于原始网络的参数和计算量,达到甚至超过原始大网络的性能。这个理论为网络剪枝的有效性提供了强有力的理论支撑。

2.9.4. Early Stopping

这项技术的哲学思想非常朴素,就是不要给网络过拟合的时间

要实现早停,我们首先需要将原始数据进行划分。

  • 训练集 (Training Set): 用于训练模型,即模型直接从中学习数据和更新权重。
  • 验证集 (Validation Set): 同样是带标签的数据,但它不参与模型的训练过程。它的唯一作用是在训练过程中,像一个“随堂测验”一样,用来监控和评估模型在未见过的数据上的泛化能力。
  • 测试集 (Test Set): 完全保留到最后,在模型训练完成后,用来进行最终的、一次性的性能评估,模拟模型在真实世界中的表现。

早停的核心,就是在训练的每一个世代 (Epoch) 结束后,都用验证集来测试一下模型。当(验证集)准确率停止提升时,我们及时停止。

在实际应用中,验证集的性能可能会有轻微的随机波动。如果验证集准确率下降一次就立刻停止,可能会过于敏感。因此,我们通常会引入一个叫做“耐心” (Patience) 的超参数。

  • Patience = N 意味着,我们会容忍验证集性能连续 N 个 Epoch 没有超过历史最好成绩。如果在 N 个 Epoch 后,性能依然没有提升,我们才真正停止训练。这使得早停机制更加稳健。

实践中,监控验证集损失 (Validation Loss) 是更常见、更可靠的选择。因为损失函数的值是连续且平滑的,它能更灵敏地反映模型性能的微小变化,为我们提供更可靠的早停信号。而准确率是离散的,可能在损失已经开始恶化时,准确率还没有发生变化。

我们的目标是找到验证集损失最低的那个点。当验证集损失停止下降并开始持续上升时,就触发早停。

早停的优缺点

  • 优点:
    • 极其简单有效: 它是最容易理解和实现的正则化方法之一。
    • 自动确定训练轮数: 它提供了一种自动化的方式来决定最佳的训练轮数,避免了手动设置一个固定的、可能不合适的Epoch数。
    • 节省训练时间: 通过在模型开始过拟合时及时停止,可以节省大量的计算资源和时间。
  • 缺点:
    • 需要划分验证集: 它要求你从数据中分出一部分作为验证集,这部分数据不能用于训练模型。但在绝大多数情况下,这种“牺牲”是完全值得的。
    • 优化过程与正则化耦合: 它将“寻找最优参数”和“防止过拟合”两个目标耦合在了一起。你因为要防止过拟合而提前停止了训练,但这可能也阻止了模型参数进一步收敛到损失函数的更低点。不过在实践中,这通常不是一个大问题。

![[Pasted image 20250916173039.png]]

2.10. Network Distillation

![[Pasted image 20250916174029.png]]

网络蒸馏技术的出发点是:我们常常可以训练出一个非常巨大、复杂且性能强大的“怪兽”模型,但这个模型因为太大、太慢,很难被部署到实际应用中(比如手机端)。那么,我们有没有办法将这个大模型所学到的“智慧”,“传授”给一个更小、更轻便的模型呢?网络蒸馏就是来解决这个问题的。

即,训练一个小模型(学生),使其产生与大模型(导师)相同的输出

  • 导师模型 (Tutor Model): 如图中的绿色曲线 "Large model" 所示。这是一个已经训练好的、非常复杂的“专家”模型。它性能强大,但计算成本高昂。
  • 学生模型 (Student Model): 如图中的蓝色虚线 "Small model" 所示。这是一个结构简单、计算量小的“新手”模型。我们希望它能学到导师的本领,但保持自己轻便的身材。

软标签 (Soft Labels)

  • 原始的“硬标签” (Hard Labels): 在原始的数据集中,标签通常是“非黑即白”的。比如在一个猫狗分类任务中,一张猫的图片,其标签就是 [1, 0] (100%是猫,0%是狗)。这种信息量很小。
  • 导师的“软标签” (Soft Labels): 经过训练的“导师模型”在看到同一张猫的图片时,它的输出可能是一个概率分布,比如 [0.95, 0.05]
    • 这个输出就是所谓的“软标签”。它蕴含了更丰富的信息:导师不仅告诉学生“这张图是猫”,还告诉他“这张图有5%的可能看起来有点像狗”。
    • 这种类别之间的相似性信息,是原始的硬标签无法提供的,是导师模型从海量数据中学到的“暗知识”,学生模型学的就是这种比原始硬标签更丰富、更“模糊”的软标签信息。
  • 学习过程学生模型的目标,就是学习并模仿导师模型输出的这个“软标签”概率分布。通过这种方式,学生不仅学到了“是什么”,还学到了“像什么”,从而获得了比直接在硬标签上学习好得多的泛化能力。

1. 为什么蒸馏有效?

  • 软标签提供了一个比硬标签更平滑、信息量更丰富的监督信号。它为学生模型的优化指明了一条更容易走的路,因为它告诉了学生不同类别之间的细微差别,这是训练小模型能接近大模型性能的关键。

2. 实际应用场景

  • 模型压缩与部署: 这是网络蒸馏最主要的应用。例如,谷歌在研究中训练了一个巨大的模型用于语音识别,然后通过蒸馏技术,将知识迁移到一个小得多的、可以离线运行在安卓手机上的模型中。
  • 集成模型压缩: 我们可以先训练一个由多个模型构成的集成模型 (Ensemble Model) 作为“导师”,然后将这个集成模型的“集体智慧”蒸馏到一个单一的、更小的“学生”模型中。这样,学生模型就能以单一模型的计算成本,达到接近集成模型的性能。

3. 温度 (Temperature) 在实践中,为了让“软标签”包含更丰富的信息,通常会引入一个叫做“温度”(Temperature, T) 的超参数。在计算Softmax概率时,将原始的logits除以 T。

  • T > 1: 会使得概率分布变得更“平滑”,放大那些小概率类别的信息,强迫导师模型“吐露”更多关于类别相似性的暗知识。
  • T = 1: 就是标准的Softmax。 在蒸馏时,通常会用一个较高的温度来训练学生模型,让其学习软标签;同时也会让学生模型学习原始的硬标签,以保证基础的准确性。

2.11. Batch Normalization

2.11.1. Normalization

数据的不同特征(features)通常具有完全不同的数值范围和单位

我们举一个例子:公寓的“卧室数量”(通常是1-5)和“平方英尺”(通常是几百到几千)。如果直接将这些原始数据输入模型,数值范围大的特征(如平方英尺)将在梯度计算中占据主导地位,导致损失函数的“等高线”图变得非常扁平,使得梯度下降的收敛过程变得困难和缓慢。

归一化的目标就是将所有输入特征都转换到相同的数值范围内,通常是 $[0, 1]$ 区间。

方法 (Min-Max Scaling):

公式为: $$x^{\prime}=\frac{x-\min(x)}{\max(x)-\min(x)}$$ 这个公式计算的是,一个值 x 在其整个数据范围(从 min 到 max)中所处的位置比例。当 x 等于 min(x) 时,x'为0;当 x 等于 max(x) 时,x'为1。

  • 优点: 简单直观,能确保数据被严格限制在特定范围内。
  • 缺点: 对异常值 (outliers) 非常敏感。如果数据中存在一个极大的异常值,那么 max(x) 就会变得非常大,导致绝大多数正常的数据点都被压缩到一个非常非常小的区间内,失去了它们原本的分布信息。

2.11.2. Standardization

与归一化不同,标准化的目标不是将数据限制在特定范围,而是将其分布调整为均值为0,标准差为1

方法 (Z-score Normalization):

公式为: $$x^{\prime}=\frac{x-\mathrm{mean}(x)}{\mathrm{std}(x)}$$

  • 直观理解:
    1. x - mean(x): 先将整个数据集的中心点平移到0。
    2. / std(x): 再将数据的“单位”缩放到1。标准化后的值(也称为z-score)表示原始值偏离均值的标准差倍数。
  • 优点: 对异常值的鲁棒性比归一化好得多,因为均值和标准差不像最大/最小值那样容易受到极端值的影响。
  • 在深度学习中的地位标准化是深度学习和许多其他机器学习算法中最常用、最首选的数据预处理方法。神经网络的训练在输入数据为零中心且方差相近时,通常会更稳定、更快速。

与批标准化的联系

  • 我们刚刚讨论的归一化和标准化,通常是作为预处理步骤仅仅对输入到网络第一层的原始数据进行的。
  • 那么网络内部呢? 当数据流经网络深处时,比如从第4层传递到第5层,经过一系列复杂的权重矩阵相乘和非线性激活函数变换后,输入到第5层的激活值 的分布可能会变得非常奇怪——它们的均值可能不再是0,方差也可能变得很大或很小。
  • 这种网络内部数据分布在训练过程中不断发生变化的现象,被称为“内部协变量偏移” (Internal Covariate Shift)。它会给深层网络的训练带来困难,就像让每一层都在试图学习一个“移动靶”一样。

2.11.3. Batch Normalization

![[Pasted image 20250916174840.png]]

我们可以把批标准化的过程理解为“先统一,再赋予自由

第一步:标准化 (Standardize)

  • 做什么: “在计算完小批量(minibatch)的均值和标准差后,进行标准化”。
  • 具体流程:
    1. 我们拿到一个 mini-batch 的数据(这些数据是上一层网络传来的激活值)。
    2. 计算这个 mini-batch 内所有数据的均值 (mean) 和标准差 (std)
    3. 使用我们上一页学过的标准化公式 $x′=(x−mean)/std$,将这个 mini-batch 内的每一个数据点都进行转换。
  • 效果: 经过这一步,无论这个 mini-batch 的原始数据分布如何,都会被强行转换成一个均值为0,标准差为1的标准正态分布。
  • 图示分析:
    • 原始数据 (Original data): 图中的例子包含6个数据点,计算出的均值约为10,标准差约为6.78。
    • 标准化之后 (After standardize): 这6个数据点被重新映射,它们的中心点移动到了0,分布范围也被“压缩”,新的均值为0,标准差为1。
  • 目的: 这一步的目的是解决“内部协变量偏移”问题。通过强行统一每一层输入的分布,使得网络每一层的学习环境都更加稳定,从而加速训练。

第二步:缩放与偏移 (Scale and Offset)

  • 做什么: “通过γ进行缩放,通过β进行偏移(γ和β都是待学习的参数)”。
  • 一个显而易见的问题: 我们费了半天劲把数据变成了均值为0、标准差为1的分布,为什么紧接着又要用 $γ$ 和 $β$ 把它变回去呢? 这不是多此一举吗?
  • 答案:为了恢复网络的表达能力 (To Restore Representational Power)
    • 问题所在: 如果我们强行让每一层的输入都固定为均值为0、标准差为1的分布,这可能过于死板,会限制网络的学习能力。
    • 一个极端的例子: 想象一下,如果某个隐藏层使用的是Sigmoid激活函数。如果我们把输入给它的数据全都限制在0附近的一个很小的范围内(标准正态分布大部分数据都在 $[-1, 1]$ 之间),这就意味着激活函数的输入永远都处在它中间那段近似线性的区域。这会导致Sigmoid函数失去了其宝贵的非线性!网络花了很大力气学习的非线性能力就这么被“标准化”给破坏了。
    • 解决方案: 引入两个可以学习的参数 $γ$ (gamma) 和 $β$ (beta)
      • $γ$ : 缩放因子,负责调整分布的宽度(标准差)。
      • $β$: 偏移因子,负责调整分布的中心点(均值)。
    • 关键在于“可学习”: $γ$ 和 $β$ 与网络的权重 $w$ 一样,都是通过反向传播自动学习的。这给了网络一个“选择权”:
      • 如果网络发现均值为0、标准差为1的分布就是最好的,它可以通过学习让 $γ=1$, $β=0$。
      • 如果网络发现,对于某一层来说,一个均值为0.5、标准差为2的分布能让模型学得更好,它就可以通过学习来调整 $γ$ 和 $β$ 的值,将分布调整到那个理想的状态。
      • 在最极端的情况下,网络甚至可以学到 γ = 原始的标准差β = 原始的均值,这样就能完全抵消掉第一步的标准化,恢复出原始的分布。
  • 图示分析:
    • 缩放后 (After scale by 2): 在这个例子中,假设网络学到的$γ=2$,标准化后的数据点都被乘以2,分布被拉宽。
    • 偏移后 (After offset by 0.5): 接着,假设网络学到的 $β=0.5$,所有数据点再被加上0.5,整个分布的中心点从0移动到了0.5。

总结: 批标准化的完整过程是:先用简单粗暴的方式将数据分布“拉”到一个标准起跑线上(标准化),然后再通过 $γ$ 和 $β$ 这两个可学习的参数,赋予网络“权力”,让它自己去寻找对当前层来说最合适的分布位置和宽度。

训练 (Training) vs. 推理 (Inference)

  • 训练时,均值和标准差是在每个 mini-batch 上计算的。
  • 但在推理(测试或部署)时,我们可能一次只处理一个样本,计算其均值和标准差是无意义的。
  • 解决方案: 在训练过程中,Batch Norm层会维护一个全局的、所有 mini-batch 的均值和标准差的移动平均值。在推理时,就使用这个全局的统计量来进行标准化,从而保证了模型输出的确定性。

![[Pasted image 20250916175354.png]]

使用批标准化后,我们在训练模型时会观察到的现象:

  • “完成一个epoch需要更长的时间” (Longer time to do one epoch)
    • 原因: 这是因为批标准化层本身引入了额外的计算。在每个mini-batch通过时,网络都需要额外计算该批次的均值、标准差,并执行标准化和缩放/偏移操作。这些计算会增加每个训练迭代的耗时,从而拉长了完成一个完整epoch所需的时间。
  • “但能更快地达到所需精度” (But faster to achieve accuracy)
    • 原因: 这正是我们愿意接受第一个缺点的根本原因。虽然每个epoch变慢了,但批标准化极大地稳定和加速了整个训练过程的收敛
    • 补充: 这种加速来自于几个方面:
      • 允许使用更高的学习率: BN使得损失曲面变得更平滑,减少了梯度剧烈变化的可能性。这使得我们可以放心地使用更大的学习率来加速训练,而不用担心模型发散。
      • 减少对初始化的依赖: BN使得网络对权重的初始值不再那么敏感。
      • 最终结果是,我们需要训练的epoch总数大大减少。原来可能需要100个epoch才能达到的精度,现在可能只需要20个epoch。因此,总的训练时间通常会显著缩短

批标准化的用法与技巧

  • 可以取代输入数据的标准化
    • 用法: 你可以在网络的第一层就放置一个批标准化层(如图中 Input 之后所示)。它会自动地对你输入的原始数据进行标准化。
    • 补充: 这在实践中非常方便,可以简化数据预处理的流程。虽然提前对数据进行标准化仍然是一个好习惯,但BN的存在使得模型对输入数据的尺度不再那么敏感。
  • 卷积层/全连接层中的偏置项可以省略
    • 这是一个非常重要且常用的优化技巧
    • 原因: 让我们回顾一下流程。一个标准的卷积/全连接层计算的是 $y = Wx + b$ (其中 $b$ 是偏置项)。紧接着的BN层的第一步,就是要减去批次的均值 $μ$。
    • 整个计算就变成了 $BN(Wx + b)$。在计算均值时,$mean(Wx + b) = mean(Wx) + b$ 。在标准化时,减去均值就变成了 $(Wx + b) - (mean(Wx) + b) = Wx - mean(Wx)$。
    • 我们可以看到,偏置项 $b$ 的效果被减去均值的操作完全抵消了。而BN层本身就有一个可学习的偏移参数 $β$ ,这个 $β$ 完全可以起到原先偏置项 $b$ 的作用。
    • 结论: 因此,当一个卷积层或全连接层后面紧跟着一个BN层时,这个卷积层/全连接层自身的偏置项 $b$ 就成了冗余。在代码实现中,我们应该将该层的 $bias$ 参数设置为 $False$,这样可以节省少量参数,让模型更精简。
  • 在网络中的放置位置
    • 图下方的结构 Layer -> Batch Norm -> ActivationFn 展示了BN层最经典、最常用的放置位置。
    • 顺序线性变换层 (Conv/Linear) -> 批标准化层 (BN) -> 激活函数层 (Activation)
    • 逻辑: 这样做的目的是,对线性变换的输出进行标准化,使其分布稳定,然后再送入非线性激活函数。这可以确保激活函数的输入总是在一个比较“健康”的区间,避免进入饱和区导致梯度消失。

. 批标准化的正则化效果

  • BN在一定程度上也起到了正则化 (Regularization) 的作用,有助于防止过拟合。
  • 这种正则化效果来自于它在每个mini-batch上计算均值和方差所引入的噪声。因为每个mini-batch都是对整个数据集的抽样,其统计量都略有不同。这种轻微的噪声使得模型不能依赖于任何一个特定的数据分布,从而迫使它学习到更鲁棒的特征。
  • 在实践中,使用了BN后,有时可以减少甚至移除Dropout等其他正则化技术。

2. 对批次大小 (Batch Size) 的依赖

  • BN的一个关键弱点是它的效果依赖于一个足够大的Batch Size
  • 如果Batch Size太小(比如2, 4, 8),那么在这个小批次上计算出的均值和方差将会有很大的噪声,不能很好地代表整个数据集的统计分布,反而会损害模型的性能。
  • 在Batch Size受限的情况下(例如,在一些内存有限的计算机视觉任务中),层标准化 (Layer Normalization) 或 组标准化 (Group Normalization) 等替代方案通常会是更好的选择。