01. 基础与神经网络机制
1. L1 与 L2 正则化
为什么需要正则化?
模型出现 Overfitting, 从 Bias-Variance 的角度看,这通常意味着模型的 Variance 过高:模型对训练数据过于敏感,学到了训练集中的噪声或偶然规律。
正则化的基本思路是:
在拟合训练数据的同时,限制模型参数不要变得过于极端。
L1 和 L2 都是在原本的 loss 后面加一个“惩罚项”,目的都是不让模型权重太夸张,正则化本质上是在 降低 Variance,但也可能带来更高的 Bias。
\[ \mathcal L_{\text{L1}} =\mathcal L_{\text{data}}+\lambda\sum_i |w_i| \]
\[ \mathcal L_{\text{L2}} =\mathcal L_{\text{data}}+\lambda\sum_i w_i^2 \]
| 对比 | L1 | L2 |
|---|---|---|
| 惩罚方式 | 权重绝对值 | 权重平方 |
| 对小权重的效果 | 持续向 0 推 | 越接近 0,推力越小 |
| 最终结果 | 容易产生大量 0 权重 | 通常只把权重变小 |
| 主要作用 | 稀疏化、特征选择 | 限制大权重、提高稳定性 |
| 常见场景 | 高维稀疏特征 | 线性模型和神经网络 |
简单来说:
- L1 更像删除不重要的特征。
- L2 更像让模型不要过度依赖某些特征。
为什么 L1 会产生稀疏权重?
L1 的惩罚项是:
\[ |w|. \]
当 \(w\neq 0\) 时,它对权重的影响大致为:
\[ \frac{\partial |w|}{\partial w} = \operatorname{sign}(w). \]
这意味着,只要权重还没有变成 0,L1 就会用近似固定的力度把它向 0 推。
此外,\(|w|\) 在 \(w=0\) 的位置有一个尖点。使用适合 L1 的优化方法时,一个较小且不重要的权重到达 0 后,可以直接保留在 0。
因此,L1 容易得到这样的结果:
\[ w=[1.2,\;0,\;0,\;-0.7,\;0]. \]
这相当于模型只保留少数有用特征,因此 L1 也可以用于自动 Feature Selection。
为什么 L2 通常只缩小权重?
L2 的惩罚项是:
\[ \frac{1}{2}w^2. \]
它对权重的梯度是:
\[ \frac{\partial \frac{1}{2}w^2}{\partial w} = w. \]
权重越大,L2 把它向 0 拉的力度越大;权重越小,这个力度也越小。
例如:
- 当 \(w=10\) 时,L2 会产生很强的限制;
- 当 \(w=0.001\) 时,L2 的影响已经非常小。
所以 L2 更容易得到这样的结果:
\[ w=[0.8,\;0.02,\;-0.01,\;-0.5,\;0.03]. \]
大权重被明显压小,但小权重通常不会刚好变成 0。因此,L2 主要用于控制权重大小,而不是删除特征。
什么时候使用 L1?
L1 适合“特征很多,但真正有用的可能只有少数几个”的情况,例如:
- 文本中的 Bag-of-Words 特征;
- 大量 One-Hot 类别特征;
- 高维广告或推荐系统特征;
- 希望模型能够自动选择特征;
- 希望模型更加稀疏、容易解释。
例如,一个线性模型原本使用 10,000 个特征。加入 L1 后,可能只有几百个特征的权重不为 0。
但如果多个特征高度相关,L1 可能只保留其中一个,并将其他相关特征设为 0。更换训练数据后,它选择的特征也可能发生变化。
什么时候使用 L2?
L2 适合“大多数特征都有一定作用,但不希望某些权重过大”的情况,例如:
- 线性回归;
- 逻辑回归;
- 特征之间存在较强相关性;
- 神经网络训练;
- 主要目标是提高模型稳定性,而不是删除特征。
当多个特征高度相关时,L2 通常会把权重分配给多个相关特征,而不是只保留其中一个。因此,它的结果通常比 L1 更稳定。
什么时候使用 Elastic Net?
如果特征很多,而且特征之间还存在较强相关性,可以结合 L1 和 L2:
\[ \mathcal L = \mathcal L_{\text{data}} + \lambda_1\sum_i|w_i| + \frac{\lambda_2}{2}\sum_iw_i^2. \]
这就是 Elastic Net。
它同时具有:
- L1 的稀疏性,可以删除不重要的特征;
- L2 的稳定性,可以处理相互关联的特征。
因此,可以这样选择:
- 希望获得稀疏模型或自动选择特征:优先考虑 L1;
- 希望限制大权重并提高稳定性:优先考虑 L2;
- 既希望选择特征,又存在大量相关特征:考虑 Elastic Net。
Weight Decay 是什么?
每次更新参数时,除了根据 Loss 更新权重,还额外把权重向 0 缩小一点。
普通梯度下降大概是:
\[ w \leftarrow w - \eta \nabla L \]
加上 weight decay 后,可以理解为:
\[ w \leftarrow (1-\eta\lambda)w - \eta \nabla L \]
其中:
- \(\eta\) 是 learning rate。
- \(\lambda\) 是 weight decay 强度。
- \((1-\eta\lambda)w\) 表示先把权重缩小一点。
所以 weight decay 的效果是:
- 防止权重越来越大。
- 让模型不要过分依赖某几个特别大的参数。
- 通常能减少过拟合。
Weight Decay 和 L2 是一回事吗?
在普通 SGD 中,L2 Regularization 和 Weight Decay 基本等价。
为什么 Adam 中更常使用 AdamW?
在 Adam 中,L2 产生的梯度也会经过 Adam 的动量计算和自适应缩放。因此,不同参数受到的实际衰减程度可能不同。
AdamW 将 Weight Decay 与 Loss 的梯度更新分开:
- Adam 根据 Loss 的梯度更新参数;
- Weight Decay 单独把参数缩小一点。
因此,AdamW 更符合“每次更新时让权重衰减一点”的定义,也更容易单独控制正则化强度。
简单来说:
- SGD + L2:通常等价于 Weight Decay;
- Adam + L2:不完全等价于 Weight Decay;
- AdamW:将 Weight Decay 与梯度更新分开处理。
这也是神经网络训练中经常使用 AdamW 的原因之一。
为什么常用 L1/L2,而较少使用 L3/L4?
- L1 是凸的并能产生稀疏解,L2 光滑、旋转对称且优化简单。
- \(p>2\) 的 \(L_p\) 惩罚会更强地惩罚大权重,但通常没有 L1 的稀疏性,也缺少 L2 的统计、数值和解析便利。
- \(0<p<1\) 更接近直接计算非零参数个数,但非凸,优化更困难。
2. BatchNorm 与 LayerNorm
上一节的 L1、L2 是在 Loss 中直接限制模型权重,属于显式正则化。BatchNorm 和 LayerNorm 则作用于网络的中间激活值,主要目的是改善训练过程。
- L1/L2:限制模型参数,主要用于控制 Overfitting。
- BatchNorm/LayerNorm:调整激活值的分布,主要用于稳定优化。
- BatchNorm 可能附带一定正则效果,但这不是它的主要目的。
BatchNorm
BatchNorm 对同一个 mini-batch 中、同一特征或通道的激活值进行标准化:
\[ \hat{x} = \frac{x-\mu_{\text{batch}}} {\sqrt{\sigma_{\text{batch}}^2+\epsilon}}, \qquad y=\gamma\hat{x}+\beta. \]
其中:
- \(\mu_{\text{batch}}\) 和 \(\sigma_{\text{batch}}^2\) 是当前 batch 的均值和方差;
- \(\epsilon\) 用于防止除以 0;
- \(\gamma\) 和 \(\beta\) 是可学习参数,让模型可以重新调整激活值的范围。
BatchNorm 的主要作用是让训练更加稳定,通常允许使用更大的学习率,并降低模型对参数初始化的敏感程度。由于每个 batch 的统计量存在随机变化,它也会带来一些类似正则化的噪声,但这种效果只是副作用。
BatchNorm 为什么有助于训练?
如果不同层的激活值尺度差异很大,某些方向的梯度可能特别大,另一些方向则特别小,模型会更难优化。
BatchNorm 将激活值调整到相对稳定的范围,因此通常可以:
- 减少不同层之间的数值尺度差异;
- 让梯度更新更加稳定;
- 使用更大的 Learning Rate;
- 加快模型收敛;
- 降低对参数初始化的敏感程度。
因此,BatchNorm 首先是一种优化方法,而不是专门的防止 Overfitting 方法。
BatchNorm 是否属于正则化?
BatchNorm 不属于 L1、L2 这种显式正则化,但可能产生隐式正则效果。
训练时,每个样本的标准化结果依赖当前 batch 中的其他样本。由于 mini-batch 是随机采样的,不同 batch 的均值和方差会有所不同,相当于给激活值加入了一些随机扰动。
这种扰动可能让模型减少对训练样本细节的依赖,因此有时能够缓解 Overfitting。
BatchNorm 的主要作用是改善优化,正则化只是由 batch 统计噪声带来的附带效果。不能因为模型使用了 BatchNorm,就默认不再需要 Weight Decay、数据增强或其他正则化方法。
BatchNorm 与 LayerNorm
两者的核心区别是:
BatchNorm 在不同样本之间统计同一特征;LayerNorm 在单个样本内部统计不同特征。
| 对比 | BatchNorm | LayerNorm |
|---|---|---|
| 统计范围 | 同一 batch 中的同一特征或通道 | 单个样本或 token 内的所有隐藏特征 |
| 是否依赖 Batch Size | 是 | 否 |
| 训练与推理 | 使用不同统计量 | 计算方式一致 |
| 是否需要 Running Statistics | 需要 | 不需要 |
| 小 Batch 下的表现 | 可能不稳定 | 通常不受影响 |
| 常见场景 | CNN、计算机视觉 | Transformer、LLM、NLP |
| 隐式正则效果 | 可能有 | 通常较弱 |
假设一个 batch 可以表示为一个矩阵,其中行是样本、列是特征:
- BatchNorm 可以理解为沿列计算:比较不同样本的同一个特征。
- LayerNorm 可以理解为沿行计算:比较一个样本内部的不同特征。
对于 CNN,BatchNorm 通常还会同时使用空间位置上的数据,为每个通道计算统计量。
为什么 Transformer 通常使用 LayerNorm?
Transformer 的输入通常表示为:
\[ [\text{batch},\ \text{sequence length},\ \text{hidden dimension}]. \]
LayerNorm 对每个 token 的 Hidden Dimension 独立进行归一化,因此:
- 不依赖 Batch Size;
- 不需要不同样本具有相同的序列长度;
- 训练和自回归推理的计算方式一致;
- 即使一次只生成一个 token,也可以正常工作。
BatchNorm 则依赖 batch 中其他样本的统计量。在小 batch、变长序列和自回归生成场景中,这些统计量不够方便和稳定。
因此:
- CNN 通常更常使用 BatchNorm;
- Transformer 和 LLM 通常更常使用 LayerNorm 或 RMSNorm。
Batch Size 如何影响 BatchNorm?
Batch Size 越小,用于估计均值和方差的样本越少:
- 统计量的随机性更大;
- 可能带来更强的隐式正则效果;
- 但统计量也可能不准确,导致训练不稳定。
Batch Size 越大:
- Batch Statistics 更稳定;
- BatchNorm 的随机扰动更小;
- 附带的正则效果可能减弱。
如果由于显存限制只能使用很小的 batch,可以考虑:
- LayerNorm;
- GroupNorm;
- 在多张 GPU 之间同步统计量的 SyncBatchNorm。
3. Batch Size、梯度下降与 Optimizer
模型训练的目标是找到一组参数,使整个训练集上的平均 Loss 最小。不同梯度下降方法的区别是:
每次更新参数时,使用多少训练样本来估计梯度。
三种梯度下降
Batch Gradient Descent
每一步都使用完整训练集计算梯度。
优点:
- 梯度准确、稳定;
- 相同参数下每次得到的梯度相同。
缺点:
- 数据量大时计算很慢;
- 每次更新都需要遍历完整数据集;
- 很难充分利用频繁更新带来的优化速度。
Stochastic Gradient Descent
每一步只使用一个样本计算梯度。
优点:
- 参数更新频繁;
- 单次更新计算量小;
- 梯度噪声可能带来一定隐式正则效果。
缺点:
- 梯度波动很大;
- 通常需要更小的学习率;
- 难以充分发挥 GPU 的并行计算能力。
Mini-batch Gradient Descent
每一步使用一小批样本计算梯度,是深度学习中最常见的方法。
它在两方面取得平衡:
- 比单样本 SGD 的梯度更稳定;
- 比完整 Batch GD 的更新更频繁、计算成本更低;
- 可以利用 GPU 对矩阵运算进行并行加速。
实际深度学习中常说的“SGD 训练”,通常使用的也是 Mini-batch SGD,而不是每次只使用一个样本。
Batch Size 会影响什么?
Batch Size 不只是决定一次放多少数据,还会同时影响:
- 梯度噪声:Batch 越小,梯度估计的随机性通常越大。
- 更新次数:在相同 Epoch 数下,Batch 越大,每个 Epoch 的参数更新次数越少。
- 显存使用:Batch 越大,通常需要更多显存。
- 硬件吞吐:在一定范围内,Batch 越大越容易发挥 GPU 并行能力。
- 泛化能力:适量的梯度噪声有时可以减少 Overfitting。
- BatchNorm 统计量:Batch 越小,BatchNorm 的均值和方差越不稳定。
因此,比较不同 Batch Size 时,不能只比较相同的 Epoch 数。因为 Batch Size 改变后,参数更新次数也发生了变化。
Learning Rate 为什么要和 Batch Size 一起调整?
Batch Size 增大后,梯度通常更稳定,但每个 Epoch 的更新次数更少。如果仍然使用原来的 Learning Rate,模型的训练行为会发生明显变化。
常见经验是线性缩放:
\[ B_{\text{new}}=kB_{\text{old}} \quad\Rightarrow\quad \eta_{\text{new}}\approx k\eta_{\text{old}}. \]
也就是 Batch Size 扩大 \(k\) 倍时,Learning Rate 也尝试扩大 \(k\) 倍。
但这只是起始调参规则,不是严格定律。Learning Rate 过大时,训练初期可能不稳定,因此大 Batch 训练通常还会配合 Learning Rate Warmup。
可以把 Batch Size 和 Learning Rate 的关系粗略理解为:
\[ \text{Gradient Noise Scale} \propto \frac{\text{Learning Rate}}{\text{Batch Size}}. \]
因此,改变 Batch Size 时,通常也需要重新调整 Learning Rate 和训练步数。
Batch Size 等于 1 是否泛化最好?
不一定。梯度噪声只有在模型能够正常收敛的情况下,才可能产生有益的正则效果。
Batch Size 等于 1 时:
- 梯度的随机性很大;
- 训练可能需要较小的 Learning Rate;
- GPU 并行利用率很低;
- 训练时间通常更长;
- 普通 BatchNorm 的统计量可能不可靠。
但 Batch Size 等于 1 并不是一定无法训练。在线学习、强化学习或某些小模型中也会使用单样本更新。
正确结论是:
Batch Size 不是越小越好,而是在优化稳定性、泛化能力、显存和训练速度之间进行权衡。
不存在适用于所有任务的最佳 Batch Size。通常先根据显存和硬件效率选择可行范围,再通过验证集比较不同设置。
Gradient Accumulation 是什么?
当显存无法容纳较大的 batch 时,可以连续计算多个小 batch 的梯度,累积后再更新一次参数。
如果:
- 每张 GPU 的 Micro-batch Size 为 \(B\);
- 使用 \(N\) 张 GPU;
- 累积 \(K\) 步后更新参数;
那么 Effective Batch Size 为:
\[ B_{\text{effective}} = B\times N\times K. \]
Gradient Accumulation 可以在较少显存下模拟大 Batch 的梯度更新,但需要注意:
- 梯度噪声主要由 Effective Batch Size 决定;
- BatchNorm 统计量通常只根据当前 Micro-batch 计算;
- 因此,Gradient Accumulation 不能让 BatchNorm 获得更大的统计 batch。
这也是小 Micro-batch 场景中 LayerNorm 和 GroupNorm 更稳定的原因之一。
SGD、Momentum、Adam 和 AdamW
Batch Size 决定“使用多少样本估计梯度”,Optimizer 决定“如何使用这个梯度更新参数”。它们是两个不同的选择。
| Optimizer | 核心特点 | 优点 | 缺点 | 常见场景 |
|---|---|---|---|---|
| SGD | 直接沿梯度方向更新 | 简单、内存开销小 | 对 Learning Rate 敏感,收敛可能较慢 | 简单模型 |
| SGD + Momentum | 累积过去的更新方向 | 减少震荡、加快收敛 | 通常需要仔细调整 Learning Rate | CNN、视觉任务 |
| Adam | 为不同参数调整更新大小 | 前期收敛快,适合稀疏或不均匀梯度 | Weight Decay 处理不够直接 | NLP、复杂模型 |
| AdamW | Adam 加上独立的 Weight Decay | 收敛快,正则化更容易控制 | 内存开销通常高于 SGD | Transformer、LLM、现代深度网络 |
Momentum 可以理解为给梯度更新加入“惯性”:
- 如果多个步骤的梯度方向一致,就加速前进;
- 如果梯度方向反复变化,就减少来回震荡。
Adam 除了使用 Momentum,还会根据每个参数过去的梯度大小,分别调整其更新步长。
AdamW 则进一步将 Weight Decay 从梯度计算中分离,使它更符合“每次更新时直接缩小权重”的定义。
应该如何选择?
没有一个 Optimizer 在所有任务上都最好,可以按照以下经验开始:
- Transformer、LLM 和大多数 NLP 任务:通常从 AdamW 开始。
- CNN 或传统视觉任务:可以比较 SGD + Momentum 和 AdamW。
- 梯度非常稀疏或不同参数尺度差异较大:Adam/AdamW 通常更容易训练。
- 追求更高泛化性能时:应该在相同计算预算下实际比较,而不能默认 SGD 一定优于 AdamW。
选择 Batch Size 和 Optimizer 后,还需要一起调整:
- Learning Rate;
- Learning Rate Schedule;
- Warmup;
- Weight Decay;
- 训练步数。
这些参数共同决定模型能否收敛以及最终的泛化能力,不能完全独立调节。
5. 反向传播
神经网络的一次训练包含四个步骤:
- Forward Pass:根据输入和当前参数计算预测。
- Loss:比较预测和真实标签。
- Backpropagation:计算每个参数对 Loss 的影响。
- Optimizer Step:使用梯度更新参数。
因此,反向传播负责回答:
如果某个参数发生一点变化,最终的 Loss 会变化多少?
30 秒回答
反向传播是链式法则在计算图上的高效实现。
假设:
\[ z=f(x),\qquad y=g(z),\qquad \mathcal L=h(y), \]
那么:
\[ \frac{\partial\mathcal L}{\partial x} = \frac{\partial\mathcal L}{\partial y} \frac{\partial y}{\partial z} \frac{\partial z}{\partial x}. \]
前向传播按照 \(x\rightarrow z\rightarrow y\rightarrow\mathcal L\) 计算结果;反向传播则从 Loss 开始,沿相反方向计算梯度。
反向传播为什么高效?
如果分别计算每个参数对 Loss 的影响,会重复进行大量相同计算。
反向传播会先计算后面节点的梯度,然后将结果重复利用。例如,已经得到:
\[ \frac{\partial\mathcal L}{\partial y}, \]
就可以继续计算:
\[ \frac{\partial\mathcal L}{\partial z} = \frac{\partial\mathcal L}{\partial y} \frac{\partial y}{\partial z}. \]
现代自动微分框架通常不会显式构造完整的 Jacobian,而是直接计算当前梯度与局部 Jacobian 的乘积,即 Vector-Jacobian Product。
这样可以用接近一次前向传播的计算量,得到所有参数的梯度。
一条变量经过多条路径时怎么办?
如果变量 \(x\) 通过两条路径影响 Loss:
\[ x\rightarrow a\rightarrow\mathcal L, \qquad x\rightarrow b\rightarrow\mathcal L, \]
那么两条路径的贡献需要相加:
\[ \frac{\partial\mathcal L}{\partial x} = \frac{\partial\mathcal L}{\partial a} \frac{\partial a}{\partial x} + \frac{\partial\mathcal L}{\partial b} \frac{\partial b}{\partial x}. \]
这也是残差连接和多分支网络在反向传播时梯度会自动累加的原因。
反向传播与梯度下降的区别
- Backpropagation:计算梯度。
- Optimizer:使用梯度更新参数。
- Learning Rate:控制每次更新的大小。
以 SGD 为例:
\[ w \leftarrow w-\eta\frac{\partial\mathcal L}{\partial w}. \]
其中,\(\partial\mathcal L/\partial w\) 由反向传播得到,SGD 负责使用它更新 \(w\)。
因此:
Backpropagation 告诉模型应该往哪个方向改,Optimizer 决定具体怎么改。
常见工程问题
- 忘记清零梯度:很多框架默认累积梯度,多次 Backward 前没有清零会导致意外累加。
- 错误使用
detach:detach会切断计算图,使前面的参数无法收到梯度。 - 原地操作:直接修改反向传播需要的中间结果,可能破坏计算图。
- 数值溢出或下溢:混合精度训练中,梯度可能变成
inf、NaN或 0。 - 广播错误:Tensor Shape 可以成功广播,但计算的含义可能不正确。
- 未进入训练模式:Dropout 和 BatchNorm 在 Train Mode 与 Evaluation Mode 下行为不同。
面试回答
反向传播是链式法则在计算图上的高效实现。它从 Loss 开始,按照计算图的反方向传播梯度,并将一条变量经过不同路径产生的梯度相加。反向传播只负责计算梯度,SGD、AdamW 等 Optimizer 才负责使用梯度更新参数。
6. 梯度消失与梯度爆炸
梯度消失和梯度爆炸都来自反向传播中的连续乘法。
对于一个深层网络,前面层的梯度可以写成多个局部导数的乘积:
\[ \frac{\partial\mathcal L}{\partial h_l} = \frac{\partial\mathcal L}{\partial h_L} \prod_{k=l}^{L-1} \frac{\partial h_{k+1}}{\partial h_k}. \]
如果这些导数大多小于 1,连乘后梯度会越来越小;如果大多大于 1,梯度就可能快速增大。
梯度消失
梯度消失表示靠近输入的层收到的梯度非常小,参数几乎无法更新。
常见表现:
- 前面层的 Gradient Norm 接近 0;
- 后面层还在学习,但前面层基本不变;
- Train Loss 很早进入 Plateau;
- 增加训练时间也没有明显改善。
常见原因:
- 网络过深;
- Sigmoid 或 Tanh 长期进入饱和区;
- RNN 处理很长的序列;
- 参数初始化不合适;
- 多层变换的局部导数持续小于 1。
常见处理:
- 使用 ReLU、GELU、SiLU 等激活函数;
- 使用 Residual Connection;
- 使用合适的参数初始化,如 Xavier 或 He Initialization;
- 使用 LayerNorm 或 BatchNorm 稳定激活值;
- RNN 中使用 LSTM、GRU 或 Attention;
- 增加中间监督,缩短 Loss 到前面层的传播路径。
为什么残差连接有帮助?
普通网络为:
\[ h_{l+1}=F(h_l). \]
残差网络为:
\[ h_{l+1}=h_l+F(h_l). \]
反向传播时:
\[ \frac{\partial h_{l+1}}{\partial h_l} = I+\frac{\partial F(h_l)}{\partial h_l}. \]
其中的 \(I\) 提供了一条直接传播梯度的路径。即使 \(F\) 内部的梯度比较小,梯度仍然可以通过残差连接向前传播。
这也是深层 ResNet 和 Transformer 普遍使用 Residual Connection 的重要原因。
梯度爆炸
梯度爆炸表示梯度在反向传播过程中快速增大,导致参数更新过大。
常见表现:
- Gradient Norm 突然变得非常大;
- Loss 剧烈震荡或突然升高;
- 参数或 Loss 出现
inf、NaN; - 混合精度训练频繁出现 Overflow。
常见原因:
- Learning Rate 过大;
- 参数初始化尺度过大;
- RNN 长序列中的重复矩阵乘法;
- 输入或标签中存在异常值;
- Loss 计算数值不稳定。
如何处理梯度爆炸?
最常用的方法是 Gradient Clipping。按 Global Norm 裁剪时:
\[ g \leftarrow g\cdot \min\left( 1,\frac{\tau}{\|g\|_2} \right), \]
其中:
- \(g\) 是所有参数的梯度;
- \(\|g\|_2\) 是整体 Gradient Norm;
- \(\tau\) 是允许的最大 Gradient Norm。
如果梯度没有超过 \(\tau\),它保持不变;如果超过,就按比例缩小。
其他处理方法包括:
- 降低 Learning Rate;
- 使用合适的参数初始化;
- 使用 LayerNorm 或 BatchNorm;
- 检查异常输入、标签和 Loss;
- 混合精度训练中使用 Loss Scaling。
需要注意:
Gradient Clipping 可以限制过大的梯度,但不能恢复已经消失的梯度。
面试回答
梯度消失和梯度爆炸都来自反向传播中的多层导数连乘。导数长期小于 1 时梯度会消失,长期大于 1 时梯度会爆炸。梯度消失通常通过非饱和激活、残差连接、归一化和合理初始化处理;梯度爆炸通常通过 Gradient Clipping、降低学习率和稳定数值范围处理。
7. 激活函数
神经网络的一层通常写为:
\[ h=\phi(Wx+b), \]
其中,\(Wx+b\) 是线性变换,\(\phi\) 是激活函数。
为什么需要非线性激活?
如果没有激活函数,多层线性变换仍然可以合并成一个线性变换:
\[ W_2(W_1x+b_1)+b_2 = W'x+b'. \]
无论叠加多少层,模型仍然只能学习线性关系。
激活函数引入非线性,使多层网络能够学习曲线、复杂决策边界和层次化特征。
常见激活函数
| 激活函数 | 输出范围 | 优点 | 主要问题 | 常见位置 |
|---|---|---|---|---|
| Sigmoid | \((0,1)\) | 可以表示概率 | 两端饱和、梯度消失、非零中心 | 二分类输出层、门控结构 |
| Tanh | \((-1,1)\) | 零中心 | 两端仍会饱和 | 传统 RNN、门控结构 |
| ReLU | \([0,\infty)\) | 简单、计算快、正区间梯度稳定 | 可能出现 Dying ReLU | CNN、MLP |
| Leaky ReLU | \((-\infty,\infty)\) | 负区间仍保留小梯度 | 负区间斜率需要设置 | CNN、MLP |
| GELU | \((-\infty,\infty)\) | 平滑地控制输入通过程度 | 计算比 ReLU 复杂 | BERT、部分 Transformer |
| SiLU | \((-\infty,\infty)\) | 平滑且负区间保留梯度 | 计算比 ReLU 复杂 | 现代视觉与语言模型 |
Sigmoid
\[ \sigma(x)=\frac{1}{1+e^{-x}}. \]
Sigmoid 将输入映射到 0 到 1,适合表示二分类概率。
它的导数为:
\[ \sigma'(x)=\sigma(x)(1-\sigma(x)). \]
当 \(x\) 很大或很小时,导数接近 0。因此,在深层网络中重复使用 Sigmoid 容易造成梯度消失。
Tanh
\[ \tanh(x) = \frac{e^x-e^{-x}}{e^x+e^{-x}}. \]
Tanh 的输出范围是 \(-1\) 到 \(1\),并且以 0 为中心。相比 Sigmoid,它通常更适合表示隐藏状态,但输入绝对值较大时仍然会进入饱和区。
ReLU
\[ \operatorname{ReLU}(x)=\max(0,x). \]
当 \(x>0\) 时,ReLU 的梯度为 1,因此可以缓解正区间中的梯度消失。
当 \(x<0\) 时,输出和梯度都为 0。这可能导致某些神经元长期无法更新,即 Dying ReLU。
GELU、SiLU 与 SwiGLU
GELU:
\[ \operatorname{GELU}(x)=x\Phi(x), \]
其中 \(\Phi(x)\) 是标准正态分布的累积分布函数。它会根据输入大小平滑地保留或减弱输入。
SiLU:
\[ \operatorname{SiLU}(x)=x\sigma(x). \]
SiLU 在负区间仍保留一定梯度,并且整体更加平滑。
SwiGLU 不只是一个单独的激活函数,而是一种带门控的 FFN 结构,可以简化表示为:
\[ \operatorname{SwiGLU}(x) = \operatorname{SiLU}(xW_g)\odot(xW_v). \]
其中一个分支生成内容,另一个分支决定多少内容可以通过。许多现代 Transformer 使用 SwiGLU 作为 FFN 的核心结构。
不能简单说某个激活函数在所有任务上都最好。实际效果还取决于模型结构、参数量和计算预算。
Dying ReLU 是什么?
如果某个 ReLU 神经元的输入长期小于 0:
\[ Wx+b<0, \]
它的输出和梯度都会一直为 0,参数无法继续更新。
常见原因:
- Learning Rate 太大,一次更新将神经元推入负区间;
- 参数初始化不合适;
- 输入分布发生明显偏移。
常见处理:
- 降低 Learning Rate;
- 使用 He Initialization;
- 使用 Leaky ReLU、GELU 或 SiLU;
- 使用合适的 Normalization;
- 使用 Residual Connection。
输出层如何选择激活函数?
- 回归任务:通常使用线性输出,不加激活。
- 二分类:通常使用一个 Logit,并通过 Sigmoid 得到概率。
- 多分类且类别互斥:使用 Softmax。
- 多标签分类:每个标签独立使用 Sigmoid。
面试回答
激活函数为神经网络引入非线性。如果没有激活函数,多层线性变换仍然等价于一层线性模型。Sigmoid 和 Tanh 可能在饱和区造成梯度消失;ReLU 简单高效,但可能出现 Dying ReLU;GELU、SiLU 和 SwiGLU 更平滑,因此常用于现代 Transformer。
8. Softmax 及其反向传播
神经网络的最后一层通常输出一组没有范围限制的数值,称为 Logits:
\[ z=[z_1,z_2,\ldots,z_C]. \]
Softmax 将这些 Logits 转换为一个概率分布:
\[ s_i = \frac{e^{z_i}} {\sum_j e^{z_j}}, \qquad \sum_i s_i=1. \]
因此,Softmax 适合“多个类别中只能选择一个”的多分类任务。
Softmax 与 Sigmoid 的区别
- Softmax:所有类别相互竞争,概率之和为 1,适合单标签多分类。
- Sigmoid:每个类别独立判断,适合二分类或多标签分类。
例如,一张图片只能属于猫、狗、鸟中的一个类别时使用 Softmax;一篇文章可以同时属于 AI、Security 和 Software Engineering 时,使用多个 Sigmoid。
为什么要减去最大 Logit?
直接计算 \(e^{z_i}\) 时,如果 \(z_i\) 很大,可能发生数值溢出。
稳定实现为:
\[ s_i = \frac{e^{z_i-\max(z)}} {\sum_j e^{z_j-\max(z)}}. \]
Softmax 对所有 Logits 同时加减同一个常数不会改变结果:
\[ \operatorname{softmax}(z) = \operatorname{softmax}(z+c). \]
因此,减去最大值不会改变概率,但可以保证最大的指数项为 \(e^0=1\),避免 Overflow。
Softmax 的 Jacobian
Softmax 中每个输出都依赖所有 Logits:
\[ \frac{\partial s_i}{\partial z_j} = s_i(\delta_{ij}-s_j). \]
其中:
- 当 \(i=j\) 时:
\[ \frac{\partial s_i}{\partial z_i} = s_i(1-s_i); \]
- 当 \(i\neq j\) 时:
\[ \frac{\partial s_i}{\partial z_j} = -s_is_j. \]
矩阵形式为:
\[ J = \operatorname{diag}(s)-ss^\top. \]
Softmax 与 Cross-Entropy
对于 One-Hot 标签 \(y\),Cross-Entropy Loss 为:
\[ \mathcal L = -\sum_i y_i\log s_i. \]
将 Softmax 和 Cross-Entropy 一起求导后,可以化简为:
\[ \frac{\partial\mathcal L}{\partial z} = s-y. \]
这个结果非常直观:
- 对正确类别,梯度为 \(s_i-1\),会推动其 Logit 增大;
- 对错误类别,梯度为 \(s_i\),会推动其 Logit 减小。
实际实现中不需要显式构造 \(C\times C\) 的 Jacobian。深度学习框架通常会将 LogSoftmax 和 Cross-Entropy 合并计算,以提高效率和数值稳定性。
面试回答
Softmax 将一组 Logits 转换为和为 1 的概率分布,适合类别互斥的多分类任务。数值稳定实现会先减去最大 Logit。Softmax 的 Jacobian 是 \(\operatorname{diag}(s)-ss^\top\),与 Cross-Entropy 结合后,对 Logits 的梯度可以直接化简为 \(s-y\)。
9. Dropout
L1、L2 通过限制参数减少 Overfitting;Dropout 则通过在训练过程中随机删除部分激活值,降低模型对固定特征组合的依赖。
因此,Dropout 是一种带随机性的正则化方法。
30 秒回答
训练时,Dropout 随机将一部分神经元输出设为 0:
\[ \tilde h_i = \frac{m_i h_i}{1-p}, \qquad m_i\sim\operatorname{Bernoulli}(1-p), \]
其中:
- \(p\) 是 Dropout Rate,即删除概率;
- \(m_i=0\) 表示该激活被删除;
- \(m_i=1\) 表示该激活被保留。
推理时关闭 Dropout,所有神经元都正常工作。
Dropout 为什么能够减少 Overfitting?
如果没有 Dropout,模型可能形成一条固定的预测路径,并过度依赖少数神经元。
加入 Dropout 后,任何神经元都可能在当前 Forward Pass 中被删除,因此模型必须让不同特征共同承担预测任务。
它主要产生三种效果:
- 减少固定依赖:模型不能只依赖少数神经元。
- 注入训练噪声:每次 Forward Pass 使用不同的激活组合。
- 近似模型集成:训练过程可以看作在共享参数的多个子网络上训练。
从 Bias-Variance 的角度看,Dropout 通常通过增加训练难度来降低 Variance,但 Dropout Rate 过大也会增加 Bias,导致 Underfitting。
为什么训练时要除以 \(1-p\)?
Dropout 后,只剩下大约 \(1-p\) 比例的激活值。如果不进行缩放,整层输出的平均大小会下降。
使用 Inverted Dropout:
\[ \tilde h_i = \frac{m_i h_i}{1-p}, \]
其期望为:
\[ \mathbb E[\tilde h_i] = \frac{\mathbb E[m_i]h_i}{1-p} = h_i. \]
因此,训练时和推理时的输出期望保持一致。
现代框架通常使用 Inverted Dropout:
- 训练时:随机置 0,并将保留值除以 \(1-p\);
- 推理时:直接关闭 Dropout,不需要额外缩放。
Dropout 应该什么时候使用?
Dropout 更适合以下情况:
- Train Loss 很低,但 Validation Loss 明显更高;
- 模型较大,但训练数据有限;
- 增加数据比较困难;
- 全连接层或 Transformer 出现明显 Overfitting。
如果模型已经 Underfitting,通常不应该继续增加 Dropout。
现代大模型拥有大量训练数据,并且还会使用 Weight Decay、数据清洗和其他正则化,因此不一定需要很高的 Dropout Rate。
Dropout 与 BatchNorm
Dropout 会随机改变激活值分布,而 BatchNorm 需要根据当前 batch 估计激活值的均值和方差。
如果 Dropout 放在 BatchNorm 前面,它产生的随机变化可能影响 BatchNorm 的统计量。因此,两者同时使用时需要注意顺序和实际验证结果。
常见做法是先进行 BatchNorm,再使用激活函数和 Dropout,但具体顺序仍取决于网络结构。
常见工程问题
- 推理前没有切换到 Evaluation Mode,导致输出仍然随机;
- Dropout Rate 太大,模型出现 Underfitting;
- 验证时错误地保持 Dropout 开启;
- 误以为 Dropout 会减少参数量或推理成本。
Dropout 只是暂时屏蔽激活值,不会真正删除模型参数,因此通常不会减少模型大小或常规推理成本。
面试回答
Dropout 是一种随机正则化方法。训练时,它随机将部分激活值设为 0,减少模型对少数神经元和固定特征组合的依赖;推理时关闭 Dropout。现代框架使用 Inverted Dropout,在训练时将保留的激活除以 \(1-p\),使训练和推理阶段的输出期望保持一致。
10. Perceptron
Perceptron 可以看作最简单的人工神经元:
- 对输入进行线性加权;
- 加上 Bias;
- 使用阶跃函数输出分类结果。
它是理解线性分类器和神经网络基本结构的起点。
前向计算
对于输入 \(x\),Perceptron 首先计算:
\[ z=w^\top x+b. \]
然后根据 \(z\) 的正负进行分类:
\[ \hat y = \operatorname{sign}(w^\top x+b). \]
它的决策边界为:
\[ w^\top x+b=0, \]
这是一条直线、一个平面或更高维空间中的超平面。因此,Perceptron 本质上是线性分类器。
Perceptron 如何学习?
假设标签为:
\[ y\in\{-1,+1\}. \]
当样本被正确分类时:
\[ y(w^\top x+b)>0, \]
不需要更新参数。
当样本被错误分类时:
\[ y(w^\top x+b)\leq 0, \]
按照标签方向更新:
\[ w \leftarrow w+\eta yx, \]
\[ b \leftarrow b+\eta y. \]
这个更新会让当前样本下一次更可能被正确分类。
如果训练数据线性可分,Perceptron Learning Algorithm 可以在有限次更新后找到一个能够正确分类训练数据的超平面。如果数据不是线性可分的,它可能持续更新而无法收敛。
Perceptron 与神经网络有什么关系?
Perceptron 已经包含神经网络单元的基本结构:
\[ \text{output} = \text{activation}(w^\top x+b). \]
但传统 Perceptron 使用不可微的阶跃函数,因此不能直接使用现代反向传播训练多层网络。
现代神经网络将阶跃函数替换为 ReLU、Sigmoid、GELU 等可训练的激活函数,并将多个神经元和多层结构组合起来。
Perceptron、Logistic Regression 与神经网络
| 模型 | 变换 | 输出 | 决策边界 |
|---|---|---|---|
| Perceptron | \(w^\top x+b\) | 硬分类结果 | 线性 |
| Logistic Regression | \(\sigma(w^\top x+b)\) | 分类概率 | 线性 |
| 多层神经网络 | 多层线性变换和非线性激活 | 概率或数值 | 可以是非线性 |
Logistic Regression 虽然使用了 Sigmoid,但其决策边界仍然由 \(w^\top x+b=0\) 决定,因此仍然是线性分类器。
多层神经网络通过隐藏层学习新的特征表示,才能形成复杂的非线性决策边界。
Perceptron 为什么不能解决 XOR?
XOR 的输入与输出为:
| \(x_1\) | \(x_2\) | \(y\) |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
正类位于两个对角位置,负类位于另外两个对角位置。无法用一条直线将两类完全分开,因此 XOR 不是线性可分问题。
单层 Perceptron 只能学习线性决策边界,所以不能解决 XOR。
解决方法包括:
- 人工加入非线性特征,例如 \(x_1x_2\);
- 使用 Kernel Method;
- 使用带隐藏层和非线性激活函数的多层神经网络。
面试回答
Perceptron 是一个线性二分类模型,先计算 \(w^\top x+b\),再通过阶跃函数输出类别。样本被错分时,它按照标签方向更新权重。如果数据线性可分,Perceptron 可以收敛;如果数据不是线性可分的,它可能无法收敛。XOR 不是线性可分问题,所以单层 Perceptron 无法解决,需要非线性特征或多层神经网络。
目录
| 章节 |
|---|
| 00. 机器学习核心概念 |
| 01. 基础与神经网络机制 |
| 02. 模型评估与指标 |
| 04. 经典机器学习 |
| 05. NLP、RNN 与词向量 |
| 06. LLM 基础 |
| 07. 训练与系统 |
| 08. 对齐与 RLHF |
| 09. 推理与部署 |
| 12. ML Coding |
| 参考资料 |