📜  Adagrad 优化器背后的直觉(1)

📅  最后修改于: 2023-12-03 15:13:15.532000             🧑  作者: Mango

Adagrad 优化器背后的直觉

简介

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 - 理论与实践

代码实现(Python)
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)引入了可调整的动量和二阶动量来解决这个问题。