自适应优化器收敛性理论

深度学习优化器的收敛性分析是理解训练动态的理论基础。本文档系统梳理从经典 SGD 到现代 Adam/AdamW 的收敛性理论,包括在线学习框架下的 Regret 分析、非凸优化理论和方差缩减方法。


1. 预备知识:在线凸优化框架

1.1 问题的数学形式

在在线学习的框架下,优化器被视为**在线凸优化(Online Convex Optimization, OCO)**算法。1

为一系列凸函数(对应每个时刻的损失函数),优化器产生参数序列 。定义 累积后悔(Regret)

其中 为可行域。若 ,则算法被称为悔恨最优(no-regret)

1.2 梯度下降的 Regret 边界

对于 -光滑凸函数,使用固定步长 的梯度下降:

其中 为欧氏投影。

定理 1(标准 SGD 收敛性):设 -Lipschitz 且 -光滑,则使用步长 有:

其中

1.3 非凸设置下的收敛性

在深度学习的非凸设置下,目标是找到静止点(Stationary Point),即满足 的点。

定义-静止点当且仅当

定理 2(非凸 SGD 收敛性):2-方差有界的随机梯度,满足 。则使用步长

1.4 Snigdha et al. 的深度分析

Snigdha 等人的工作进一步细化了非凸设置下的收敛边界,考虑了梯度噪声的结构特性。3

定理 3(结构化噪声下的收敛):假设梯度噪声协方差满足 ,其中 为 Hessian 估计。则 Adam 在适当条件下达到:


2. Adam 优化器的收敛性分析

2.1 Adam 算法回顾

Adam(Adaptive Moment Estimation)的更新规则为:4

其中 为梯度, 为指数衰减率。

2.2 Adam 的 Regret Bound 分析

Reddi 等人首次给出了 Adam 在在线凸优化下的有限时间 Regret 边界。5

定理 4(Adam 的 Regret 上界):设 为凸函数,。则 Adam 的 Regret 满足:

特别地,当 (无动量)时:

2.3 Adam vs SGD:关键理论差异

特性SGDAdam
收敛率(在线凸优化)
自适应学习率需手动调度自适应
动量需显式添加内置一阶矩估计
过拟合倾向较小可能过大(见下节)
非凸收敛有保证需额外条件

2.4 Adam 收敛性的反例

Reddi 等人构造了一个重要反例,说明 Adam 在某些情况下可能发散:5

考虑二维问题,,其中 为 ReLU 函数。通过精心设计的梯度序列,可以使 Adam 产生振荡。

关键问题:当 时,Adam 可能无法收敛到最优解。

2.5 自适应学习率的理论优势

2.5.1 条件数不敏感

考虑病态条件的问题:

使用固定学习率 的 SGD 需要 以保证收敛,收敛率依赖于

而 Adam 通过自适应学习率 有效规范化不同方向的更新。

2.5.2 稀疏梯度问题

对于稀疏特征的问题,Adam 的自适应学习率特别有效。设梯度 是稀疏的,即只有少数坐标非零。Adam 的更新规则使:

即使某些坐标长期没有梯度( 很小),也能保持合理的更新幅度。

2.6 Adam 收敛性的充分条件

定理 5(Adam 收敛的充分条件):6 若满足以下条件,Adam 收敛:

  1. 有界梯度假设 对所有 成立
  2. 参数约束 有界
  3. 动量平衡 接近,通常取
  4. 学习率调度

3. AdamW 的理论基础

3.1 权重衰减 vs L2 正则化

3.1.1 数学区分

L2 正则化在损失函数中添加

梯度更新为:

**权重衰减(Weight Decay)**直接对参数进行衰减:

看起来形式相同?关键区别在于 Adam 的自适应学习率

3.1.2 Adam+L2 的实际问题

在 Adam 中,L2 正则化等价于在梯度上添加 ,而自适应学习率 会导致 L2 效应随梯度历史变化:

而权重衰减:

结论:Adam+L2 中的 L2 效应被自适应学习率缩小,而 Adam+WD 的权重衰减效应保持恒定。

3.2 AdamW 在 Transformer 训练中的理论优势

Loshchilov 和 Hutter 的研究表明,AdamW 在训练 Transformer 时优于 Adam+L2。7

3.2.1 谱范数分析

考虑权重矩阵 的谱范数

权重衰减对谱范数的影响

通过适当选择 ,可以控制权重矩阵的谱范数增长速度。

3.2.2 理论保证

定理 6(谱范数有界收敛):设 -光滑函数, 对所有 。使用 AdamW:

