Skip to content

필사 모드: 机器学习的数学优化:从 Adam 到凸优化、ZeRO optimizer

中文
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

目录

  1. 优化基础:凸优化与 KKT 条件
  2. 梯度下降系列 optimizer
  3. 二阶优化方法
  4. 学习率调度
  5. 正则化技巧
  6. 损失函数设计
  7. LLM 训练优化
  8. 测验

优化基础

凸优化 (Convex Optimization)

函数 f:RnRf: \mathbb{R}^n \to \mathbb{R} 若为(convex)函数,则对任意两点 x,yx, yλ[0,1]\lambda \in [0,1],以下不等式成立。

f(λx+(1λ)y)λf(x)+(1λ)f(y)f(\lambda x + (1-\lambda)y) \leq \lambda f(x) + (1-\lambda)f(y)

凸函数的核心性质:

  • 任何局部最小值(local minimum)都是全局最小值(global minimum)
  • 梯度下降法的收敛得到保证
  • 深度学习的损失函数大多是非凸(non-convex)的,但凸分析技巧依然有用

强凸(Strongly Convex):若存在 m>0m > 0,使得 f(y)f(x)+f(x)T(yx)+m2yx2f(y) \geq f(x) + \nabla f(x)^T(y-x) + \frac{m}{2}\|y-x\|^2 成立,则收敛速度会加快为线性收敛(linear convergence)。

拉格朗日乘子法

用于处理等式约束的优化问题。

minxf(x)subject togi(x)=0,  i=1,,m\min_x f(x) \quad \text{subject to} \quad g_i(x) = 0, \; i = 1, \ldots, m

拉格朗日函数(Lagrangian):

L(x,λ)=f(x)+i=1mλigi(x)\mathcal{L}(x, \lambda) = f(x) + \sum_{i=1}^{m} \lambda_i g_i(x)

在最优解处,xL=0\nabla_x \mathcal{L} = 0λL=0\nabla_\lambda \mathcal{L} = 0 成立。

KKT 条件

包含不等式约束在内的一般化优化问题:

minxf(x)s.t.gi(x)0,  hj(x)=0\min_x f(x) \quad \text{s.t.} \quad g_i(x) \leq 0, \; h_j(x) = 0

KKT 条件(必要条件):

  1. 稳定性(Stationarity)f(x)+iμigi(x)+jλjhj(x)=0\nabla f(x^*) + \sum_i \mu_i \nabla g_i(x^*) + \sum_j \lambda_j \nabla h_j(x^*) = 0
  2. 原始可行性(Primal feasibility)gi(x)0g_i(x^*) \leq 0hj(x)=0h_j(x^*) = 0
  3. 对偶可行性(Dual feasibility)μi0\mu_i \geq 0
  4. 互补松弛性(Complementary slackness)μigi(x)=0\mu_i g_i(x^*) = 0

在凸问题中,KKT 条件同时也是充分条件。

鞍点 (Saddle Point)

在深度学习优化中,比局部最小值更大的问题是鞍点。在鞍点处,某些方向上函数值会增大,另一些方向上会减小,导致梯度为零。SGD 的随机噪声有助于逃离鞍点。


梯度下降系列

SGD 及其变体

基础 SGD

θt+1=θtηθL(θt;xi,yi)\theta_{t+1} = \theta_t - \eta \nabla_\theta \mathcal{L}(\theta_t; x_i, y_i)

带动量的 SGD (SGD with Momentum)

vt+1=βvt+θL(θt)v_{t+1} = \beta v_t + \nabla_\theta \mathcal{L}(\theta_t) θt+1=θtηvt+1\theta_{t+1} = \theta_t - \eta v_{t+1}

动量 β=0.9\beta = 0.9 是常用取值,它保留之前的梯度方向,从而减少震荡(oscillation)。

Nesterov 加速梯度 (Nesterov Accelerated Gradient, NAG)

vt+1=βvt+θL(θtβvt)v_{t+1} = \beta v_t + \nabla_\theta \mathcal{L}(\theta_t - \beta v_t) θt+1=θtηvt+1\theta_{t+1} = \theta_t - \eta v_{t+1}

不是在当前位置,而是在"向前看"的位置上计算梯度。

AdaGrad、RMSProp、Adam

AdaGrad:按参数自适应学习率

Gt=Gt1+gt2G_t = G_{t-1} + g_t^2 θt+1=θtηGt+ϵgt\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{G_t + \epsilon}} g_t

经常出现的特征学习率会变小,稀少出现的特征学习率会较大。缺点:学习率单调递减,可能导致训练停滞。

RMSProp:解决 AdaGrad 的累积问题

vt=βvt1+(1β)gt2v_t = \beta v_{t-1} + (1-\beta) g_t^2 θt+1=θtηvt+ϵgt\theta_{t+1} = \theta_t - \frac{\eta}{\sqrt{v_t + \epsilon}} g_t

Adam(自适应矩估计,Adaptive Moment Estimation)

mt=β1mt1+(1β1)gt(一阶矩)m_t = \beta_1 m_{t-1} + (1-\beta_1) g_t \quad \text{(一阶矩)} vt=β2vt1+(1β2)gt2(二阶矩)v_t = \beta_2 v_{t-1} + (1-\beta_2) g_t^2 \quad \text{(二阶矩)}

偏差校正(Bias correction):

m^t=mt1β1t,v^t=vt1β2t\hat{m}_t = \frac{m_t}{1 - \beta_1^t}, \quad \hat{v}_t = \frac{v_t}{1 - \beta_2^t}

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

默认超参数:β1=0.9\beta_1 = 0.9β2=0.999\beta_2 = 0.999ϵ=108\epsilon = 10^{-8}

import torch
import torch.optim as optim

model = ...  # 定义模型

# Adam optimizer
optimizer_adam = optim.Adam(
    model.parameters(),
    lr=1e-3,
    betas=(0.9, 0.999),
    eps=1e-8
)

# AdamW optimizer (分离 weight decay)
optimizer_adamw = optim.AdamW(
    model.parameters(),
    lr=1e-3,
    betas=(0.9, 0.999),
    eps=1e-8,
    weight_decay=0.01  # 独立于 L2 应用
)

AdamW 与 Lion

AdamW:在 Adam 的基础上,不把 weight decay 作为 L2 惩罚项,而是直接应用在参数更新上。

θt+1=θtη(m^tv^t+ϵ+λθt)\theta_{t+1} = \theta_t - \eta \left(\frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} + \lambda \theta_t\right)

这在数学上与普通 Adam 的 L2 正则化并不等价(详细说明见测验)。

Lion(EvoLved Sign Momentum)

ut=β1mt1+(1β1)gtu_t = \beta_1 m_{t-1} + (1-\beta_1) g_t θt+1=θtηsign(ut)\theta_{t+1} = \theta_t - \eta \cdot \text{sign}(u_t) mt=β2mt1+(1β2)gtm_t = \beta_2 m_{t-1} + (1-\beta_2) g_t

Lion 只使用符号(sign),因此内存效率高,更新幅度也保持均匀。

Optimizer内存收敛速度适合场景
SGD+Momentum计算机视觉、大批量
Adam中等NLP、通用场景
AdamW中等Transformer 训练
Lion大规模模型
L-BFGS非常快小规模模型

二阶优化

Newton 方法

利用二阶导数(Hessian)。

θt+1=θtHt1f(θt)\theta_{t+1} = \theta_t - H_t^{-1} \nabla f(\theta_t)

这里 Ht=2f(θt)H_t = \nabla^2 f(\theta_t) 是 Hessian 矩阵。虽然具有二次收敛(quadratic convergence),但 n×nn \times n Hessian 矩阵求逆的计算量为 O(n3)O(n^3),在深度学习中并不现实。

L-BFGS (Limited-memory BFGS)

不直接存储 Hessian,而是用最近 mm 个梯度差来近似。

Ht1用两个向量序列 {sk},{yk} 近似H_t^{-1} \approx \text{用两个向量序列 } \{s_k\}, \{y_k\}\text{ 近似}

其中 sk=θk+1θks_k = \theta_{k+1} - \theta_kyk=fk+1fky_k = \nabla f_{k+1} - \nabla f_k

import torch
import torch.optim as optim

# L-BFGS 需要闭包(closure)函数
optimizer = optim.LBFGS(
    model.parameters(),
    lr=1.0,
    max_iter=20,
    history_size=10,
    line_search_fn='strong_wolfe'
)

def closure():
    optimizer.zero_grad()
    output = model(input_data)
    loss = criterion(output, target)
    loss.backward()
    return loss

optimizer.step(closure)

自然梯度下降法 (Natural Gradient)

利用 Fisher 信息矩阵,考虑参数空间的曲率。

θt+1=θtηF(θt)1L(θt)\theta_{t+1} = \theta_t - \eta F(\theta_t)^{-1} \nabla \mathcal{L}(\theta_t)

Fisher 矩阵:F(θ)=E[logp(yx;θ)logp(yx;θ)T]F(\theta) = \mathbb{E}\left[\nabla \log p(y|x;\theta) \nabla \log p(y|x;\theta)^T\right]

K-FAC(Kronecker-factored Approximate Curvature)是自然梯度法的一种实用实现。


学习率调度

Warmup

在训练初期逐步提高学习率,使训练更稳定。

ηt=ηmaxtTwarmup(tTwarmup)\eta_t = \eta_{\max} \cdot \frac{t}{T_{\text{warmup}}} \quad (t \leq T_{\text{warmup}})

Cosine Annealing

ηt=ηmin+12(ηmaxηmin)(1+cosπtT)\eta_t = \eta_{\min} + \frac{1}{2}(\eta_{\max} - \eta_{\min})\left(1 + \cos\frac{\pi t}{T}\right)

from torch.optim.lr_scheduler import CosineAnnealingLR, OneCycleLR, ReduceLROnPlateau

# Cosine Annealing
scheduler = CosineAnnealingLR(optimizer, T_max=100, eta_min=1e-6)

# OneCycleLR: Warmup + Cosine Decay
scheduler = OneCycleLR(
    optimizer,
    max_lr=1e-3,
    total_steps=1000,
    pct_start=0.3,         # 30% warmup
    anneal_strategy='cos'
)

# ReduceLROnPlateau: 验证损失不再改善时降低学习率
scheduler = ReduceLROnPlateau(
    optimizer,
    mode='min',
    factor=0.5,
    patience=10,
    min_lr=1e-6
)

Cyclical Learning Rate (CLR)

周期性地变动学习率,帮助逃离鞍点。

ηt=ηmin+(ηmaxηmin)max(0,1tstep_size2k+1)\eta_t = \eta_{\min} + (\eta_{\max} - \eta_{\min}) \cdot \max\left(0, 1 - \left|\frac{t}{\text{step\_size}} - 2k + 1\right|\right)

调度器特点适合场景
Cosine Annealing平滑衰减Transformer 预训练
OneCycleLRWarmup + 快速衰减微调、短周期训练
ReduceLROnPlateau自适应常规训练、需要验证
Cyclical LR周期性变动逃离鞍点
Linear Warmup初期稳定化LLM 训练

正则化技巧

L1 / L2 正则化

L2 正则化 (Ridge)

Lreg=L+λ2θ22\mathcal{L}_{\text{reg}} = \mathcal{L} + \frac{\lambda}{2} \|\theta\|_2^2

梯度:θLreg=θL+λθ\nabla_\theta \mathcal{L}_{\text{reg}} = \nabla_\theta \mathcal{L} + \lambda \theta

L1 正则化 (Lasso)

Lreg=L+λθ1\mathcal{L}_{\text{reg}} = \mathcal{L} + \lambda \|\theta\|_1

L1 会诱导出稀疏(sparse)解。

Batch Normalization 与 Layer Normalization

Batch Normalization (BN)

x^i=xiμBσB2+ϵγ+β\hat{x}_i = \frac{x_i - \mu_B}{\sqrt{\sigma_B^2 + \epsilon}} \cdot \gamma + \beta

这里 μB\mu_BσB2\sigma_B^2 是小批量(mini-batch)内的统计量。沿批量方向做归一化。

Layer Normalization (LN)

x^=xμLσL2+ϵγ+β\hat{x} = \frac{x - \mu_L}{\sqrt{\sigma_L^2 + \epsilon}} \cdot \gamma + \beta

这里的统计量是沿每个样本的特征维度计算的。

归一化统计量计算方向适合场景
Batch Norm批量方向(相同特征)CNN、大批量
Layer Norm特征方向(相同样本)Transformer、RNN
Instance Norm空间方向(相同通道)风格迁移
Group Norm通道分组小批量

Weight Decay 与 L2 正则化

在 SGD 中:

θt+1=θtη(L+λθt)=(1ηλ)θtηL\theta_{t+1} = \theta_t - \eta(\nabla \mathcal{L} + \lambda \theta_t) = (1 - \eta\lambda)\theta_t - \eta \nabla \mathcal{L}

在这种情况下,weight decay 与 L2 正则化是等价的。但在 Adam 中:

  • L2 Adam:把 λθ\lambda \theta 加到梯度上之后,再应用自适应学习率 → 被自适应系数相除,正则化效果被削弱
  • AdamW:在参数更新后直接减去 λθ\lambda \theta → 对所有参数施加均等的 weight decay
# Dropout
import torch.nn as nn

class RegularizedModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(512, 256)
        self.bn1 = nn.BatchNorm1d(256)
        self.ln1 = nn.LayerNorm(256)
        self.dropout = nn.Dropout(p=0.3)

    def forward(self, x):
        x = self.fc1(x)
        x = self.bn1(x)   # 或使用 self.ln1(x)
        x = torch.relu(x)
        x = self.dropout(x)
        return x

损失函数设计

Cross-Entropy Loss

LCE=c=1Cyclogp^c\mathcal{L}_{CE} = -\sum_{c=1}^{C} y_c \log \hat{p}_c

二分类:LBCE=[ylogp+(1y)log(1p)]\mathcal{L}_{BCE} = -[y \log p + (1-y)\log(1-p)]

Focal Loss

用于解决类别不平衡问题。降低易分类样本的贡献。

LFL=(1pt)γlog(pt)\mathcal{L}_{FL} = -(1-p_t)^\gamma \log(p_t)

这里 ptp_t 是正确类别的预测概率,γ0\gamma \geq 0 是 focusing parameter。当 γ=0\gamma = 0 时,等价于普通的 Cross-Entropy。

import torch
import torch.nn.functional as F

class FocalLoss(torch.nn.Module):
    def __init__(self, alpha=0.25, gamma=2.0):
        super().__init__()
        self.alpha = alpha
        self.gamma = gamma

    def forward(self, logits, targets):
        bce_loss = F.binary_cross_entropy_with_logits(
            logits, targets.float(), reduction='none'
        )
        p = torch.sigmoid(logits)
        p_t = p * targets + (1 - p) * (1 - targets)
        focal_weight = (1 - p_t) ** self.gamma
        alpha_t = self.alpha * targets + (1 - self.alpha) * (1 - targets)
        loss = alpha_t * focal_weight * bce_loss
        return loss.mean()

Contrastive Loss 与 Triplet Loss

Contrastive Loss(Siamese Network):

L=(1y)d22+ymax(0,md)2\mathcal{L} = (1-y)\frac{d^2}{2} + y \cdot \max(0, m - d)^2

这里 d=f(x1)f(x2)2d = \|f(x_1) - f(x_2)\|_2y=0y=0 表示相似对,y=1y=1 表示不相似对。

Triplet Loss

Ltrip=max(0,f(a)f(p)22f(a)f(n)22+m)\mathcal{L}_{trip} = \max(0, \|f(a) - f(p)\|_2^2 - \|f(a) - f(n)\|_2^2 + m)

使用锚点(anchor, a)、正样本(positive, p)、负样本(negative, n)。

InfoNCE Loss (NT-Xent)

对比学习(Contrastive Learning)的核心损失函数。

LInfoNCE=logexp(sim(zi,zj)/τ)k=12N1kiexp(sim(zi,zk)/τ)\mathcal{L}_{InfoNCE} = -\log \frac{\exp(\text{sim}(z_i, z_j)/\tau)}{\sum_{k=1}^{2N} \mathbf{1}_{k \neq i} \exp(\text{sim}(z_i, z_k)/\tau)}

这里 τ\tau 是 temperature parameter,sim\text{sim} 是余弦相似度。

import torch
import torch.nn.functional as F

def info_nce_loss(features, temperature=0.07):
    """
    features: (2N, D) - 每张图像的两个 augmentation view
    """
    N = features.shape[0] // 2
    features = F.normalize(features, dim=1)

    # 计算相似度矩阵
    similarity = torch.matmul(features, features.T) / temperature

    # 去除自身相似度(将对角线设为 -inf)
    mask = torch.eye(2 * N, dtype=torch.bool, device=features.device)
    similarity.masked_fill_(mask, float('-inf'))

    # 正样本对: i 与 i+N,i+N 与 i
    labels = torch.cat([
        torch.arange(N, 2 * N),
        torch.arange(N)
    ]).to(features.device)

    loss = F.cross_entropy(similarity, labels)
    return loss

LLM 训练优化

梯度裁剪 (Gradient Clipping)

防止梯度爆炸(exploding gradient)。

ggmin(1,clip_normg2)g \leftarrow g \cdot \min\left(1, \frac{\text{clip\_norm}}{\|g\|_2}\right)

import torch

def train_with_clipping(model, optimizer, loss, max_norm=1.0):
    optimizer.zero_grad()
    loss.backward()

    # 监控梯度范数
    total_norm = 0
    for p in model.parameters():
        if p.grad is not None:
            param_norm = p.grad.data.norm(2)
            total_norm += param_norm.item() ** 2
    total_norm = total_norm ** 0.5

    # 应用裁剪
    torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=max_norm)
    optimizer.step()
    return total_norm

ZeRO Optimizer (Zero Redundancy Optimizer)

模型训练时分三个阶段优化内存。

ZeRO 阶段分片对象内存节省
Stage 1Optimizer 状态~4x
Stage 2+ 梯度~8x
Stage 3+ 参数~64x(以 N 张 GPU 为基准)

混合精度(mixed precision) + ZeRO-3 可以让数十亿参数量级的模型在单节点上完成训练。

8-bit Adam

通过量化(quantization),把 optimizer 状态以 INT8 而非 FP32 存储。

  • optimizer 状态内存节省 75%(相较于 FP32)
  • 通过 block-wise quantization 将精度损失降到最低
  • 可通过 bitsandbytes 库实现
# bitsandbytes 8-bit Adam
import bitsandbytes as bnb

optimizer = bnb.optim.Adam8bit(
    model.parameters(),
    lr=1e-4,
    betas=(0.9, 0.999)
)

Adafactor

用矩阵分解近似 Adam 的二阶矩。

Vtr^tv^tT(rank-1 近似)V_t \approx \hat{r}_t \hat{v}_t^T \quad (\text{rank-1 近似})

只使用与参数量成比例的内存(行向量 + 列向量)。被用于 T5、PaLM 等超大规模模型的训练。

Optimizer内存(相对于参数量的倍数)LLM 适配度
Adam8x(params + 2 states)一般
AdamW8x
8-bit Adam6x
Adafactor~2x非常好
Lion6x

测验

Q1. 为什么 Adam optimizer 需要 bias correction?

答案:为了校正初始矩估计值因零初始化而产生的偏差。

