梯度与反向传播
推导缩放点积注意力中,softmax 函数的雅可比矩阵形式,并说明与反向传播的关系。¶
缩放点积注意力的一般形式为:


画出 Transformer 层的前向计算图,并标出反向传播时的梯度流动。¶
一个标准 Pre-Norm Transformer 解码器层(简化)的前向计算图如下:


-
一路直接通过残差连接向前一阶段的输出传递(无变换,梯度直接复制)。
-
另一路进入 FFN(或注意力)子层,先通过其内部运算(包括可能的 LayerNorm)反向传播,然后与残差支路梯度相加。
具体到注意力子层:
输入 x ─→ LayerNorm → 计算 Q,K,V → S = QK^T/√d → softmax → A → O = AV → 输出Proj → out_attn
残差: x 直接加到 out_attn 形成 y
反向传播时:
-
梯度从 y 流向 out_attn 和 x(残差)。
-
out_attn 的梯度经过输出投影、A、S 等依次回传至 Q,K,V 和 LayerNorm。
-
LayerNorm 的梯度继续回传到 x 的另一份拷贝(实际上 x 是残差分支的起点,两个分支共享同一个 x,梯度需累加)。
整体上,残差连接使得梯度可以无损地直接流向较早的层,缓解梯度消失。
LayerNorm 的反向传播如何推导?¶



代入可得最终的向量化公式:

更精确的常见写法是:

或者保留均值形式:

其中 mean 表示对所有元素取平均。该公式表明 LayerNorm 在反向传播中也会中心化和缩放梯度,有助于稳定训练。
残差连接是如何影响梯度流动的?从反向传播的角度解释它为什么缓解梯度消失。¶
残差连接定义为 y=x+F(x),其中 F 为子层(注意力或 FFN)。反向传播时,根据链式法则:


此外,残差连接也使损失平面更平滑,有助于优化。
在 Pre-Norm 和 Post-Norm 中,初始层的梯度传播路径有何不同?画图说明。¶
Post-Norm(原始 Transformer):子层输出先相加残差,再归一化。

Pre-Norm:输入先归一化,再进入子层,最后残差。

梯度传播路径:
-
Post-Norm:梯度从 y 传入 LayerNorm,再分两路进入 x 和 F(x)。LayerNorm 在残差之后,会缩放整个残差分支的梯度。随着层数加深,LayerNorm 可能放大或缩小梯度,初始层的梯度需穿过许多 LayerNorm,易受缩放累积影响,造成梯度消失或爆炸。
-
Pre-Norm:梯度从 y 分两路:一路直接给 x(无任何变换),另一路进入 F(LayerNorm(x)),需先经过 F 的反向,再经过 LayerNorm 反向后才到达 x。但 x 的主路径完全不经过 LayerNorm,因此初始层能得到较为纯净的直接梯度,训练更稳定。Pre-Norm 近年来成为默认选择。
图示:
Post-Norm: x → [+F(x)] → LN → y
Pre-Norm: x → LN → F → [+x] → y
梯度图中,Post-Norm 的残差支路和子层支路汇合后共同经过 LN;Pre-Norm 的残差支路直接连接,LN 只在于层支路内。
为什么 Transformer 训练时梯度容易在 LayerNorm 附近出现突变?¶
LayerNorm 的前向涉及除以标准差,当某个隐藏单元方差很小或存在异常值时,归一化后的数值可能出现较大的值,进而反向传播时梯度会出现尖峰。此外,初始训练阶段,参数随机,某些维度的激活可能极大或极小,经过 softmax 或其它非线性操作后梯度变化剧烈。LayerNorm 的梯度计算中有减去均值等操作,对异常值敏感,容易导致梯度突变。这通常通过梯度裁剪、更好的初始化、学习率预热等来缓解。
解释一下梯度噪声在 Transformer 训练中的影响,大 batch 与小 batch 的梯度噪声有何不同?¶
梯度噪声指 mini-batch 梯度相对于全数据集梯度的随机波动。适当噪声有助于逃离局部极小和鞍点,起到正则化作用,改善泛化。大 batch 训练时,梯度估计更准确,噪声小,但可能导致收敛到尖锐极小值,泛化能力下降;小 batch 噪声大,优化路径更震荡,但容易找到平坦极小值,泛化往往更好。Transformer 训练中,常需根据 batch size 调整学习率(如平方根缩放、线性缩放),并且大 batch 需要额外的稳定技巧(如 warmup、LayerNorm、更高权重衰减)。噪声过大也可能导致训练不稳定,所以实际中会动态调整 batch size 或使用梯度累积。
如何通过梯度直方图、参数更新幅度等指标监控训练健康度?¶
-
梯度直方图:观察各层梯度的分布。正常训练时,梯度大致呈钟形,均值接近零,标准差稳定。若某层梯度突然消失(全为零)或爆炸(极大尖峰),或分布严重偏斜,说明有问题。
-
参数更新幅度(更新范数/参数范数比):即
||Δw|| / ||w||,通常在 10−3 量级左右。若过大,学习率可能太高;若过小,可能学习率太低或出现梯度消失。 -
损失曲线:训练损失和验证损失平滑下降且最终收敛。若出现震荡或上升,需检查。
-
权重范数:应稳定增长或趋于稳定。突然增大可能梯度爆炸。
-
激活统计:各层激活的均值、方差,帮助发现死区或饱和。
-
学习率变化:如果使用 warmup/decay,监控学习率是否符合预期。
使用 TensorBoard 或 Wandb 记录这些指标,可及时诊断。
梯度裁剪的阈值如何科学设定?过小或过大对训练有什么影响?¶
梯度裁剪通过限制全局梯度范数(如 L2 范数)来防止梯度爆炸。阈值通常通过监控初始训练步的梯度范数分布来设定,选择一个略高于正常梯度范数的值,例如取正常梯度范数的 90 百分位数。经验值如 1.0, 5.0 等也常被使用。阈值过小:频繁裁剪会改变梯度方向,削弱有效更新,导致收敛变慢或最终性能下降。阈值过大:无法有效阻止爆炸,可能仍出现训练崩溃。科学设定需结合模型和数据,进行小范围 grid search,观察损失稳定性和最终困惑度。
在混合精度训练中,梯度缩放(Loss Scaling)如何影响梯度的反向传播?动态缩放算法如何工作?¶
FP16 表示的范围有限,许多小梯度可能下溢为 0,导致参数得不到更新。Loss Scaling 在前向结束后将损失乘以一个大常数 S(如 65536),然后反向传播,这样所有梯度都扩大了 S 倍,小梯度得以保留在 FP16 可表示范围。在更新参数前,再将梯度除以 S,恢复到正确的尺度(但更新时可能仍用 FP32 主权重,只需在应用梯度前 descale)。
动态缩放算法:自动调整 S。开始用较大 S,若在连续 N 次迭代中没有出现 NaN/Inf 梯度,则尝试增大 S(如乘以 2);若出现 NaN/Inf,则跳过本次更新并减小 S(如除以 2)。这样可以在不溢出的前提下最大化缩放因子,尽量减少下溢。
自注意力中,如果 softmax 的输出非常尖锐(接近 one-hot),梯度会有什么问题?¶

请推导 RoPE 在反向传播时的梯度计算,如何高效实现。¶

这相当于用角度 −mθi 旋转输出梯度。高效实现时,可直接调用与正向相同的旋转核,只需将角度取反。由于旋转矩阵是分块对角且每块独立,计算量很小,且可完全并行。
如果使用 ZeRO-2,梯度在反向传播时是如何聚合和更新的?¶
ZeRO-2(优化器状态分区 + 梯度分区)的流程:
-
每个 GPU 持有完整模型参数副本,但仅存储与自己分区对应的优化器状态(Adam 的 m, v)。
-
前向和反向传播各卡独立计算自己 microbatch 的损失和梯度,产生完整梯度(所有参数)。
-
在反向传播结束后,执行 Reduce-Scatter 操作:各卡将自己的梯度分块,对于每个参数分区,归属该分区的卡从所有卡收集该分区梯度并求和(平均),得到该分区的聚合梯度。其他分区梯度被丢弃。
-
然后各卡利用自己的优化器状态分片和聚合梯度,更新自己负责的那部分参数。
-
最后,通过 All-Gather 将更新后的参数分片广播到所有卡,确保下一轮所有卡拥有完整的更新后的参数。
因此,ZeRO-2 通过 reduce-scatter 聚合梯度,分散了优化器状态和更新过程。
在 Pipeline Parallelism 中,梯度是如何反向传递的?1F1B 是如何调度前向和反向的?¶
梯度反向传递:流水线并行将模型不同层分配到不同设备。前向时,micro-batch 依次通过各设备,设备间传递激活。反向时,梯度从最后一个设备开始,按相反顺序传播。对于每个 micro-batch,设备计算自己层的反向,并将输入梯度传回前一个设备。所有 micro-batch 的反向完成后,设备得到自己负责层的平均梯度,然后更新参数。
1F1B(One Forward, One Backward)调度:先执行若干连续的前向 micro-batch(预热阶段),使流水线充满。然后交替执行一个前向和一个反向(1F1B)。这样能尽早开始反向传播,平衡内存中保存的激活数量,降低峰值显存。具体地,假设 4 个设备,micro-batch 数 M=8,预热阶段执行 4 个前向,然后每个设备在完成一个前向后立即执行一个反向。最终完成所有 micro-batch。
解释为什么在微调大模型时,LoRA 等方法的梯度更新只集中在少量参数上,而不会影响其他层。¶
LoRA 在原始权重矩阵旁引入低秩适配器 A⋅B,其中 A,B 维度很小。微调时,原始权重冻结,只优化 A,B。前向时,h=Wx+(BA)x,梯度计算时,由于 W 冻结,损失对 W 的梯度不用于更新,只有对 A,BA,B 的梯度被计算并更新。因此梯度更新仅集中在 LoRA 引入的少量参数上。其他层如果也插入 LoRA,则各自更新各自的适配器参数,不影响原始权重。这种方式大幅减少了可训练参数,降低了通信和存储成本。
当你发现模型训练时某个参数矩阵的梯度一直为零,可能是什么原因?如何排查?¶
可能原因:
-
该矩阵所在层被冻结:如微调时只训练部分层,检查参数
requires_grad设置。 -
学习率为零:优化器中该参数组的学习率设为了 0。
-
梯度流中断:该层之前的计算图断裂,例如使用了
.detach()或 inplace 操作覆盖了需要梯度的变量。 -
激活函数饱和或死区:比如 ReLU 输入全为负,导致梯度为零;或者前面的网络输出恒为常数,使得该层输入无变化。
-
该层没有参与损失计算:例如在多任务中,某些输出头未被使用。
-
混合精度下梯度下溢:小梯度在 FP16 下变为零。
-
条件计算中该 token 未被路由到该专家(MoE)。
排查步骤:
-
检查参数的
requires_grad和优化器参数组设置。 -
在反向传播后打印该参数的
.grad是否为 None 或全零。 -
使用
torch.autograd的register_hook检查梯度流。 -
检查前向输出是否异常(如恒定值),逐层排查数据流。
-
尝试关闭混合精度或增大 loss scale 看梯度是否出现。
-
简化模型或输入,确认是否数据导致。