这提供了谱范数的有界保证,有助于:

  • 改善泛化性能
  • 增强训练稳定性
  • 控制激活值的爆炸

3.3 AdamW 的收敛性分析

定理 7(AdamW 收敛性):8 在标准假设下,AdamW 满足:

其中 为权重衰减系数。注意最后一项 表示权重衰减对收敛精度的影响——这解释了为什么 需要适当选择。

3.4 实践中的参数选择

# AdamW 的 PyTorch 实现
class AdamW(torch.optim.Optimizer):
    def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), 
                 eps=1e-8, weight_decay=1e-2):
        defaults = dict(lr=lr, betas=betas, eps=eps, 
                        weight_decay=weight_decay)
        super().__init__(params, defaults)
    
    def step(self, closure=None):
        for group in self.param_groups:
            for p in group['params']:
                if p.grad is None:
                    continue
                
                grad = p.grad.data
                state = self.state[p]
                
                # 状态初始化
                if len(state) == 0:
                    state['exp_avg'] = torch.zeros_like(p.data)
                    state['exp_avg_sq'] = torch.zeros_like(p.data)
                
                exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq']
                beta1, beta2 = group['betas']
                
                # 动量更新
                exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1)
                exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1 - beta2)
                
                # 偏差校正
                bias_correction1 = 1 - beta1 ** state['step']
                bias_correction2 = 1 - beta2 ** state['step']
                
                # 自适应学习率
                step_size = group['lr'] / bias_correction1
                denom = (exp_avg_sq.sqrt() / np.sqrt(bias_correction2)).add_(group['eps'])
                
                # 权重衰减(独立于梯度)
                p.data.add_(p.data, alpha=-group['lr'] * group['weight_decay'])
                
                # 参数更新
                p.data.addcdiv_(exp_avg, denom, value=-step_size)

4. 动量校正与指数移动平均

4.1 偏差校正的数学推导

指数移动平均(Exponential Moving Average, EMA)定义为:

展开可得:

初始时刻 ,导致早期 被低估。

校正因子

足够大()时,,校正效应可忽略。

4.2 动量参数的优化选择

4.2.1 经典动量(SGD with Momentum)

标准动量更新:

有效步长:动量累积使实际步长接近 (稳态时)。

收敛率9

4.2.2 Nesterov 动量

Nesterov 加速梯度(NAG):

收敛率(强凸情况):

相比标准动量的 ,NAG 在强凸函数上达到

4.3 方差缩减方法

随机梯度的高方差是收敛缓慢的主要原因。方差缩减技术通过构造低方差梯度估计来加速收敛。

4.3.1 SVRG(Stochastic Variance Reduced Gradient)

SVRG 每隔 步计算一次全量梯度 10

更新规则:

其中 为随机梯度。修正项 的方差为:

时方差趋于零。

定理 8(SVRG 收敛性):对于 -光滑、-强凸函数,使用

收敛率:,快于 SGD 的

4.3.2 SAGA

SAGA 维护所有样本的梯度表:11

更新规则:

方差

4.3.3 SVRG/SAGA 的比较

方法存储需求每步计算量收敛率(强凸)
SGD1 个样本
SVRG1 个全梯度2 个样本(加速)
SAGA 个梯度1 个样本(加速)
SVRG+SGD1-2 个样本(折中)
class SVRG:
    """
    SVRG (Stochastic Variance Reduced Gradient) 实现
    
    核心思想:周期性地计算全量梯度来修正随机梯度
    """
    def __init__(self, model, data_loader, lr=0.1, m=1000, weight_decay=1e-4):
        self.model = model
        self.data_loader = data_loader
        self.lr = lr
        self.m = m  # 全量梯度更新周期
        self.weight_decay = weight_decay
        self.step_counter = 0
        
    def step(self):
        """执行一次 SVRG 更新"""
        # 获取随机样本
        batch = next(self.data_loader)
        x, y = batch
        
        # 保存当前参数快照的梯度(作为 μ)
        self.model.zero_grad()
        loss_full = self.criterion(self.model(x), y)
        loss_full.backward()
        
        # μ = 全量梯度(存储在 model.grad 中)
        mu = [p.grad.clone() for p in self.model.parameters()]
        
        # 随机样本的梯度
        self.model.zero_grad()
        loss_sampled = self.criterion(self.model(x), y)
        loss_sampled.backward()
        
        # 方差缩减梯度 = 随机梯度 - 当前值 + 快照值
        for p, grad in zip(self.model.parameters(), mu):
            if p.grad is not None:
                # 这里简化处理,实际需要存储每个样本的梯度
                p.grad = p.grad - p.grad + grad + self.weight_decay * p.data
        
        # 参数更新
        with torch.no_grad():
            for p in self.model.parameters():
                p -= self.lr * p.grad
        
        self.step_counter += 1
        
        # 定期更新快照
        if self.step_counter % self.m == 0:
            self.snapshot_params = [p.clone() for p in self.model.parameters()]
    
    def criterion(self, pred, target):
        return F.cross_entropy(pred, target)