说明:Adam 中若把 m0=0m_0 = 0v0=0v_0 = 0 初始化,则在初期时间步 tt 上,mtm_tvtv_t 会低估真实梯度的矩。举例来说,在 t=1t=1m1=(1β1)g1m_1 = (1-\beta_1)g_1,而其真实期望值 E[m1]=(1β1)E[g1](1β1)g1\mathbb{E}[m_1] = (1-\beta_1)\mathbb{E}[g_1] \approx (1-\beta_1) g_1,要把它还原为 E[g1]\mathbb{E}[g_1] 就必须乘以 1/(1β1)1/(1-\beta_1)。当 β1=0.9\beta_1 = 0.9 时,在 t=1t=1 处会被校正为 m^1=m1/0.1=10m1\hat{m}_1 = m_1 / 0.1 = 10 \cdot m_1。当 tt 足够大时,β1t0\beta_1^t \to 0,校正系数收敛到 1,效果随之消失。

Q2. 为什么 Weight Decay 和 L2 正则化在 Adam 中并不等价?

答案:因为 Adam 的自适应学习率会对 L2 惩罚项的梯度进行缩放。

说明:在 SGD 中,θθη(L+λθ)\theta \leftarrow \theta - \eta(\nabla \mathcal{L} + \lambda \theta),两种方式在数学上是等价的。但在 Adam 中加入 L2 正则化后,梯度会变为 gt+λθtg_t + \lambda\theta_t,再被自适应缩放系数 1/v^t1/\sqrt{\hat{v}_t} 相除。因此权重较大(即 vtv_t 较大)的参数,L2 惩罚也会随之变小。AdamW 把 weight decay 从自适应缩放中剥离出来,改为 θθ(1ηλ)ηm^t/(v^t+ϵ)\theta \leftarrow \theta(1-\eta\lambda) - \eta\hat{m}_t/(\sqrt{\hat{v}_t}+\epsilon) 的形式处理,从而对所有参数施加均等的正则化。

Q3. Batch Normalization 与 Layer Normalization 的区别,以及各自适合的场景是?

答案:BN 在批量维度上归一化,LN 在特征维度上归一化。

说明:BN 用小批量内相同特征(神经元)的均值/方差做归一化。因此依赖批量大小,批量较小时统计量的估计会不稳定。它适合像 CNN 那样具有空间特征、且批量足够大的场景。LN 是沿每个样本的特征维度做归一化,因此与批量大小无关。它适合像 Transformer 那样序列长度可变,或像 RNN 那样难以维持批量统计量的场景。推理时也不需要批量统计量,因此对在线推理更有利。

Q4. Focal Loss 在类别不平衡问题上比 Cross-Entropy 更有效的数学原理是什么?

答案:因为 (1pt)γ(1-p_t)^\gamma 权重会动态降低易分类样本的贡献。

说明:普通 CE 损失是 log(pt)-\log(p_t),容易分类的多数类样本也会做出同等的贡献。观察 Focal Loss 的 (1pt)γ(1-p_t)^\gamma 项,当 pt=0.9p_t = 0.9(易分类样本)时,(10.9)2=0.01(1-0.9)^2 = 0.01,权重变得非常小。相反,当 pt=0.1p_t = 0.1(难分类样本)时,(10.1)2=0.81(1-0.1)^2 = 0.81,几乎维持原样。使用 γ=2\gamma = 2 时,易分类样本的损失会减小到 100 分之一。由此,模型得以集中学习难分类的少数类样本。

Q5. InfoNCE Loss 在对比学习中学到良好表示的原理是什么?

答案:让同一图像的不同 augmentation 对趋于相似,让不同图像趋于疏远,以此进行学习。

说明:InfoNCE 最大化互信息(mutual information)的下界。分子 exp(sim(zi,zj)/τ)\exp(\text{sim}(z_i, z_j)/\tau) 提高正样本对(同一图像的两个 view)之间的相似度,分母则包含 2N12N-1 个负样本对。Temperature τ\tau 用于调节分布的尖锐程度。τ\tau 越小,竞争越激烈,表示空间也就越具区分性。在大批量下提供多样的负样本,能让表示更具泛化性。SimCLR、MoCo、CLIP 等主要的对比学习模型都使用这一损失函数。

현재 단락 (1/264)

1. [优化基础:凸优化与 KKT 条件](#优化基础)

작성 글자: 0원문 글자: 11,055작성 단락: 0/264