📅  最后修改于: 2023-12-03 15:13:15.532000             🧑  作者: Mango
Adagrad 是一种自适应学习率方法,最初由 Duchi 等人于 2011 年提出。它的主要特点是:对于每个参数,它使用历史梯度平方和的平方根来缩放学习率,以便每个参数都有不同的学习率。
Adagrad 的直觉可以通过一个简单的例子来说明。假设我们要最小化以下函数:
f(x,y) = x^2 + 2y^2
我们可以使用梯度下降法来解决这个问题:
x' = x - α * ∂f(x,y)/∂x
y' = y - α * ∂f(x,y)/∂y
其中,α 是学习率。
如果我们使用固定的学习率(例如,α=0.1),那么在一些方向上(例如,y 方向)可能会很快不断地下降,而在其他方向上可能会一直徘徊不前(例如,x 方向)。这会导致收敛速度变慢,并且可能难以找到全局最小值。
Adagrad 的做法是对每个参数,使用历史梯度平方和的平方根来缩放学习率。因为历史梯度平方和的平方根是不断增加的(因为梯度平方非负),所以学习率会随时间逐渐减小,更有可能在最小值处停止。
因此,对于上面的例子,Adagrad 可以写成:
x' = x - α * ∂f(x,y)/∂x / sqrt(sum(∂f(x,y)/∂x)^2)
y' = y - α * ∂f(x,y)/∂y / sqrt(sum(∂f(x,y)/∂y)^2)
其中,sum(∂f(x,y)/∂x)^2 表示前 t 个时间步的每个时刻 ∂f(x,y)/∂x 的平方和。
具体的计算方法可以参考 Adagrad - 理论与实践。
import numpy as np
class AdagradOptimizer:
def __init__(self, alpha=0.01, eps=1e-8):
self.alpha = alpha # 学习率
self.eps = eps # 避免除以 0 的小常数
self.sum_sq_grad = None # 历史梯度平方和的平均数
def update(self, w, grad):
if self.sum_sq_grad is None:
self.sum_sq_grad = np.zeros_like(grad)
self.sum_sq_grad += grad ** 2
lr = self.alpha / (np.sqrt(self.sum_sq_grad) + self.eps)
w -= lr * grad
return w
以上是基本的 Adagrad 优化器实现。在每个时间步,optimizer.update(参数,梯度) 可以返回更新后的参数。需要注意的是,由于每个参数都有自己的历史梯度平方和,所以需要为每个参数单独维护 sum_sq_grad。
在这篇文章中,我们介绍了 Adagrad 优化器的直觉,并实现了一个基本的 Adagrad 优化器。由于 Adagrad 基于历史梯度平方和的平均数,因此它跟踪了每个参数的梯度信息,并适应了每个参数的学习率。Adagrad 的主要缺点是当历史梯度平方和变得非常大时,更新会变慢,甚至会停止更新。因此,后来的优化器(例如 RMSprop 和 Adam)引入了可调整的动量和二阶动量来解决这个问题。