5. 其他自适应方法的理论分析

5.1 AdaGrad

AdaGrad 对每个参数自适应学习率:12

其中 为逐元素乘法。

优势:自动适应稀疏参数的高学习率需求。

劣势 单调递增,导致学习率持续衰减。

定理 9(AdaGrad Regret Bound):对于凸函数:

特别地,对于稀疏梯度,AdaGrad 达到 的 Regret。

class AdaGrad:
    """AdaGrad 实现"""
    def __init__(self, params, lr=1.0, eps=1e-10):
        self.params = list(params)
        self.lr = lr
        self.eps = eps
        self.G = [torch.zeros_like(p) for p in self.params]
    
    def step(self):
        for i, p in enumerate(self.params):
            if p.grad is None:
                continue
            g = p.grad.data
            self.G[i] += g.pow(2)
            # 自适应学习率
            p.data -= self.lr * g / (self.G[i].sqrt() + self.eps)

5.2 RMSProp

RMSProp 通过指数移动平均避免 AdaGrad 的学习率衰减问题:13

特点:与 Adam 类似,但不使用一阶矩估计(动量)。

收敛性:RMSProp 在标准假设下具有与 Adam 相似的 Regret 边界。

class RMSProp:
    """RMSProp 实现"""
    def __init__(self, params, lr=1e-3, alpha=0.99, eps=1e-8):
        self.params = list(params)
        self.lr = lr
        self.alpha = alpha
        self.eps = eps
        self.v = [torch.zeros_like(p) for p in self.params]
    
    def step(self):
        for i, p in enumerate(self.params):
            if p.grad is None:
                continue
            g = p.grad.data
            self.v[i] = self.alpha * self.v[i] + (1 - self.alpha) * g.pow(2)
            p.data -= self.lr * g / (self.v[i].sqrt() + self.eps)

5.3 AdaBound

AdaBound 通过动态裁剪学习率结合了 Adam 和 SGD 的优点:14

其中 随时间收敛到 ,使 AdaBound 渐近等价于 SGD。

class AdaBound:
    """AdaBound 实现"""
    def __init__(self, params, lr=1e-3, beta1=0.9, beta2=0.999, 
                 final_lr=0.1, gamma=1e-3):
        self.params = list(params)
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.final_lr = final_lr
        self.gamma = gamma
        self.eps = 1e-8
        self.m = [torch.zeros_like(p) for p in self.params]
        self.v = [torch.zeros_like(p) for p in self.params]
        self.t = 0
    
    def step(self):
        self.t += 1
        # 动态学习率边界
        lower = self.final_lr * (1 - 1 / (self.gamma * self.t + 1))
        upper = self.final_lr * (1 + 1 / (self.gamma * self.t))
        
        for i, p in enumerate(self.params):
            if p.grad is None:
                continue
            g = p.grad.data
            
            # Adam 风格的动量更新
            self.m[i] = self.beta1 * self.m[i] + (1 - self.beta1) * g
            self.v[i] = self.beta2 * self.v[i] + (1 - self.beta2) * g.pow(2)
            
            # 偏差校正
            m_hat = self.m[i] / (1 - self.beta1 ** self.t)
            v_hat = self.v[i] / (1 - self.beta2 ** self.t)
            
            # 动态裁剪学习率
            eta_t = self.lr / (np.sqrt(self.t) if self.t < 100 else self.lr)
            clipped_lr = torch.clamp(eta_t, lower, upper)
            
            p.data -= clipped_lr * m_hat / (v_hat.sqrt() + self.eps)

5.4 Lion 优化器

Lion(EvoLved Sign Momentum)是 Chen 等人提出的新型优化器:15

关键创新:使用 代替逐元素除法。

理论分析

  1. 内存效率:只存储一阶矩 ,不存储二阶矩
  2. 符号梯度的期望
  3. 更新尺度 为维度)

收敛性:Lion 在实验中表现与 Adam 相当甚至更好,但在理论上尚未有完整的 Regret 分析。

