跳到主内容

什么是梯度裁剪?怎么防止训练炸掉

2026-10-06 发布 来源:我告诉你AI导航 阅读

凌晨两点,训练一个 AI 模型到第 12000 步,loss 从 2.31 跳到 nan,之后全是 nan。换随机种子重跑,炸在相近的位置;把 batch 调小,只是把爆炸往后推了几百步。这类现场大多指向梯度爆炸,而梯度裁剪是工程上...

凌晨两点,训练一个 AI 模型到第 12000 步,loss 从 2.31 跳到 nan,之后全是 nan。换随机种子重跑,炸在相近的位置;把 batch 调小,只是把爆炸往后推了几百步。这类现场大多指向梯度爆炸,而梯度裁剪是工程上最先被加进去的那道保险。它拦的是什么、四种做法差在哪、阈值凭什么定、什么时候加了也没用,下面分开说。

炸掉之前,梯度先长成了什么样子

反向传播按链式法则把各层的梯度连乘起来,深度每加一层、序列每长一段,连乘的次数就多一次。当这些因子的乘积长期大于 1,梯度范数会随步数指数增长,参数一步跨出去很远,loss 随之跳变;再往后数值超出浮点表示范围,变成 inf 或 nan。混合精度训练里这个过程更快,fp16 能表示的数值区间窄,还没到 nan 就已经溢出。

判断是不是这个原因,不需要猜。在训练循环里打印梯度范数,正常时它在某个量级附近波动,爆炸前会出现几个数量级的跃升。看到这种跃升,再回头调梯度下降的步长与更新方向,比盲目重跑有效得多。

四种裁剪,压的位置不一样

名字都叫裁剪,动刀的位置差得很远。下面这张表按实现方式分开,重点看最后一列——失效条件往往比功能更能决定它管不管用:

类型适合谁代价失效条件
按值裁剪个别分量明显异常、只想挡住离群点的人每个分量各截各的,缩放比例不同,梯度方向会被改掉整体偏大但单个分量都不超阈值时,一次都不触发
逐层范数裁剪层间尺度差异大、想给每层单独设限的人几十上百层各配一个阈值,调参和维护成本高层数一多阈值就没人管,最后形同虚设
全局范数裁剪多数 Transformer 与循环网络的默认选择所有层共用一个缩放系数,某层爆掉会连带压低其余层范数长期贴着阈值,等于一直在缩小学习率
分位数自适应阈值梯度分布随训练阶段漂移的长周期训练要维护统计窗口,实现复杂,出问题难定位训练前期统计量还不稳,早期的判断不可靠

全局范数裁剪之所以成为默认,原因在缩放方式:它算出整个梯度向量的长度,超过阈值就整体乘一个小于 1 的系数,方向完全不变,只是把步长缩短。代价是所有层共用一个系数。按值裁剪看着更细,实际每个分量各截各的,方向会被改掉,只在个别异常值特别突出的场景下才划算。

阈值是看出来的,不是拍出来的

常见做法是先不加裁剪跑几百到一千步,记录梯度范数的分布,取略高于常态上限的值:常态在 0.6 附近波动,阈值就取 1.0 上下。这个数没有通用答案,模型规模、层数、batch 大小都会改变它,抄别人配置里的 1.0 之前,先看一眼自己的分布。

定得太低,等于每一步都在缩小步长,训练变慢,收敛到的位置也可能更差;定得太高,绝大多数时候不触发,等于没加。有一个可观测的指标:裁剪触发比例。健康状态下它应该是个位数百分比,长期超过两三成,说明阈值偏低,或者学习率的量级本身就设大了。

它按得住这一步,按不住为什么会炸

裁剪是兜底,不是修复。梯度之所以会爆,常见根因有四类:学习率过大、初始化尺度不当、数据里混进了异常样本、序列或网络过深导致连乘过长。裁剪让训练能继续跑,这四类问题一个都没解决,模型可能在反复被压制的状态下勉强收敛,最终效果比正常训练差一截。

还有两处它管不到。一是已经出现的 nan:nan 乘以任何缩放系数仍然是 nan,裁剪必须在数值溢出之前生效,参数一旦被污染就救不回来。二是梯度消失,反向传播的梯度小到接近零时,裁剪毫无作用,那是另一个方向的问题,通常要靠权重初始化的尺度控制和归一化层来处理。

什么情况下值得默认加上

三类训练基本可以无脑加:序列长度超过几百的循环网络或 Transformer、使用混合精度、loss 曲线出现过偶发尖峰又自行恢复。加它的成本只有几行代码和一次范数计算,收益是训练不会在半夜挂掉。

反过来,几层的小网络、fp32、batch 也不大时,梯度爆炸的概率很低,加与不加差别有限。该做的仍然是先量一遍梯度范数的波动范围,配合损失函数曲线的形态一起判断:平滑下降说明没事,锯齿状尖峰说明该加。

速查:梯度裁剪的四个数

  • 阈值取常态范数的 1.5 倍上下,跑几百步再定
  • 触发比例长期超过三成,先怀疑阈值和学习率
  • 全局范数裁剪不改方向,按值裁剪会改
  • nan 出现之后再裁剪无效,必须在溢出前生效

反馈页面信息

请描述需要更正的地方,页面标题和网址会自动附上。

也可以发邮件至 [email protected]