为什么梯度裁剪的默认模长是1?
By 苏剑林 | 2025-01-02 | 39286位读者 |我们知道,梯度裁剪(Gradient Clipping)是让模型训练更加平稳的常用技巧。常用的梯度裁剪是根据所有参数的梯度总模长来对梯度进行裁剪,其运算可以表示为
clip(g,τ)={g,‖g‖≤ττ‖g‖g,‖g‖>τ
这样一来,clip(g,τ)保持跟g相同的方向,但模长不超过τ。注意这里的‖g‖是整个模型所有的参数梯度放在一起视为单个向量所算的模长,也就是所谓的Global Gradient Norm。
不知道大家有没有留意到一个细节:不管是数百万参数还是数百亿参数的模型,τ的取值在很多时候都是1。这意味着什么呢?是单纯地复用默认值,还是背后隐含着什么深刻的原理呢?
是什么 #
可能有读者觉得,默认值又不一定是最优值,有什么值得纠结的?确实,τ=1未必是最优的选择,但它是很多模型的默认选择,并且在这个默认选择下表现尚可,这反过来表明τ=1具有普遍的合理性。
这里的“合理性”又指什么呢?让我们回到clip运算上。如果‖g‖总是小于τ,那么clip就退化为恒等变换了;如果‖g‖总是大于τ,那么clip就退化成L2归一化。换句话说,clip之所以为clip,就是因为τ产生了适当的区分度,使得大部分的‖g‖都是小于τ的,只有小部分才是大于τ的,这就是τ的合理性的含义。
这个当然可以举出反例,而且还不少,这里主要想强调这个现象的普遍性以及这个默认设置的普适性,所以较真的读者大可不必过于执着于个别细节。
因此,我们认为,τ=1的普遍合理性的含义,就是不论模型参数量多少、怎么初始化、取何种损失函数,它的梯度总模长都能恰好大致以1为“异常值”的分界点,这无疑是一个非常不可思议的性质——笔者第一次意识到这个结论时的感受便是如此。
为什么 #
为什么会如此“巧合”呢?笔者的答案可能会让人有些意外:因为只有这样,模型才有稳定训练的可能。
让我们考虑损失函数L(θ),优化器更新规则为θt+1=θt−ηut,那么损失函数的变化近似为
ΔL=L(θt+1)−L(θt)≈(θt+1−θt)⋅∇θtL(θ)=−ηut⋅gt
先考虑最简单的SGD,那么ut=gt以及ΔL=−η‖gt‖2,即损失函数的变化量正比于梯度模长的平方。我们知道,不管是CV还是NLP,纯粹的SGD(不带动量)都是非常低效的优化器,训练到中后期,平均来说多数任务每步的损失下降量是远不如学习率大小的,也就是|ΔL|<η,由此推得‖gt‖<1。这就表明了‖gt‖<1是一个能正常收敛的模型的长期表现。
当然,训练初期模型有可能会出现‖gt‖>1,这是正常的,但很少情况会出现‖gt‖≫1,或者说一个优秀的初始化应该避免出现‖gt‖≫1,像DeepNorm等的理论依据便是如此。原因是相似的,如果梯度模长太大,那么前期的学习就会过于“激进”,导致提前收敛到不好的局部解。另一个方案是缩小η,这同样能够缩小|ΔL|,这也就是为什么在训练初期我们通常使用Warmup。
顺便说,关于Warmup的理解大家可以参考论文《Optimal Linear Decay Learning Rate Schedules and Further Refinements》,这是笔者认为对Warmup的最合理的分析。
怎么办 #
简单来说,就是由于损失函数的变化量正比于梯度模长的平方,所以训练的平稳性决定了梯度模长不能太大,并且长期表现为小于1。而初期如果出现明显大于1的梯度模长,那么通常的策略是Warmup。或者也可以考虑一个更通用的策略:设置另一个阈值T,根据ut⋅gt的值对η进行裁剪
ηt={η,ut⋅gt≤TTut⋅gtη,ut⋅gt>T
这样就免除了额外的Warmup设置,更加具有自适应性。
对于Adam等优化器,我们可以跟《当Batch Size增大时,学习率该如何随之变化?》一样,通过ut=sign(gt)来进行近似分析,此时
ΔL=−ηsign(gt)⋅gt=−η‖gt‖1
这里的‖‖1是L1范数,即分量的绝对值之和。由于梯度分量基本都小于1,因此‖gt‖1≫‖gt‖,因此同样出于平稳训练的需求,Adam的学习率通常要明显小于SGD的学习率。此外,上式还可以改写成
ΔL=−ηsign(gt)⋅gt=−η√N‖gt‖cos(sign(gt),gt)
这里假设了gt没有零分量,因此‖sign(gt)‖=√N,N是模型总参数量。实践发现‖gt‖和cos(sign(gt),gt)在不同的模型尺度下都大致为常数,因此如果要维持ΔL不变,应该有η反比于√N,也就是说模型参数量增加到4倍,那么学习率可以考虑减半。
全剧终 #
本文对“梯度裁剪的默认模长为1”这一现象给出了自己的一些看法和思考。
转载到请包括本文地址:https://spaces.ac.cn/archives/10657
更详细的转载事宜请参考:《科学空间FAQ》
如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。
如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!
如果您需要引用本文,请参考:
苏剑林. (Jan. 02, 2025). 《为什么梯度裁剪的默认模长是1? 》[Blog post]. Retrieved from https://spaces.ac.cn/archives/10657
@online{kexuefm-10657,
title={为什么梯度裁剪的默认模长是1?},
author={苏剑林},
year={2025},
month={Jan},
url={\url{https://spaces.ac.cn/archives/10657}},
}
January 6th, 2025
苏老师,想请教一个经验性问题,我在训练0.5B模型的时候(资源有限更大的负担不起),会发现grad norm很快会下降到0.3以下,但随着训练会逐步涨到0.5,趋势上和学习率的cosine decay很一致(前期基本是平的,随着学习率下降越来越快,grad norm也增大越来越多),这是学习率不足的情况下grad norm的一种补偿吗?
如果涨到0.5然后长期稳定在0.5附近,那么是比较正常的。如果0.3开始,一直慢慢涨到0.5到结束,那么这个可能在更长期的训练下有爆炸的风险。我之前遇到过这个现象,是因为Embedding层的更新不够科学。
是涨得越来越快,整个训练周期中lr依照warm up + cosine decay来调度,在训练后期lr下降速度越来越快,grad norm涨得也越来越快。整个训练420K steps,我打印了一个micro batch的grad norm:
========== step: 150k ==========
wte.weight 0.6336
h.0.ln_1.weight 0.0052
h.0.attn.c_attn.weight 0.2984
h.0.attn.c_proj.weight 0.4093
h.0.ln_2.weight 0.0093
h.0.mlp.c_fc.weight 0.1553
h.0.mlp.c_fc2.weight 0.1583
h.0.mlp.c_proj.weight 0.2255
h.1.ln_1.weight 0.011
h.1.attn.c_attn.weight 0.8381
h.1.attn.c_proj.weight 0.3084
h.1.ln_2.weight 0.0067
h.1.mlp.c_fc.weight 0.1961
h.1.mlp.c_fc2.weight 0.1949
h.1.mlp.c_proj.weight 0.2278
h.2.ln_1.weight 0.0061
h.2.attn.c_attn.weight 0.625
h.2.attn.c_proj.weight 0.2228
h.2.ln_2.weight 0.0067
h.2.mlp.c_fc.weight 0.2067
h.2.mlp.c_fc2.weight 0.2072
h.2.mlp.c_proj.weight 0.2307
========== step: 350k ==========
wte.weight 0.8919
h.0.ln_1.weight 0.0068
h.0.attn.c_attn.weight 0.5072
h.0.attn.c_proj.weight 0.6148
h.0.ln_2.weight 0.0216
h.0.mlp.c_fc.weight 0.3006
h.0.mlp.c_fc2.weight 0.3022
h.0.mlp.c_proj.weight 0.3842
h.1.ln_1.weight 0.0065
h.1.attn.c_attn.weight 1.141
h.1.attn.c_proj.weight 0.5584
h.1.ln_2.weight 0.0124
h.1.mlp.c_fc.weight 0.3528
h.1.mlp.c_fc2.weight 0.342
h.1.mlp.c_proj.weight 0.3873
h.2.ln_1.weight 0.0043
h.2.attn.c_attn.weight 0.911
h.2.attn.c_proj.weight 0.3865
h.2.ln_2.weight 0.0104
h.2.mlp.c_fc.weight 0.3603
h.2.mlp.c_fc2.weight 0.3482
h.2.mlp.c_proj.weight 0.3834
看上去embedding的norm确实涨得比较多,但attention和swiglu的grad也都在涨
可以把 embedding 上的 weight decay关掉
请问为什么embedding上的weight decay和grad norm上涨有关?
weight_decay会对loss有一定的负面影响,weight_decay过大的话,会明显压制Embedding对loss的贡献,所以grad越来越大(越来越欠拟合)
请问Embedding层的更新不够科学具体指的是什么?有什么参考资料吗?
https://kexue.fm/archives/9736
January 7th, 2025
llm参数这么多,还分布式,Global Gradient Norm是怎么做的呢?
不用这么死板,工程上可以采样也可以每个node 或每个device计算自己的Gradient Norm
就是按照定义算
January 7th, 2025
实际上,我发现在训练 LLM 的过程中 grad-norm 始终是几百,也就是说一直在被 clip,这样训练也是有效的,该怎么理解呢?如果对 loss 除以一个常数,使得 grad norm 降下来会更好吗?
看文章里提到的《Optimal Linear Decay Learning Rate Schedules and Further Refinements》这篇论文的推导,一直clip等价于norm是被clip成的那个常数,等价于Linear Decay,是一个非常好的scheduler,所以可以正常收敛,不知道我的理解有没有问题
不是一回事,grad clip只是clip grad,影响的是Adam的m、v,不影响学习率,所以没有decay。
grad norm长期被normalize其实也不会有显著问题(尤其是Adam优化器下),只是某些情况下可能会有欠拟合的风险。
January 7th, 2025
我理解这个clip应该是rnn时代带过来的吧,当时因为循环的问题,大于1的数在经过长度n之后会有指数爆炸的问题。同样的角度思考我理解1这个数如果有特殊性的话就是1n=1
现在很多Transfromer模型包括LLM的训练,普遍都有这个grad clip。
January 19th, 2025
苏老师,我的LLM不加label smooth时,grad norm1.,这个会有影响吗?
没看懂有什么因果关系或者问题。
苏老师,我的LLM训练不加label smooth时,grad norm < 1,加了就 > 1,这个会有影响吗?
没什么影响,正常表现。预测smoothed label比预测one hot难,所以grad大一点。
感谢苏老师回复,我现在跑起来也是正常的
January 20th, 2025
苏老师,看到这个因为我自己没接触过LLM,只能联想想到了之前看google的那个pcgard好像也会做类似的投影,多任务的时候,对于方向相反的loss用dot*g/(||g||^2),之前过这方面的尝试,但是发现好像有的模型有用,有的模型会造成loss一直上升,比一个loss还烂,这是为什么,按理说投影到了同一个方向,至少不会比但loss更烂才对吧
我看得不是很明白,不过你这个问题,是不是参考之前的多任务学习系列就可以了? https://kexue.fm/search/%E5%A4%9A%E4%BB%BB%E5%8A%A1%E5%AD%A6%E4%B9%A0
January 23rd, 2025
苏老师,gradient clipping和gradient penalty是不是可以互相替代的呢?
严格来说,这两者没太多联系,更不用说互为替代品了。
gradient clipping是让训练更平稳的一个测试,也就是说原本gradient是有波动的,它试图减少这种波动的负面影响,并且对训练速度的影响很小。
gradient penalty的目的通常是增强模型的泛化性能,它是迫使gradient没有波动,并且加了gradient penalty之后,训练成本通常会大很多(需要求梯度的梯度)。