参考:
Adam中包含两个重要部分: 学习率调整(RMSprop算法) 与 梯度修正估计
在标准梯度下降算法中,每个参数在每次迭代时都使用相同的学习率。如果学习率过大则可能无法收敛;如果过小则收敛非常慢;
而Adam算法通过计算梯度的一阶矩估计和二阶距估计,从而为不同的参数设计独立的自适应学习率。
Adam是两种随机梯度下降扩展式的优点集合:
- 适应性梯度算法(AdaGrad), 为每个参数保留一个学习率以提升在稀疏梯度上的性能
- 均方根传播(RMSProp), 基于权重梯度最近量级的均值为每一个参数适应性的保留学习率。
RMSprop算法
RMSProp算法首先计算每次迭代梯度
g
t
g_t
gt 平方的指数衰减移动平均
G
t
G_t
Gt
G
t
=
β
G
t
−
1
+
(
1
−
β
)
g
t
⊙
g
t
=
(
1
−
β
)
∑
τ
=
1
t
β
t
−
τ
g
τ
⊙
g
τ
其中,
β
为自定义衰减率
G_t = \beta G_{t-1} + (1-\beta)g_t \odot g_t \\ = (1 - \beta)\sum_{\tau =1}^t \beta^{t - \tau}g_\tau \odot g_\tau \\ 其中,\beta 为自定义衰减率
Gt=βGt−1+(1−β)gt⊙gt=(1−β)τ=1∑tβt−τgτ⊙gτ其中,β为自定义衰减率
然后,根据指数衰减移动平均
G
t
G_t
Gt 计算参数更新差值
Δ
θ
t
\Delta\theta_t
Δθt :
Δ
θ
t
=
−
α
G
t
+
ϵ
⊙
g
t
\Delta\theta_t = - \frac{\alpha}{\sqrt{G_t + \epsilon}}\odot g_t
Δθt=−Gt+ϵα⊙gt
其中,
α
\alpha
α 是初始学习率,
ϵ
\epsilon
ϵ 是为了保持数值稳定性而设置的非常小的常数。
动量法
在小批量随机梯度下降算法中,如果可能出现每次迭代的梯度估计和整个训练集上的最优梯度不一样的情况,具有一定的随机性,导致下降时出现振荡。一种有效的缓解梯度估计随机性的方式是,通过最近一段时间内的平均梯度来代替当前时刻的随机梯度来作为参数更新的方向。从而提高优化速度。
动量法使用之前累积的动量来代替真正的梯度,在第
t
t
t次迭代时,计算负梯度的“加权移动平均”来作为参数的更新方向。
Δ
θ
t
=
ρ
Δ
θ
t
−
1
−
α
g
t
=
−
α
∑
τ
=
1
t
ρ
t
−
τ
g
τ
\Delta \theta_t = \rho \Delta \theta_{t-1} - \alpha g_t \\ = -\alpha \sum_{\tau=1}^{t}\rho ^{t-\tau}g_\tau
Δθt=ρΔθt−1−αgt=−ατ=1∑tρt−τgτ
其中,
ρ
\rho
ρ 为动量因子,通常设为0.9,
α
\alpha
α 为学习率。
动量法将每个参数的实际更新差值表示为最近一段时间内梯度的加权平均值,在迭代前期,梯度方向都一致,动量法加速参数更新幅度; 但是在迭代后期,梯度方向会不一致,动量法降低参数更新幅度
Adam算法
Adam结合了RMSprop算法和动量法,使用动量法作为参数更新反向,使用RMSProp作为自适应调整学习率
- 计算梯度 g t g_t gt 平方的指数加权平均
G t = β 2 G t − 1 + ( 1 − β 2 ) g t ⊙ g t G_t = \beta_2 G_{t-1} + (1-\beta_2)g_t \odot g_t Gt=β2Gt−1+(1−β2)gt⊙gt
- 计算梯度 g t g_t gt 的指数加权平均(动量法)
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 , β 2 \beta_1, \beta_2 β1,β2 分别为两个移动平均的衰减率。在Adam原文中取值分别为0.9和0.999.
- 对偏差进行修正
M t ^ = M t 1 − β 1 t G t ^ = G t 1 − β 2 t \hat{M_t} = \frac{M_t}{1-\beta_1^t}\\ \hat{G_t} = \frac{G_t}{1 - \beta_2^t} Mt^=1−β1tMtGt^=1−β2tGt
- 计算参数更新差值
Δ θ t = − α G t ^ + ϵ M t ^ \Delta \theta_t = - \frac{\alpha}{\sqrt{\hat{G_t} + \epsilon}} \hat{M_t} Δθt=−Gt^+ϵαMt^
扩展, AdamW 算法。在Adam算法的基础上,AdamW增加了权重衰减,直接将权重衰减应用到权重更新步骤中,而不是将之作为损失函数的一部分进行梯度计算。
在adam算法基础上,AdamW算法:
θ
t
−
θ
t
−
1
=
−
α
(
M
t
^
G
t
^
+
ϵ
+
λ
θ
t
−
1
)
\theta_t - \theta_{t-1} = -\alpha(\frac{\hat{M_t}}{\sqrt{\hat{G_t} + \epsilon}} + \lambda \theta_{t-1})
θt−θt−1=−α(Gt^+ϵMt^+λθt−1)
其中,
λ
\lambda
λ是权重衰减系数。
看一个实现:
void gpt2_update(GPT2 *model, float learning_rate, float beta1, float beta2, float eps, float weight_decay, int t) {
for (size_t i = 0; i < model->num_parameters; i++) {
float param = model->params_memory[i];
float grad = model->grads_memory[i];
// update the first moment (momentum)
float m = beta1 * model->m_memory[i] + (1.0f - beta1) * grad;
// update the second moment (RMSprop)
float v = beta2 * model->v_memory[i] + (1.0f - beta2) * grad * grad;
// bias-correct both moments
float m_hat = m / (1.0f - powf(beta1, t));
float v_hat = v / (1.0f - powf(beta2, t));
// update
model->m_memory[i] = m;
model->v_memory[i] = v;
model->params_memory[i] -= learning_rate * (m_hat / (sqrtf(v_hat) + eps) + weight_decay * param);
}
}
model->num_parameters 对应参数个数,model->params_memory
对应模型的参数数组,model->grads_memory
对应模型参数相应位置上的梯度。
// 使用公式 5 更新动量
float m = beta1 * model->m_memory[i] + (1.0f - beta1) * grad;
// 使用公式 4 更新RMSprop
float v = beta2 * model->v_memory[i] + (1.0f - beta2) * grad * grad;
公式 6 修正偏差:
float m_hat = m / (1.0f - powf(beta1, t));
float v_hat = v / (1.0f - powf(beta2, t));
最后更新参数
model->params_memory[i] -= learning_rate * (m_hat / (sqrtf(v_hat) + eps) + weight_decay * param);