梯度裁剪
在 backward 后按总范数或元素值限制梯度,缓解梯度爆炸。
学习目标
- 把 (3,4) 梯度裁剪到范数 1
- 裁剪后为什么损失仍可能爆炸?
- 说明:裁剪限制有限梯度的幅度;非有限梯度应定位原因而不是靠阈值隐藏。
为什么需要这个概念
全局范数裁剪把所有参与参数的梯度看成一个向量;范数超过阈值 M 时按同一比例缩小,低于阈值时保持。它与逐元素截断不同。
学习前需要掌握
背景与问题
循环网络或困难批次可能产生很大的梯度,使一次更新破坏已有参数。梯度裁剪限制更新方向的输入幅度,用于控制这种瞬时风险。
概念定义
全局范数裁剪把所有参与参数的梯度看成一个向量;范数超过阈值 M 时按同一比例缩小,低于阈值时保持。它与逐元素截断不同。
直观理解
不是把方向重新指向别处,而是把过长的箭头缩短;各参数之间的相对比例尽量保留。
核心原理
若梯度向量 g 的 L2 范数为 G,裁剪系数约为 min(1,M/G)。clip_grad_norm_ 原地修改梯度,并返回裁剪前总范数。阈值是梯度尺度,和学习率共同影响实际参数变化。
顺序通常是 backward、必要时 AMP unscale、clip、optimizer.step。若在 scaled 梯度上裁剪,阈值会被缩放因子扭曲;若在 backward 前裁剪,则没有本步梯度可处理。
裁剪不能修复 NaN 损失、标签越界或除零。若频繁触发,应检查学习率、数据异常和目标归一化;持续强裁剪可能掩盖更深问题,也可能让有效更新过小。
数学表达
核心公式
全局范数裁剪把所有参与参数的梯度看成一个向量;范数超过阈值 M 时按同一比例缩小,低于阈值时保持。它与逐元素截断不同。
变量含义
g所有参数梯度M最大范数
计算步骤
- 1原范数 sqrt(9+16)=5。
- 2统一乘 1/5,得到约 (0.6,0.8)。
代码对应位置
示例 1把 (3,4) 梯度裁剪到范数 1的最小验证:使用完整构造输入核对把 (3,4) 梯度裁剪到范数 1,输出只表示本例计算。
变量解释
| 变量 | 含义 |
|---|---|
g | 所有参数梯度 |
M | 最大范数 |
完整数值示例
把 (3,4) 梯度裁剪到范数 1
已知条件
- 单个参数的梯度向量为 (3,4)
- 最大范数为 1
计算步骤
- 原范数 sqrt(9+16)=5。
- 统一乘 1/5,得到约 (0.6,0.8)。
计算结果
返回原范数 5;裁剪后范数约为 1,方向保持。
处理前后对比
- 裁剪限制有限梯度的幅度;非有限梯度应定位原因而不是靠阈值隐藏。
把 (3,4) 梯度裁剪到范数 1
如果逐元素把每个值截到 1,会得到 (1,1),方向从 3 比 4 改为 1 比 1。范数裁剪与值裁剪即使使用相同阈值,也不是相同算法。
代码保留框架为数值稳定加入的极小项,因此输出格式化到三位小数,不把微小浮点差异误认成公式错误。
计算与实现步骤
- 原范数 sqrt(9+16)=5。
- 统一乘 1/5,得到约 (0.6,0.8)。
代码实现
示例 1
把 (3,4) 梯度裁剪到范数 1的最小验证
example_01.py用途:使用完整构造输入核对把 (3,4) 梯度裁剪到范数 1,输出只表示本例计算。
import torch
p = torch.nn.Parameter(torch.zeros(2))
p.grad = torch.tensor([3., 4.])
before = torch.nn.utils.clip_grad_norm_([p], 1., error_if_nonfinite=True)
print(f"before={before.item():.3f} after={p.grad.norm().item():.3f}")
print([round(v, 3) for v in p.grad.tolist()])代码解析
解析始终位于完整代码下方,并按实际代码段逐项对应。
输入数据与任务
使用完整构造输入核对把 (3,4) 梯度裁剪到范数 1,输出只表示本例计算。
Step 1 · 1–6 行
导入当前步骤需要的数值计算、预处理、模型或评价工具。依赖集中写在代码开头,便于复现。
import torch
p = torch.nn.Parameter(torch.zeros(2))
p.grad = torch.tensor([3., 4.])
before = torch.nn.utils.clip_grad_norm_([p], 1., error_if_nonfinite=True)
print(f"before={before.item():.3f} after={p.grad.norm().item():.3f}")
print([round(v, 3) for v in p.grad.tolist()])预期输出或运行结果
before=5.000 after=1.000 [0.6, 0.8]
常见错误 · 3 条
- backward 前裁剪
- 掩盖学习率过大
- NaN 后才裁剪
实际应用
- RNN 训练的梯度爆炸控制
- AMP 训练中限制异常更新
常见错误
- backward 前裁剪
- 掩盖学习率过大
- NaN 后才裁剪
常见问题
裁剪后为什么损失仍可能爆炸?
裁剪不改变坏输入或不稳定前向,而且 Adam 等优化器还有内部状态;应检查非有限数值来源及整体更新设置。
这个示例能说明什么边界?
裁剪限制有限梯度的幅度;非有限梯度应定位原因而不是靠阈值隐藏。
推荐学习资料
参考库不会生成虚假资源或无效外部链接。