梯度裁剪模拟器 返回
机器学习

梯度裁剪模拟器

体验深度学习中防止"梯度爆炸"的梯度裁剪技术的工具。改变裁剪阈值和学习率,通过等高线上的轨迹和损失、梯度范数变化图,实时观察范数裁剪、值裁剪、不裁剪这三种方法如何在有"陡崖"的损失地形上下降。

参数设置
裁剪阈值 τ
将梯度的大小限制在此值以内
学习率 η
每一步沿梯度方向前进的距离
学习步数
裁剪方式
控制梯度大小的方法
损失地形
梯度急剧变化的地形类型
计算结果
最终损失 f
最大梯度范数(裁剪前)
裁剪触发次数
是否发散
损失降低率 (%)
裁剪效果评价
损失曲面等高线与优化轨迹

等高线表示损失f的地形,绿点为最小值。从起点出发的彩色路径是更新轨迹,标记沿其移动。在陡崖地形中不裁剪时,轨迹会突然飞出屏幕。

损失变化
梯度范数变化(裁剪前)
理论与主要公式

$$\text{if }\lVert g\rVert\gt \tau:\quad g\leftarrow g\cdot\frac{\tau}{\lVert g\rVert}$$

范数裁剪。当梯度范数‖g‖超过阈值τ时,将整个梯度向量一致地缩小。保留方向(下降方向),仅限制大小为τ。

$$g_i\leftarrow\operatorname{clip}(g_i,\,-\tau,\,+\tau),\qquad \theta\leftarrow\theta-\eta\,g_{\text{clipped}}$$

值裁剪将每个分量g_i独立地切割至±τ。最后用裁剪后的梯度以学习率η更新参数θ。

梯度裁剪概述

🙋
在深度学习中经常看到"梯度裁剪",到底在做什么呢?
🎓
简单地说就是"当梯度变得过大时,强制缩小"。学习是下降损失地形的过程,在当前位置的斜率(梯度g)乘以学习率η后前进一步。但地形中存在"陡崖",在崖边梯度会变得非常大。如果直接用这样的梯度前进,参数会被抛向远处,学习会破裂。这叫"梯度爆炸"。裁剪在更新前对梯度大小设置上限,防止这种飞出去的现象。
🙋
我试过把"裁剪方式"改成"不裁剪",看陡崖地形,轨迹一下子消失了,显示"发散"。这就是梯度爆炸吗?
🎓
正是这样。这个陡崖地形是设计成,从原点稍微离开就两侧的墙用cosh函数一下子变陡峭。墙边的x方向梯度巨大,乘以学习率后的一步会远远超过起点。越过后下一步会在更陡的地方,梯度更大了……这样正反馈会导致损失无限增大发散。这是RNN学习中真实发生的典型失败。
🙋
切换回"范数裁剪"就能安全跨越陡崖,为什么呢?
🎓
范数裁剪是把梯度向量整体缩小为g←g·(τ/‖g‖)。在陡崖梯度范数超过阈值τ的一刻,将向量长度切至τ。关键是"方向不变"。保持下降方向不变,只把步长限制在安全大小。所以不会一步飞过陡崖,而是受控地逐步越过。看下面的"梯度范数变化"图,你能看到原始梯度范数尖峰超过红色阈值线,但实际步长被τ限制住了。
🙋
还有一个"值裁剪"和它有什么区别?
🎓
值裁剪是把梯度的每个分量独立地限制在±τ范围内。比如梯度是(100,0.3),τ=5时就变成(5,0.3)。实现简单,但x分量被切了y分量没被切,合成的向量方向就偏离了原梯度。也就是"下降方向"有了扭曲。而范数裁剪是全部分量按同样比例缩小,方向不变。所以实务中通常先用范数裁剪。值裁剪虽然行为易懂,但有方向扭曲的副作用,这点要记住。

常见问题

梯度裁剪是在每个更新步骤前,将梯度的大小限制在上限以内的技术。深层网络,特别是RNN的损失地形中存在"陡崖"(损失极端急剧变化的区域),在这里梯度会变得巨大。如果直接用巨大的梯度乘以学习率进行一步更新,参数会被抛向远处,学习会崩溃(梯度爆炸)。范数裁剪通过将梯度向量整体缩小至最大长度τ来保留方向,值裁剪则将每个分量切割至±τ。两者都将步长保持有界,使得能够安全地跨越陡崖。
通常推荐使用范数裁剪(clip-by-norm)。它将梯度向量整体缩小为g←g·(τ/‖g‖),保留梯度的方向(下降方向),只限制大小为τ。值裁剪(clip-by-value)将每个分量独立地切割至±τ,实现简单但某个分量被切割时会改变合成向量的方向,使下降方向发生扭曲。在本工具的陡崖地形中,范数裁剪也能更平滑地跨越陡崖。大多数深度学习框架都提供了两种方法。
裁剪阈值τ应该设置为"通过正常时的梯度范数,而只抑制爆炸时的巨大梯度"的值。太小会缩小平常时的梯度,导致学习变慢;太大则无法阻止陡崖处的尖峰,仍然会发散。实际操作中,先学习几百个步骤观察梯度范数的分布,选择其中位数至95百分位附近,或根据经验选择1~10之间的值。本工具中,降低阈值会增加裁剪触发次数,提高阈值则会增加最大梯度范数。
RNN在每个时间步骤重复使用相同的权重矩阵,在误差反向传播时梯度按该矩阵特征值的幂次增减。当特征值大于1时,梯度会随时间序列长度指数级增大,在损失地形中形成陡峭的"陡崖"。在学习长序列时碰到这个陡崖会导致梯度一下子爆炸,学习崩溃。梯度裁剪只会抑制陡崖处的尖峰,对其他步骤无影响,因此在RNN、LSTM、Transformer等序列模型的学习中作为标准方法使用。

实际应用

循环神经网络(RNN/LSTM)的学习:梯度裁剪最初广泛应用于RNN学习。RNN在时间方向重复使用相同权重,长序列反向传播时梯度指数级增大,在损失地形中形成陡崖。机器翻译、语音识别、文本生成等早期RNN/LSTM模型中,不用范数裁剪(典型值τ=1~5)就会在几个epoch后损失变成NaN、学习停止,这是家常便饭。裁剪实际上是必需的稳定化技术。

Transformer与大规模语言模型的学习:现代巨大Transformer也标准配置梯度裁剪。学习初期或罕见的"坏批次"会出现梯度范数急增,不裁剪的话一次尖峰就能毁掉整个学习。GPT系列和BERT系列的训练配置中,"按全局范数裁剪,阈值1.0"几乎是标准值。它是让数十亿参数的学习稳定运行数周的保险。

强化学习与策略梯度法:强化学习的报酬规模不稳定,策略梯度估计值有时会大到离谱。PPO和A2C等算法的实现中,给策略网络和价值网络的梯度加范数裁剪是标配。用来防止噪声大的环境信号破坏学习的,低调但有效的安全装置。

CAE与数值优化的发散对策:机器学习以外,用梯度法最小化目标函数的数值优化也普遍遇到"步长太大发散"的问题。信任域方法和线搜索是控制步长的正式方法,梯度裁剪是"只设上限"的最简便步长限制。对参数规模不均匀的病态目标函数,可作为防止优化破裂的应急手段。

常见误解与注意事项

很常见的误解是"梯度裁剪也能修复梯度消失"。裁剪对付的是梯度变得过大的"梯度爆炸",对梯度变得过小导致学习停滞的"梯度消失"无能为力。更准确地说,裁剪是缩小大梯度的操作,和消失问题无关。梯度消失的对策是LSTM、GRU等门控结构、残差连接(跳过连接)、适当的权重初始化或批正规化等完全不同的手段。裁剪和消失对策的角色不同,要分清楚。

其次,"阈值τ越小越安全"的想法。虽然τ小确实不易发散,但太小会把正常的梯度也一律缩小,实际学习率下降,收敛变慢。极端的话τ接近0时,每步都只能走很小的距离,永远到不了谷底。本工具中降低阈值时会看到裁剪触发次数增加但损失下降变缓。τ要设在"只抑制爆炸、正常梯度直接通过"的恰当位置。

最后,"有了裁剪,学习率怎么大都没事"的误解。裁剪限制梯度为τ,实际步长是η·τ。学习率η设得太大,即使梯度有限步长也会过大,导致越过最小值、振荡甚至发散。本工具中即使启用范数裁剪,学习率拉到上限也会发散。裁剪只是"梯度侧的上限",不能替代学习率调整。

使用指南

  1. 设置裁剪阈值(clipRange:0.1~10.0)。LSTM学习中为防止梯度范数爆炸,典型值为1.0~2.0
  2. 输入学习率(lrRange:0.0001~0.1)。ResNet152学习的标准值为0.001,启用裁剪时可提升至0.01
  3. 设置步数(stepsRange:10~1000),观察梯度范数变化。RNN中100步就会显现梯度爆炸
  4. 运行模拟,对比分析裁剪前后的梯度范数、触发次数、损失降低率

具体计算例

机器翻译模型(Transformer-base,参数数6500万)的训练中,初始梯度范数45.8用阈值1.5裁剪时,范数被抑制至1.5,200步内触发87次。不裁剪时损失发散(出现NaN),裁剪后损失从4.2降至2.1(损失降低率50%),最终精度从BLEU 28.5提升至31.2

实务注意事项

  1. 梯度范数超过1000时,裁剪阈值设为0.5~1.0。自然语言处理的GRU 2层模型最优阈值为0.8
  2. 学习率和裁剪阈值联动。阈值提高10倍时,学习率需增加3~5倍才能维持学习进度
  3. 裁剪触发次数超过全部步数的30%时,考虑修改模型架构(增加残差连接、导入层正规化)
  4. 不同批大小(32、128、512)模拟结果不同,实装时必须用运行批大小重新验证