Adam算法

1. 什么是Adam优化算法?

Adam算法是在2014年提出的一种基于一阶梯度的优化算法,它结合了动量(Momentum)和 RMSprop(Root Mean Square Propagation)的思想,自适应地调整每个参数的学习率。这使得Adam特别适合处理大规模数据及参数的优化问题,例如深度神经网络。

2. Adam的工作原理

Adam算法的核心在于计算每个参数的一阶矩(即梯度的均值)和二阶矩(即梯度的未中心化方差)的指数移动平均,并利用这些矩的估计值来调整每个参数的学习率。

关键步骤

  • 计算梯度的一阶矩和二阶矩:

对于时间步 ttt,给定梯度 gtg_tgt,对应的一阶矩 mtm_tmt 和二阶矩 vtv_tvt 分别为:

mt=β1⋅mt−1+(1−β1)⋅gtm_t=\beta_1\cdot m_{t-1}+(1-\beta_1)\cdot g_tmt=β1⋅mt−1+(1−β1)⋅gt

解释:这是对过去梯度的指数衰减平均。当 β1\beta_1β1 接近 1 时,历史梯度的影响衰减得慢(即长期"记忆"),与传统 momentum 相似。初始 m0=0m_0=0m0=0。

vt=β2⋅vt−1+(1−β2)⋅gt2v_t=\beta_2\cdot v_{t-1}+(1-\beta_2)\cdot g_t^2vt=β2⋅vt−1+(1−β2)⋅gt2

其中 β1\beta_1β1 和 β2\beta_2β2 是超参数,通常接近1。这里 gt2g_t^2gt2 表示向量按元素平方(每个参数维度独立计算)。vtv_tvt 估计梯度的二阶原点(元素级方差或"幅度"),用于自适应调整每个参数的步长。初始 v0=0v_0=0v0=0

偏置校正:

由于 mtm_tmt 和 vtv_tvt 都是被初始化为向量,Adam需要对这些矩进行校正,以消除初期估计的偏差:

m^t=mt1−β1t\hat{m}_t=\frac{m_t}{1-\beta_1^t}m^t=1−β1tmt
vt^=vt1−β2t\hat{v_t}=\frac{v_t}{1-\beta_2^t}vt^=1−β2tvt

解释:当 mtm_tmt 与 vtv_tvt 从 0 开始累积时,它们在早期会被"缩小"(偏向 0),所以要除以因子 1−βt1-\beta^t1−βt 来校正这一偏差,使得 m^t\hat{m}_tm^t 与 v^t\hat{v}_tv^t 在均值意义上更接近真实的一阶、二阶矩估计。

参数更新:

最后,利用校正过的矩值来更新参数:

θt+1=θt−αv^t+ϵm^t\theta_{t+1}=\theta_t-\frac{\alpha}{\sqrt{\hat{v}_t}+\epsilon}\hat{m}_tθt+1=θt−v^t +ϵαm^t

其中,

  • θ\thetaθ 是待优化参数
  • α\alphaα 是学习率
  • ϵ\epsilonϵ 是为了防止除以零而添加的很小的数值(一般为 10−810^{-8}10−8)
  • 分母 v^t+ϵ\sqrt{\hat{v}_t}+\epsilonv^t +ϵ 是按元素的缩放项:如果某个参数的梯度幅度很大(v^t\hat{v}_tv^t大),则分母大,从而参数更新步长变小;反之,稀疏或小梯度参数能获得相对更大的步长。

3. 为什么要做偏差校正 --- 详细推导

重点是理解为什么要把 mtm_tmt 除以 1−β1t1-\beta_1^t1−β1t。

从定义:

mt=β1mt−1+(1−β1)gt, m0=1m_t=\beta_1m_{t-1}+(1-\beta_1)g_t,\ m_0=1mt=β1mt−1+(1−β1)gt, m0=1

把它展开:

1)写出 m1m_1m1:

m1=(1−β1)g1m_1=(1-\beta_1)g_1m1=(1−β1)g1

2)写出 m2m_2m2:

m2=β1m1+(1−β1)g2=β1(1−β1)g1+(1−β1)g2m_2=\beta_1m_1+(1-\beta_1)g_2=\beta_1(1-\beta_1)g_1+(1-\beta_1)g_2m2=β1m1+(1−β1)g2=β1(1−β1)g1+(1−β1)g2

3)写出 m3m_3m3:

m3=β1m2+(1−β1)g3m_3=\beta_1m_2+(1-\beta_1)g_3m3=β1m2+(1−β1)g3

代入 m2m_2m2:

=β1[β1(1−β1)g1+(1−β1)g2]+(1−β1)g3=(1−β1)[β12g1+β1g2+g3]\begin{align*} = &\beta_1[\beta_1(1-\beta_1)g_1 + (1-\beta_1)g_2] + (1-\beta_1)g_3 \\ = &(1-\beta_1)[\beta_1^2g_1 + \beta_1g_2 + g_3] \end{align*}==β1[β1(1−β1)g1+(1−β1)g2]+(1−β1)g3(1−β1)[β12g1+β1g2+g3]

(迭代展开发现是加权和):

mt=(1−β1)∑i=1tβ1t−igim_t=(1-\beta_1)\sum_{i=1}^t\beta_1^{t-i}g_imt=(1−β1)i=1∑tβ1t−igi