class Lion:
    """Lion (EvoLved Sign Momentum) 实现"""
    def __init__(self, params, lr=1e-3, beta1=0.9, beta2=0.99, 
                 weight_decay=0.0):
        self.params = list(params)
        self.lr = lr
        self.beta1 = beta1
        self.beta2 = beta2
        self.weight_decay = weight_decay
        self.m = [torch.zeros_like(p) for p in self.params]
    
    def step(self):
        for i, p in enumerate(self.params):
            if p.grad is None:
                continue
            g = p.grad.data
            
            # 动量更新
            self.m[i] = self.beta1 * self.m[i] + (1 - self.beta1) * g
            
            # 符号更新
            p.data -= self.lr * torch.sign(self.m[i])
            
            # 权重衰减
            if self.weight_decay > 0:
                p.data -= self.lr * self.weight_decay * p.data

5.5 优化器对比总结

优化器自适应学习率动量收敛保证适用场景
SGD+M强凸/非凸CV, 大 batch
AdaGrad凸函数稀疏特征
RMSProp经验有效RNN, 非稳态
Adam在线/凸默认选择
AdamW在线/凸Transformer
AdaBound在线/凸统一训练
Lion经验有效超大规模

6. 谱归一化与优化器的相互作用

6.1 谱归一化回顾

谱归一化约束权重矩阵的谱范数:16

通过幂迭代法估计

6.2 谱归一化对优化动态的影响

梯度效应:谱归一化改变了梯度的有效 Lipschitz 常数。

关于 的梯度为 ,则:

谱归一化使 ,简化了梯度分析。

6.3 谱归一化与权重衰减的协同

谱归一化间接控制了权重增长,但不提供显式正则化

组合策略17

实验表明,谱归一化 + 较小的权重衰减比单独使用谱归一化或权重衰减效果更好。


7. 收敛性理论速查

7.1 在线凸优化 Regret 边界

算法凸函数强凸函数
FTL
OGD
AdaGrad
Adam(需条件)
OMD

7.2 非凸随机优化收敛率

算法收敛率条件
SGD有界方差
SVRG有限和
Adam有界假设
AdaGrad稀疏梯度

7.3 关键公式汇总

Adam 更新

AdamW 更新

Lion 更新


8. 代码实现:综合对比

import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
from abc import ABC, abstractmethod
 
class Optimizer(ABC):
    """优化器基类"""
    @abstractmethod
    def step(self):
        pass
 
class SGDWithMomentum(Optimizer):
    """SGD with Momentum"""
    def __init__(self, params, lr=0.01, momentum=0.9, weight_decay=0.0):
        self.params = list(params)
        self.lr = lr
        self.momentum = momentum
        self.weight_decay = weight_decay
        self.v = [torch.zeros_like(p) for p in self.params]
    
    def step(self):
        for i, p in enumerate(self.params):
            if p.grad is None:
                continue
            g = p.grad.data
            
            # 动量更新
            self.v[i] = self.momentum * self.v[i] + g
            
            # 权重衰减
            if self.weight_decay > 0:
                p.data -= self.lr * self.weight_decay * p.data
            
            # 参数更新
            p.data -= self.lr * self.v[i]
 
 
class Adam(Optimizer):
    """Adam Optimizer"""
    def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), 
                 eps=1e-8, weight_decay=0.0, use_adamw=False):
        self.params = list(params)
        self.lr = lr
        self.beta1, self.beta2 = betas
        self.eps = eps
        self.weight_decay = weight_decay
        self.use_adamw = use_adamw
        self.t = 0
        
        self.m = [torch.zeros_like(p) for p in self.params]
        self.v = [torch.zeros_like(p) for p in self.params]
    
    def step(self):
        self.t += 1
        for i, p in enumerate(self.params):
            if p.grad is None:
                continue
            g = p.grad.data
            
            # 权重衰减(L2 正则化,非 AdamW)
            if self.weight_decay > 0 and not self.use_adamw:
                g = g + self.weight_decay * p.data
            
            # 一阶矩估计
            self.m[i] = self.beta1 * self.m[i] + (1 - self.beta1) * g
            
            # 二阶矩估计
            self.v[i] = self.beta2 * self.v[i] + (1 - self.beta2) * g.pow(2)
            
            # 偏差校正
            m_hat = self.m[i] / (1 - self.beta1 ** self.t)
            v_hat = self.v[i] / (1 - self.beta2 ** self.t)
            
            # 参数更新
            p.data -= self.lr * m_hat / (v_hat.sqrt() + self.eps)
            
            # AdamW: 权重衰减在自适应更新之后
            if self.weight_decay > 0 and self.use_adamw:
                p.data -= self.lr * self.weight_decay * p.data
 
 
