Adam:让大语言模型变得实用的优化算法
没有Adam,ChatGPT、Claude、Gemini、Llama以及当今许多大语言模型(LLM)很可能需要更长时间才能成为现实。当人们谈论现代AI背后的突破时,通常会提到Transformer、注意力机制、GPU或海量数据集。很少有人会提到优化器。然而,在训练现代大语言模型的过程中,每一次梯度更新都依赖于一个优化算法来决定每个参数应该改变多少。面对数十亿的参数和数万亿的训练标记,这个决策成为机器学习中最关键的工程问题之一。2014年提出的一种优化器最终成为深度学习领域默认的选择:Adam(自适应矩估计)。让我们探讨一下原因。
在Adam之前:为什么训练神经网络如此困难
想象你在浓雾中下山。你只能看到脚下的坡度。显而易见的策略很简单:向山下走一步。这本质上就是梯度下降所做的。对于神经网络,梯度告诉我们哪个方向能减少损失。更新规则很简单:
[ \theta = \theta - \eta \nabla L ]
其中 θ = 模型参数,η = 学习率,∇L = 梯度。很简单。不幸的是,真实的神经网络并不像平滑的山脉。相反,它们更像是:陡峭的悬崖、狭长的山谷、平坦的高原、噪声地形,以及数百万或数十亿个维度。梯度在每个小批量(mini-batch)中都会发生变化,因为我们只在一小部分数据样本上计算它。这就导致了随机梯度下降(SGD)。它不是平稳地下山,而是像有人在你脚下随机抖动山体时试图下降。
动量:给优化过程加上惯性
研究人员意识到,人类不会每走一步都立刻停下并改变方向。相反,我们会积累动量。优化借鉴了同样的思路。与其只遵循今天的梯度,动量还会记住之前的梯度。想象一下推一辆沉重的购物车。如果你反复朝大致相同的方向推,它会逐渐加速。小的颠簸不会立刻改变它的路径。数学上,[ v_t=\beta v_{t-1}+(1-\beta)g_t ] 其中 (g_t) 是今天的梯度,(v_t) 是累积的速度。现在更新变为 [ \theta=\theta-\eta v_t ]。这平滑了噪声较大的更新,并有助于跳出浅层的局部不规则区域。动量已经是一个巨大的改进。但另一个问题依然存在。
不同参数的学习速度不同
假设你正在训练一个拥有5亿个参数的神经网络。有些参数接收到的梯度是0.00002,而另一些参数接收到的梯度是45。使用一个全局学习率会带来问题。如果学习率大到足以处理微小梯度,那么大梯度就会爆炸;如果学习率对较大梯度来说是安全的,那么微小梯度几乎不动。这就像给公司里每个员工发完全相同的奖金,不考虑绩效、工龄或职位。有些人被多付了,而另一些人几乎注意不到这个奖励。优化需要针对每个参数单独调整。这一洞察催生了AdaGrad和RMSProp等算法。Adam则结合了两者的最佳思想。
Adam:结合动量与自适应学习率
Adam:让大语言模型(LLM)得以实践的优化算法
2014 年,Diederik P. Kingma 与 Jimmy Ba 在论文《Adam: A Method for Stochastic Optimization》中提出了 Adam。其思想优雅而简洁:Adam 为每个参数维护两个运行统计量。
- 一阶矩(第一动量):梯度的平均值,可理解为动量。记作 ( m_t )。
- 二阶矩(第二动量):梯度平方的平均值,可理解为衡量该参数更新的“波动性”或“不确定性”。记作 ( v_t )。
更新公式近似为:
[ \theta \leftarrow \theta - \eta \frac{m_t}{\sqrt{v_t} + \epsilon} ]
这产生了有趣的行为:如果一个参数持续收到巨大的梯度,其分母会变大,使后续更新变小;如果另一个参数很少变化,其分母保持较小,从而获得相对较大的更新。每个参数实际上都拥有自己个性化的学习率。
一个简单的估算示例
假设两个参数具有相同的动量:
参数 A
平均梯度 = 2
平均梯度平方 = 100
参数 B
平均梯度 = 2
平均梯度平方 = 4
忽略 ε,参数 A 更新量为 ( 2 / \sqrt{100} = 0.2 ),参数 B 更新量为 ( 2 / \sqrt{4} = 1 )。尽管当前梯度相同,但 Adam 更信任参数 B,因为其历史方差更低。这种自动缩放机制是 Adam 能高效训练深度网络的原因之一。
为什么需要偏差修正
存在一个微妙的问题:训练开始时,两个移动平均值都初始化为零,这意味着早期估计会偏向零。Kingma 和 Ba 引入了偏差修正:
[ \hat{m}_t \leftarrow \frac{m_t}{1 - \beta_1^t} ]
[ \hat{v}_t \leftarrow \frac{v_t}{1 - \beta_2^t} ]
这些修正能快速去除启动时的偏差。虽是一个小小的数学技巧,但在优化初期对实际效果有惊人的影响。