如果我们假设梯度的期望在某种意义上稳定(或考虑条件期望),令 μi=E[gi]\mu_i=\mathbb{E[g_i]}μi=E[gi],在理想均值不变的情形下(例如近似 μi=μ\mu_i=\muμi=μ),则期望为:

E[mt]=(1−β1)∑i=1tβ1t−iE[gi]≈(1−β1)μ∑i=0t−1β1i=μ(1−β1t)\mathbb{E[m_t]}=(1-\beta_1)\sum_{i=1}^t\beta_1^{t-i}\mathbb{E[g_i]}\approx(1-\beta_1)\mu\sum_{i=0}^{t-1}\beta_1^i=\mu(1-\beta_1^t)E[mt]=(1−β1)i=1∑tβ1t−iE[gi]≈(1−β1)μi=0∑t−1β1i=μ(1−β1t)

其中 ∑i=0t−1β1i\sum_{i=0}^{t-1}\beta_1^i∑i=0t−1β1i 用等比数列求和公式求得:

∑i=0t−1β1i=1−β1t1−β1\sum_{i=0}^{t-1}\beta_1^i=\frac{1-\beta_1^t}{1-\beta_1}i=0∑t−1β1i=1−β11−β1t

所以 mtm_tmt 的期望值是 μ(1−β1t)\mu(1-\beta_1^t)μ(1−β1t),而不是 μ\muμ 。当 ttt 很小时,1−β1t1-\beta_1^t1−β1t 远小于 1(尤其 β1\beta_1β1 接近 1),因此 mtm_tmt 在早期被低估。把 mtm_tmt 除以 1−β1-\beta1−β 可以抵消这个缩放因子,从而得到无偏估计:

m^t≈μ\hat{m}_t\approx \mum^t≈μ

同理对 vtv_tvt (平方梯度的 EMA)有类似推导,得出除以 1−β2t1-\beta_2^t1−β2t 的理由。

4. Adam的优点

  • 自适应学习率:Adam算法通过考虑梯度的一阶和二阶矩来自适应调整每个参数的学习率,有助于更快地收敛。
  • 偏置校正:通过偏置校正,Adam算法可以调整初始阶段的矩估计,从而提高算法的稳定性。
  • 适用于非稳态目标:有效处理梯度的稀疏性和噪声问题。
  • 易于实现:Adam算法的实现相对简单,并且在大多数深度学习框架中都有内置支持,如TensorFlow和PyTorch。

5. 完整 Python 代码

python 复制代码
import math

class AdamOptimizer:
    def __init__(self, lr=0.001, beta1=0.9, beta2=0.999, eps=1e-8):
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.eps = eps
        self.m = 0.0
        self.v = 0.0
        self.t = 0

    def step(self, grad):
        """
        grad: 当前梯度(标量示例,向量版需要按维度独立处理)
        """
        self.t += 1

        # 一阶矩更新
        self.m = self.beta1 * self.m + (1 - self.beta1) * grad
        # 二阶矩更新
        self.v = self.beta2 * self.v + (1 - self.beta2) * (grad ** 2)

        # 偏差校正(核心!)
        m_hat = self.m / (1 - self.beta1 ** self.t)
        v_hat = self.v / (1 - self.beta2 ** self.t)

        # 参数更新
        update = self.lr * m_hat / (math.sqrt(v_hat) + self.eps)
        return update, self.m, self.v, m_hat, v_hat


# ---------------------------
# 示例:最小化 f(x) = x^2
# ---------------------------

def f(x):
    return x * x

def grad_f(x):
    return 2 * x

# 初始值
x = 10.0
adam = AdamOptimizer(lr=0.1)

print("t | x | grad | m | v | m_hat | v_hat | update")
print("-"*80)

for step in range(1, 21):
    g = grad_f(x)
    update, m, v, m_hat, v_hat = adam.step(g)

    # Adam 的更新方向是负梯度方向
    x = x - update

    print(f"{step:2d} | {x:8.5f} | {g:6.3f} | {m:8.5f} | {v:8.5f} | {m_hat:8.5f} | {v_hat:8.5f} | {update:8.5f}")
相关推荐
JoannaJuanCV2 小时前
自动驾驶—CARLA仿真(4)基础概念
人工智能·机器学习·自动驾驶
GISer_Jing2 小时前
AI在前端营销和用户增长领域应用(待补充)
前端·人工智能
JoannaJuanCV2 小时前
自动驾驶—CARLA仿真(3) 坐标和坐标变换
人工智能·机器学习·自动驾驶
TMO Group 探谋网络科技2 小时前
AI电商的应用:Magento 使用 Adobe 生成式 AI改造7大业务场景
大数据·人工智能·adobe·ai
另寻沧海2 小时前
C++ Lambda表达式的隐式转换陷阱
java·c++·算法
菜鸟233号2 小时前
力扣654 最大二叉树 java实现
java·算法·leetcode
UI设计兰亭妙微2 小时前
理性数据,温柔体验:北京兰亭妙微解码 Hydra Corps. 企业管理界面的 “松弛感设计”
大数据·人工智能·用户体验设计
慎独4132 小时前
家家有:从单向支出到价值循环,绿色积分如何 重构商业逻辑?
大数据·人工智能