class Lion(Optimizer):
    """Lion Optimizer"""
    def __init__(self, params, lr=1e-3, betas=(0.9, 0.99), 
                 weight_decay=0.0):
        self.params = list(params)
        self.lr = lr
        self.beta1, self.beta2 = betas
        self.weight_decay = weight_decay
        self.m = [torch.zeros_like(p) for p in self.params]
    
    def step(self):
        for i, p in enumerate(self.params):
            if p.grad is None:
                continue
            g = p.grad.data
            
            # 动量更新
            self.m[i] = self.beta1 * self.m[i] + (1 - self.beta1) * g
            
            # 符号更新
            p.data -= self.lr * torch.sign(self.m[i])
            
            # 权重衰减
            if self.weight_decay > 0:
                p.data -= self.lr * self.weight_decay * p.data
 
 
# 收敛性验证实验
def verify_convergence(optimizer_class, optimizer_kwargs, n_steps=1000):
    """
    验证优化器在简单凸函数上的收敛性
    
    目标函数: f(x) = (x - 3)^2
    最优解: x = 3
    """
    x = nn.Parameter(torch.tensor([0.0]))
    opt = optimizer_class([x], **optimizer_kwargs)
    
    losses = []
    for _ in range(n_steps):
        loss = (x - 3) ** 2
        losses.append(loss.item())
        
        opt.zero_grad()
        loss.backward()
        opt.step()
    
    return losses, x.item()
 
 
# 收敛速度对比
if __name__ == "__main__":
    # 简单二次函数收敛测试
    optimizers = {
        "SGD+M": (SGDWithMomentum, {"lr": 0.1, "momentum": 0.9}),
        "Adam": (Adam, {"lr": 0.1}),
        "AdamW": (Adam, {"lr": 0.1, "weight_decay": 0.1},),
        "Lion": (Lion, {"lr": 0.1}),
    }
    
    results = {}
    for name, (opt_class, kwargs) in optimizers.items():
        losses, final_x = verify_convergence(opt_class, kwargs)
        results[name] = {
            "final_loss": losses[-1],
            "final_x": final_x,
            "converged": losses[-1] < 1e-6
        }
        print(f"{name}: loss={losses[-1]:.6f}, x={final_x:.6f}, "
              f"converged={results[name]['converged']}")

参考


相关文章

Footnotes

  1. Zinkevich, M. (2003). “Online Convex Programming and Generalized Infinitesimal Gradient Ascent”. ICML 2003.

  2. Ghadimi, S., & Lan, G. (2013). “Stochastic First- and Zero-Order Methods for Nonconvex Optimization”. SIAM Journal on Optimization.

  3. Mukkamala, M.C., et al. (2017). “Convergence Analysis of Adam under Bounded Gradients”. NeurIPS 2017 Workshop.

  4. Kingma, D.P., & Ba, J. (2015). “Adam: A Method for Stochastic Optimization”. ICLR 2015.

  5. Reddi, S.J., et al. (2018). “On the Convergence of Adam and Beyond”. ICLR 2018. 2

  6. Chen, X., et al. (2019). “Theoretical Analysis of Adam Algorithm”. arXiv:1901.10456.

  7. Loshchilov, I., & Hutter, F. (2019). “Decoupled Weight Decay Regularization”. ICLR 2019.

  8. Wu, Y., et al. (2020). “On the Convergence of AdamW”. arXiv:2007.05345.

  9. Sutskever, I., et al. (2013). “On the Importance of Initialization and Momentum in Deep Learning”. ICML 2013.

  10. Johnson, R., & Zhang, T. (2013). “Accelerating Stochastic Gradient Descent using Predictive Variance Reduction”. NeurIPS 2013.

  11. Defazio, A., et al. (2014). “SAGA: A Fast Incremental Gradient Method with Support for Non-Strongly Convex Composite Objectives”. NeurIPS 2014.

  12. Duchi, J., Hazan, E., & Singer, Y. (2011). “Adaptive Subgradient Methods for Online Learning and Stochastic Optimization”. JMLR.

  13. Tieleman, T., & Hinton, G. (2012). “Lecture 6.5 - RMSProp”. Coursera.

  14. Luo, L., et al. (2019). “AdaBound: An Adaptive Learning Rate Method”. ICLR 2019.

  15. Chen, X., et al. (2023). “Symbolic Discovery of Optimization Algorithms”. ICML 2023.

  16. Miyato, T., et al. (2018). “Spectral Normalization for Generative Adversarial Networks”. ICLR 2018.

  17. Yoshida, Y., et al. (2017). “Spectral Normalization and SGD for Training GANs”. NeurIPS Workshop.