中文版
深入分析:Adam与RMSProp优化器的区别以及显存消耗
在深度学习中,优化器是训练神经网络时至关重要的组件。Adam(Adaptive Moment Estimation)和RMSProp是两种常用的优化算法,它们都试图通过自适应调整学习率来提高模型的训练效率和性能。今天,我们将深入探讨这两种优化器的区别,并结合数学公式详细分析它们的工作原理,最后分析在训练过程中它们的显存消耗。
1. Adam与RMSProp的基本概念
1.1 Adam优化器
Adam优化器结合了动量(Momentum)和自适应学习率的思想,它在每个参数上维护两个变量:
- 第一矩(momentum):对梯度的指数加权平均,用于减少振荡。
- 第二矩(RMSProp的变体):对梯度的平方的指数加权平均,用于自适应调整学习率。
Adam的主要思想是通过对一阶和二阶矩的估计,来调整每个参数的更新步长,使得每个参数在训练过程中能够拥有自己的学习率。
1.1.1 Adam优化器的数学公式
Adam优化器的更新公式如下:
-
梯度计算:
g t = ∇ θ L ( θ t ) g_t = \nabla_\theta L(\theta_t) gt=∇θL(θt)
其中 ( g t g_t gt ) 是在时间步 ( t t t ) 的梯度,( θ t \theta_t θt ) 是模型参数,( L ( θ t ) L(\theta_t) L(θt) ) 是损失函数。 -
一阶矩(动量)更新:
m t = β 1 m t − 1 + ( 1 − β 1 ) g t m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t mt=β1mt−1+(1−β1)gt
其中 ( β 1 \beta_1 β1 ) 是一阶矩的衰减系数,通常取值接近1(如 0.9)。 -
二阶矩(RMSProp变体)更新:
v t = β 2 v t − 1 + ( 1 − β 2 ) g t 2 v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2 vt=β2vt−1+(1−β2)gt2
其中 ( β 2 \beta_2 β2 ) 是二阶矩的衰减系数,通常取值接近1(如 0.999)。 -
偏置修正:
m ^ t = m t 1 − β 1 t , v ^ t = v t 1 − β 2 t \hat{m}_t = \frac{m_t}{1 - \beta_1^t}, \quad \hat{v}_t = \frac{v_t}{1 - \beta_2^t} m^t=1−β1tmt,v^t=1−β2tvt
这里的偏置修正是为了在训练初期纠正一阶矩和二阶矩的偏差。 -
参数更新:
θ t + 1 = θ t − α v ^ t + ϵ m ^ t \theta_{t+1} = \theta_t - \frac{\alpha}{\sqrt{\hat{v}_t} + \epsilon} \hat{m}_t θt+1=θt−v^t+ϵαm^t
其中,( α \alpha α ) 是学习率,( ϵ \epsilon ϵ ) 是一个小常数,防止除零错误。
1.2 RMSProp优化器
RMSProp(Root Mean Square Propagation)优化器是一种自适应优化算法,它通过维护梯度平方的指数加权平均来调整每个参数的学习率。与传统的SGD优化器不同,RMSProp为每个参数分配了不同的学习率。Root Mean Square是均方根的意思。
1.2.1 RMSProp优化器的数学公式
RMSProp的更新公式如下:
-
梯度计算:
g t = ∇ θ L ( θ t ) g_t = \nabla_\theta L(\theta_t) gt=∇θL(θt) -
梯度平方的指数加权平均:
v t = β v t − 1 + ( 1 − β ) g t 2 v_t = \beta v_{t-1} + (1 - \beta) g_t^2 vt=βvt−1+(1−β)gt2
其中,( β \beta β ) 是衰减系数,通常取值接近1(如 0.9)。 -
参数更新:
θ t + 1 = θ t − α v t + ϵ g t \theta_{t+1} = \theta_t - \frac{\alpha}{\sqrt{v_t + \epsilon}} g_t θt+1=θt−vt+ϵαgt
其中,( α \alpha α ) 是学习率,( ϵ \epsilon ϵ ) 是一个小常数,防止除零错误。
1.3 Adam与RMSProp的区别
-
动量项的差异:
- Adam:不仅使用了梯度的一阶矩(动量),还使用了梯度平方的二阶矩(类似RMSProp)。这种方法使得Adam能够更加全面地调整每个参数的学习率,特别是在稀疏梯度情况下,效果更为显著。
- RMSProp:只使用梯度平方的加权平均,不包含一阶矩。因此,RMSProp只依赖于梯度的变化,而没有考虑动量的积累。
-
更新规则的差异:
- Adam:对梯度的更新使用了偏置修正,并且同时考虑一阶和二阶矩的动态变化。
- RMSProp:仅对梯度平方的历史进行更新,并且没有偏置修正。
-
适用场景:
- Adam:通常适用于所有场景,特别是当网络复杂且梯度稀疏时,Adam通常能够提供更好的性能。
- RMSProp:在处理循环神经网络(RNN)等任务时,RMSProp由于自适应学习率的特性,也能表现良好,但在面对稀疏梯度和非凸优化问题时,Adam更具优势。
2. 显存消耗分析:Adam与RMSProp
Adam和RMSProp在训练过程中都会增加额外的内存开销,主要体现在它们需要存储梯度的历史信息和其他优化器状态变量。下面分析这两种优化器在显存消耗方面的不同:
2.1 Adam的显存消耗
- Adam需要为每个参数维护一阶矩和二阶矩,因此其内存消耗是标准SGD的3倍:
- 模型参数(例如:LLaMA-2 7B有70亿参数):假设使用float32,每个参数占4字节,模型参数所需的内存为28 GB。
- 梯度:与模型参数大小相同,需要28 GB。
- 优化器状态:Adam需要两个状态变量(动量和二阶矩),所以优化器状态占用的内存为56 GB(28 GB × 2)。
因此,总显存需求为:
总显存需求 = 28 GB ( 模型 ) + 28 GB ( 梯度 ) + 56 GB ( 优化器 ) = 112 GB \text{总显存需求} = 28 \, \text{GB} (\text{模型}) + 28 \, \text{GB} (\text{梯度}) + 56 \, \text{GB} (\text{优化器}) = 112 \, \text{GB} 总显存需求=28GB(模型)+28GB(梯度)+56GB(优化器)=112GB
下面的python代码测试7B大模型本身的参数量:以float32计算。进位采用1024,计算得出:7B大模型的参数量为26.08 GB;当进位采用1000时,计算得出28.00 GB。为什么尝试1000,是因为在其他博文中看到28GB这个数字,自己测试一下,发现他们是在以1000为进位的时候测试得出的。参考文章:https://cuiyuhao.com/posts/c87c0f5d/
# 定义参数
num_parameters = 7 * 10**9 # 70 亿个参数
bytes_per_param = 4 # 每个参数占用 4 字节(32 位浮动数)
# 计算显存需求(单位:字节)
memory_in_bytes = num_parameters * bytes_per_param
# 将字节转换为 GB
memory_in_GB = memory_in_bytes / (1024 ** 3) # 转换为 GB, 可调为1000
print(f"模型需要的显存为: {memory_in_GB:.2f} GB")
以bf16为例,由于它是float32的一半,所以它的参数量为 26.08GB / 2 = 13.04GB (以1024为进位),当以1000进位的时候,28GB / 2 = 14GB
2.2 RMSProp的显存消耗
RMSProp优化器只需要维护梯度平方的加权平均,因此内存消耗比Adam少。与SGD一样,RMSProp仅需要存储梯度的平方(即一个变量):
- 模型参数:28 GB
- 梯度:28 GB
- 优化器状态:RMSProp仅需要一个状态变量(梯度平方),占用28 GB。
因此,总显存需求为:
总显存需求 = 28 GB ( 模型 ) + 28 GB ( 梯度 ) + 28 GB ( 优化器 ) = 84 GB \text{总显存需求} = 28 \, \text{GB} (\text{模型}) + 28 \, \text{GB} (\text{梯度}) + 28 \, \text{GB} (\text{优化器}) = 84 \, \text{GB} 总显存需求=