1.简介
在上一篇文章里,我们介绍了SGD这个最朴素的梯度下降方法,以及其Momentum和NAG的优化改进方法。我们现在又回过头来看这个没加Momentum的图,可以看到在垂直方向上的梯度较大,走得很快,而水平方向上的位移较小,走的很慢,看着就很急!但你先别急,那么能不能不使用Momentum就让水平方向上的位移大一点,垂直方向上的位移小一点呢?
2.二阶动量
我们想要迄今为止梯度大的参数(即垂直方向)更新得慢一点,梯度小的参数(即水平方向)更新得快一点,这个就是二阶动量的定义:
其中,为当前时刻的二阶动量,
是某一时刻的梯度,
表示迄今所有梯度的平方和。AdaGrad就是在SGD的基础上,把二阶动量加入到参数更新的过程中:
其中,为模型参数,
为学习率,这时学习速率就会根据二阶动量来自适应进行调整。
我们现在再从上述的二维图扩展到整个深度学习模型中,并不是所有模型参数都会被频繁更新。对于频繁更新的参数,由于我们已经积累了大量的先验信息,所以不希望被单个样本影响的太大,所以希望学习速率慢一点;对于偶尔更新的参数,我们想要它从偶尔出现的样本上尽可能获取更多得信息,所以希望学习速率快一点。
因此二阶动量可以在数据稀疏的情况下表现很好,因为稀疏数据中0占大多数,此时二阶动量累积梯度和较大,可以有效降低重复数据的学习速度。但是由于会随着训练过程不断增大,所以学习率最终会逼近于0,可能会造成训练的提前终止。
3.pytorch代码
AdaGrad的伪代码流程如下,重点在最后两行,不考虑weight_decay的情况下,把梯度的平方累加到state_sum中,再用当前梯度除以state_sum乘以学习率来更新参数:
以下代码为pytorch官方AdaGrad代码。
def _single_tensor_adagrad(
params: List[Tensor],
grads: List[Tensor],
state_sums: List[Tensor],
state_steps: List[Tensor],
*,
lr: float,
weight_decay: float,
lr_decay: float,
eps: float,
has_sparse_grad: bool,
maximize: bool,
differentiable: bool,
):
for (param, grad, state_sum, step_t) in zip(params, grads, state_sums, state_steps):
# update step
step_t += 1
step = _get_value(step_t)
grad = grad if not maximize else -grad
if weight_decay != 0:
if grad.is_sparse:
raise RuntimeError(
"weight_decay option is not compatible with sparse gradients"
)
grad = grad.add(param, alpha=weight_decay)
clr = lr / (1 + (step - 1) * lr_decay)
if grad.is_sparse:
grad = grad.coalesce() # the update is non-linear so indices must be unique
grad_indices = grad._indices()
grad_values = grad._values()
state_sum.add_(_make_sparse(grad, grad_indices, grad_values.pow(2)))
std = state_sum.sparse_mask(grad)
std_values = std._values().sqrt_().add_(eps)
param.add_(
_make_sparse(grad, grad_indices, grad_values / std_values), alpha=-clr
)
else:
is_complex = torch.is_complex(param)
if is_complex:
grad = torch.view_as_real(grad)
state_sum = torch.view_as_real(state_sum)
param = torch.view_as_real(param)
state_sum.addcmul_(grad, grad, value=1)
if differentiable:
std = state_sum.sqrt() + eps
else:
std = state_sum.sqrt().add_(eps)
param.addcdiv_(grad, std, value=-clr)
if is_complex:
param = torch.view_as_complex(param)
state_sum = torch.view_as_complex(state_sum)
业务合作/学习交流+v:lizhiTechnology
如果想要了解更多优化器相关知识,可以参考我的专栏和其他相关文章:
【优化器】(一) SGD原理 & pytorch代码解析_sgd优化器-CSDN博客
【优化器】(二) AdaGrad原理 & pytorch代码解析_adagrad优化器-CSDN博客
【优化器】(三) RMSProp原理 & pytorch代码解析_rmsprop优化器-CSDN博客
【优化器】(四) AdaDelta原理 & pytorch代码解析_adadelta里rho越大越敏感-CSDN博客
【优化器】(五) Adam原理 & pytorch代码解析_adam优化器-CSDN博客
【优化器】(六) AdamW原理 & pytorch代码解析-CSDN博客
【优化器】(七) 优化器统一框架 & 总结分析_mosec优化器优点-CSDN博客
如果想要了解更多深度学习相关知识,可以参考我的其他文章: