【优化器】(二) AdaGrad原理 & pytorch代码解析

1.简介

在上一篇文章里,我们介绍了SGD这个最朴素的梯度下降方法,以及其Momentum和NAG的优化改进方法。我们现在又回过头来看这个没加Momentum的图,可以看到在垂直方向上的梯度较大,走得很快,而水平方向上的位移较小,走的很慢,看着就很急!但你先别急,那么能不能不使用Momentum就让水平方向上的位移大一点,垂直方向上的位移小一点呢?

2.二阶动量

我们想要迄今为止梯度大的参数(即垂直方向)更新得慢一点,梯度小的参数(即水平方向)更新得快一点,这个就是二阶动量的定义:

V_{t}=\sum_{\tau =1}^{t}g_{\tau }^{2}

其中,V_{t}为当前时刻的二阶动量,g_{\tau }^{}是某一时刻的梯度,\sum_{\tau =1}^{t}g_{\tau }^{2}表示迄今所有梯度的平方和。AdaGrad就是在SGD的基础上,把二阶动量加入到参数更新的过程中:

w^{t+1}=w^{t}-\alpha \cdot g_{t}/\sqrt{V_{t}}

其中,w为模型参数,\alpha为学习率,这时学习速率就会根据二阶动量来自适应进行调整。

我们现在再从上述的二维图扩展到整个深度学习模型中,并不是所有模型参数都会被频繁更新。对于频繁更新的参数,由于我们已经积累了大量的先验信息,所以不希望被单个样本影响的太大,所以希望学习速率慢一点;对于偶尔更新的参数,我们想要它从偶尔出现的样本上尽可能获取更多得信息,所以希望学习速率快一点。

因此二阶动量可以在数据稀疏的情况下表现很好,因为稀疏数据中0占大多数,此时二阶动量累积梯度和较大,可以有效降低重复数据的学习速度。但是由于V_{t}会随着训练过程不断增大,所以学习率最终会逼近于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

 如果想要了解更多优化器相关知识,可以参考我的专栏和其他相关文章:

优化器_Lcm_Tech的博客-CSDN博客

【优化器】(一) 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博客

如果想要了解更多深度学习相关知识,可以参考我的其他文章:

【损失函数】(一) L1Loss原理 & pytorch代码解析_l1 loss-CSDN博客

【图像生成】(一) DNN 原理 & pytorch代码实例_pytorch dnn代码-CSDN博客

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值