1.简介

在之前的文章了,我们学习了SGD,以及在其基础上加了一阶动量的SGD Momentum,还有在其基础上加了二阶动量的AdaGrad、AdaDelta、RMSProp。那么自然而然就会想到把一阶动量和二阶动量结合起来,这样就形成了我们常用的优化器Adam:Adaptive+Momentum。

2.Adam

类似于Momentum,Adam使用了一阶动量的窗口衰减累加,公式如下:

其中,为当前step的一阶动量,为上一个step的一阶动量,为历史一阶动量保留率

类似于RMSProp,Adam使用了二阶动量的窗口衰减累加,公式如下:

其中,为当前step的二阶动量,为上一个step的二阶动量,为历史二阶动量的衰减率。

最终的更新公式为:

其中,为增加分母稳定性的系数,为学习率。

3.思考

其实我们回过头看,Adam其实存在可能不收敛的问题。因为对SGD而言,学习率是固定的,但是一般会使用scheduler使得学习率不断衰减,所以最终是会收敛的。而对AdaGrad而言,二阶动量的不断累加会导致学习率不断趋近于0最终收敛(虽然不一定能到最优解)。但是对Adam来说,由于二阶动量的突变性,可能在后期时引起学习率的震荡。

4.pytorch代码

Adam的伪代码流程如下,可以看到两个输入的参数和就是我们常设置的两个超参数,一班设为0.9和0.999。同时我们可以看到伪代码里分别对和除以,这一步其实是在做偏差纠正,因为当的时候,并没有历史向量,所以和的系数直接为,这就意味着当较大的时候,更新速度较慢,所以除以一个系数来进行纠正。

以下代码为pytorch官方Adam的代码。

def _single_tensor_adam(params: List[Tensor],

grads: List[Tensor],

exp_avgs: List[Tensor],

exp_avg_sqs: List[Tensor],

max_exp_avg_sqs: List[Tensor],

state_steps: List[Tensor],

grad_scale: Optional[Tensor],

found_inf: Optional[Tensor],

*,

amsgrad: bool,

beta1: float,

beta2: float,

lr: float,

weight_decay: float,

eps: float,

maximize: bool,

capturable: bool,

differentiable: bool):

assert grad_scale is None and found_inf is None

for i, param in enumerate(params):

grad = grads[i] if not maximize else -grads[i]

exp_avg = exp_avgs[i]

exp_avg_sq = exp_avg_sqs[i]

step_t = state_steps[i]

if capturable:

assert param.is_cuda and step_t.is_cuda, "If capturable=True, params and state_steps must be CUDA tensors."

# update step

step_t += 1

if weight_decay != 0:

grad = grad.add(param, alpha=weight_decay)

if torch.is_complex(param):

grad = torch.view_as_real(grad)

exp_avg = torch.view_as_real(exp_avg)

exp_avg_sq = torch.view_as_real(exp_avg_sq)

param = torch.view_as_real(param)

# Decay the first and second moment running average coefficient

exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1)

exp_avg_sq.mul_(beta2).addcmul_(grad, grad.conj(), value=1 - beta2)

if capturable or differentiable:

step = step_t

# 1 - beta1 ** step can't be captured in a CUDA graph, even if step is a CUDA tensor

# (incurs "RuntimeError: CUDA error: operation not permitted when stream is capturing")

bias_correction1 = 1 - torch.pow(beta1, step)

bias_correction2 = 1 - torch.pow(beta2, step)

step_size = lr / bias_correction1

step_size_neg = step_size.neg()

bias_correction2_sqrt = bias_correction2.sqrt()

if amsgrad:

# Maintains the maximum of all 2nd moment running avg. till now

if differentiable:

max_exp_avg_sqs_i = max_exp_avg_sqs[i].clone()

else:

max_exp_avg_sqs_i = max_exp_avg_sqs[i]

max_exp_avg_sqs[i].copy_(torch.maximum(max_exp_avg_sqs_i, exp_avg_sq))

# Uses the max. for normalizing running avg. of gradient

# Folds in (admittedly ugly) 1-elem step_size math here to avoid extra param-set-sized read+write

# (can't fold it into addcdiv_ below because addcdiv_ requires value is a Number, not a Tensor)

denom = (max_exp_avg_sqs[i].sqrt() / (bias_correction2_sqrt * step_size_neg)).add_(eps / step_size_neg)

else:

denom = (exp_avg_sq.sqrt() / (bias_correction2_sqrt * step_size_neg)).add_(eps / step_size_neg)

param.addcdiv_(exp_avg, denom)

else:

step = _get_value(step_t)

bias_correction1 = 1 - beta1 ** step

bias_correction2 = 1 - beta2 ** step

step_size = lr / bias_correction1

bias_correction2_sqrt = _dispatch_sqrt(bias_correction2)

if amsgrad:

# Maintains the maximum of all 2nd moment running avg. till now

torch.maximum(max_exp_avg_sqs[i], exp_avg_sq, out=max_exp_avg_sqs[i])

# Use the max. for normalizing running avg. of gradient

denom = (max_exp_avg_sqs[i].sqrt() / bias_correction2_sqrt).add_(eps)

else:

denom = (exp_avg_sq.sqrt() / bias_correction2_sqrt).add_(eps)

param.addcdiv_(exp_avg, denom, value=-step_size)

业务合作/学习交流+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博客

相关文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: