RNN中的时间反向传播(BPTT)过程涉及多个时间步的梯度相乘。当这些梯度持续大于1.0时,它们的乘积会呈指数级增长,导致梯度爆炸问题。这会导致训练期间网络权重的更新过大,引起数值不稳定,并可能使模型发散(损失变为NaN或无穷大)。设想一下尝试下坡,但步子迈得太大以至于完全越过了谷底;梯度爆炸也会引起类似的优化混乱。梯度裁剪是一种直接且有效的方法,专门用于应对这种不稳定情况。它并不能阻止梯度一开始变得很大,但它会在这些大梯度用于更新模型权重之前进行干预。梯度裁剪如何起作用核心思想很简单:对梯度的幅度(范数)施加一个最大限值。在训练期间,在计算批次中所有参数的梯度后,但在通过优化器(如SGD或Adam)应用权重更新之前,我们会检查梯度向量的总体大小。计算全局范数:计算模型中所有可训练参数的整个梯度向量的范数。通常使用L2范数: $$ ||g|| = \sqrt{\sum_{i} g_i^2} $$ $g$ 代表包含所有单独参数梯度 $g_i$ 的向量。这个范数给出了一个单一的标量值,表示当前更新步骤中梯度的总体幅度。与阈值比较:将这个计算出的范数 $||g||$ 与预定义的超参数,即 阈值 $c$ 进行比较。必要时重新缩放:如果 $||g|| \le c$,梯度在可接受范围内,不采取任何操作。原始梯度用于权重更新。如果 $||g|| > c$,梯度被认为过大(“爆炸”)。它们随后被重新缩放,使其范数恰好等于阈值 $c$。这种重新缩放是乘法进行的,保留了梯度向量的方向但减小了其幅度: $$ g \leftarrow \frac{c}{||g||} g $$ 这确保了更新步长被限制,防止优化过程采取过大的步骤。以下图表说明了这一思想在简化的2D梯度空间中。digraph G {rankdir=LR;node [shape=plaintext];splines=false;subgraph cluster_0 {label="梯度空间";bgcolor="#e9ecef";node [shape=point, width=0.01, height=0.01];edge [arrowhead=none, color="#adb5bd"];origin [label="", pos="0,0!"];threshold_circle [label="", shape=circle, style=dashed, color="#495057", width=2.5, height=2.5, pos="0,0!"];g_explode_tip [label="", pos="1.5,1.5!"];g_explode [label="原始梯度(爆炸)", fontsize=10, pos="0.75,1.7!"];origin -> g_explode_tip [arrowhead=vee, color="#f03e3e", penwidth=1.5];g_clip_tip [label="", pos="0.88388,0.88388!"];g_clip [label="裁剪后的梯度", fontsize=10, pos="0.5,0.4!"];origin -> g_clip_tip [arrowhead=vee, color="#1c7ed6", penwidth=1.5];threshold_label [label="阈值 c", fontsize=10, pos="1.35,0!"];}} 如果梯度向量的范数超过阈值 c(落在虚线圆圈之外),它将沿着其原始方向按比例缩小,直到其范数等于 c(位于圆圈边界上)。圆圈内的梯度不受影响。阈值的选择裁剪阈值 $c$ 是一个超参数,通常需要调整。过高:裁剪很少发生,这种方法将无法有效阻止超大梯度引起的不稳定。过低:裁剪会频繁发生,可能减缓收敛速度,因为它会减少训练早期或在处理复杂损失平面时所需的大步幅的幅度。阈值的常见值通常在1.0到5.0之间,但最佳值取决于具体的模型、数据集和损失函数的规模。在训练期间(裁剪前)监测梯度范数可以为选择一个合理的起始点提供参考。许多深度学习框架提供工具用于记录梯度范数。作用与局限梯度裁剪是一种标准且通常必要的方法,在训练RNN时,特别是在涉及可能长序列的任务中训练LSTMs和GRUs(我们稍后会介绍)时。它直接处理梯度爆炸问题,从而使训练更稳定可靠。然而,重要的是要记住,梯度裁剪不能解决梯度消失问题。它只处理梯度变得过大的情况,而不是过小。其他方法,例如使用门控架构(LSTMs/GRUs)或仔细的权重初始化,需要用来解决梯度消失问题并提升长距离依赖关系的学习。梯度裁剪是一个重要的稳定工具,但它不是解决所有RNN训练难题的完整方案。