优化器对比(SGD、Adam、AdamW、Lion)
一句话概述
优化器是深度学习的"引擎"——决定了参数如何根据梯度进行更新。SGD(随机梯度下降)是最基础的优化器,简单但收敛慢;SGD+Momentum加入"惯性"(动量项),像滚下坡的球越滚越快;Adam(2014年)结合了Momentum和RMSProp的自适应学习率,成为最通用的优化器——每个参数都有独立的自适应学习率;AdamW(2017年)修正了Adam中权重衰减的实现错误,将L2正则化和自适应学习率解耦,显著提升了泛化性能;Lion(2023年,Google)简化了Adam的设计——只使用符号梯度+动量+权重衰减,在部分任务上超越AdamW且更省显存。选择优化器的核心原则:简单任务用SGD+Momentum(泛化好但需要仔细调参),通用任务用AdamW(默认推荐),大模型探索Lion(收敛快、省显存)。
💡 核心要点:①SGD+Momentum加入惯性项加速收敛,泛化性好但需要精细调参 ②Adam自适应学习率,每个参数独立调整,收敛快但可能泛化差 ③AdamW修正了权重衰减的实现,是对Adam的重要改进,推荐默认使用 ④Lion简化设计只用符号梯度,在大模型和视觉任务上表现出色 ⑤优化器选择要根据任务规模和类型:小任务SGD、通用AdamW、大模型Lion
教学与演示
一、从SGD到SGD+Momentum:速度的进化
是什么(定义):SGD参数更新公式为θ_t+1 = θ_t - η·g_t,每次只沿梯度的反方向走固定步长。这种"一步一个脚印"的方式在高曲率方向(狭长山谷形的损失面)上效率极低——梯度在窄方向上反复震荡,在宽方向上进展缓慢。SGD+Momentum引入动量项v_t:v_t = β·v_{t-1} + g_t,θ_{t+1} = θ_t - η·v_t。动量像是"惯性"——累积了历史梯度方向,在震荡方向相互抵消,在一致方向加速前进。典型β=0.9。
大白话 SGD像一个人在浓雾中走下山——每走一步就停下来重新判断方向,走得慢而且在狭窄的"沟里"左右摇摆。SGD+Momentum像是推着一个有惯性的大球下山——球会"记住"之前的运动方向,在一直向下的方向上越滚越快,在左右晃动的方向上自然抵消。这样过沟的速度就快多了。
为什么(原理):损失函数在参数空间中通常有"狭长山谷"——不同方向的曲率差异很大(Hessian矩阵的条件数大)。沿高曲率方向梯度大但方向频繁翻转(震荡),沿低曲率方向梯度小但方向一致(需要走很远离最优)。动量通过指数移动平均(EMA)平滑了梯度方向:震荡方向的梯度正负交替→被平均消掉;一致方向的梯度不断叠加→加速前进。
import numpy as np
# SGD vs SGD+Momentum 的对比实现
# 在一个狭长山谷形的损失面上演示两者的差异
def rosenbrock_loss(x, y, a=1, b=100):
"""Rosenbrock函数(香蕉函数)——狭长山谷形的经典测试函数"""
return (a - x) ** 2 + b * (y - x ** 2) ** 2
def rosenbrock_grad(x, y, a=1, b=100):
"""Rosenbrock函数的梯度"""
grad_x = -2 * (a - x) - 4 * b * x * (y - x ** 2)
grad_y = 2 * b * (y - x ** 2)
return np.array([grad_x, grad_y])
# 优化器实现
def sgd_step(params, grads, lr):
return params - lr * grads
class SGDMomentum:
"""带Momentum的SGD"""
def __init__(self, lr, momentum=0.9):
self.lr = lr
self.momentum = momentum
self.velocity = None
def step(self, params, grads):
if self.velocity is None:
self.velocity = np.zeros_like(params)
self.velocity = self.momentum * self.velocity + grads
return params - self.lr * self.velocity
# 对比两种优化器在Rosenbrock函数上的收敛轨迹
np.random.seed(42)
init_x, init_y = -1.5, 2.0
total_steps = 2000
# SGD
params_sgd = np.array([init_x, init_y], dtype=float)
trajectory_sgd = [params_sgd.copy()]
for t in range(total_steps):
grads = rosenbrock_grad(*params_sgd)
params_sgd = sgd_step(params_sgd, grads, lr=0.002)
trajectory_sgd.append(params_sgd.copy())
# SGD + Momentum
params_mom = np.array([init_x, init_y], dtype=float)
optimizer_mom = SGDMomentum(lr=0.002, momentum=0.9)
trajectory_mom = [params_mom.copy()]
for t in range(total_steps):
grads = rosenbrock_grad(*params_mom)
params_mom = optimizer_mom.step(params_mom, grads)
trajectory_mom.append(params_mom.copy())
print("=== SGD vs SGD+Momentum:Rosenbrock函数优化 ===\n")
print(f"初始位置: ({init_x}, {init_y})")
print(f"最优位置: (1, 1)\n")
# 对比收敛速度
for name, traj in [("SGD", trajectory_sgd), ("SGD+Momentum", trajectory_mom)]:
final_pos = traj[-1]
final_loss = rosenbrock_loss(*final_pos)
dist_to_opt = np.linalg.norm(final_pos - np.array([1.0, 1.0]))
print(f" {name}:")
print(f" 最终位置: ({final_pos[0]:.6f}, {final_pos[1]:.6f})")
print(f" 最终损失: {final_loss:.2e}")
print(f" 距离最优: {dist_to_opt:.2e}")
# 对比振荡程度(用y方向的方差衡量)
sgd_y_std = np.std([p[1] for p in trajectory_sgd[-500:]])
mom_y_std = np.std([p[1] for p in trajectory_mom[-500:]])
print(f"\n 最后500步的y方向振荡标准差:")
print(f" SGD: {sgd_y_std:.4f}")
print(f" SGD+Momentum: {mom_y_std:.4f}")
print(f" 振荡减少: {(1 - mom_y_std / sgd_y_std) * 100:.1f}%")
print("\n→ SGD+Momentum的动量有效减少了峡谷中的横向振荡")
print("→ 动量在向下方向加速,在震荡方向平滑")
二、Adam与AdamW:自适应学习率的王者
是什么(定义):Adam(Adaptive Moment Estimation)由Kingma和Ba于2014年提出,结合了Momentum(一阶矩估计m_t)和RMSProp(二阶矩估计v_t)的思想。每个参数有独立的自适应学习率——梯度大的参数学习率自动变小,梯度小的参数学习率自动变大。m_t = β₁·m_{t-1} + (1-β₁)·g_t(动量),v_t = β₂·v_{t-1} + (1-β₂)·g_t²(自适应缩放),加上偏差修正后:θ_{t+1} = θ_t - η·m̂t/(√v̂_t+ε)。AdamW由Loshchilov和Hutter于2017年提出修正——Adam中权重衰减等价于L2正则化会干扰自适应学习率的计算,AdamW将权重衰减从梯度中分离:θ{t+1} = θ_t - η·(m̂_t/(√v̂_t+ε) + λ·θ_t),其中λ是权重衰减系数。
大白话 Adam可以理解为给每个参数配备了"私人教练"。有的参数需要大步快走(梯度小→学习率自动放大),有的参数需要小步慢走(梯度大→学习率自动缩小)。同时Adam还用"记忆"机制(动量)防止震荡。但Adam有个bug——它把"减肥"(权重衰减)和"吃东西的量"(梯度)混在一起算,导致减肥效果受食物影响。AdamW修正了这个bug——把减肥和吃分开,先算吃多少(自适应学习率),再独立减去一个"固定减肥量"(解耦权重衰减)。
为什么(原理):Adam成功的关键是自适应学习率。在稀疏梯度场景(如NLP中的词嵌入——只有少数词在每个batch中出现),不同参数的更新频率差异巨大。SGD对稀疏参数更新极慢(因为它们很少被更新),而Adam自动给稀疏参数分配更大的学习率。此外,Adam对超参数相对不敏感——默认设置(η=1e-3, β₁=0.9, β₂=0.999)在大多数任务上都工作良好。AdamW的改进看似微小但影响深远——解耦权重衰减让正则化强度和自适应学习率互不干扰,显著提升了泛化性能。
import numpy as np
# Adam和AdamW的完整实现与对比
# 展示自适应学习率和解耦权重衰减
class Adam:
"""Adam优化器完整实现"""
def __init__(self, lr=0.001, betas=(0.9, 0.999), eps=1e-8, weight_decay=0.01):
self.lr = lr
self.beta1, self.beta2 = betas
self.eps = eps
self.weight_decay = weight_decay
self.m = None # 一阶矩(动量)
self.v = None # 二阶矩(自适应缩放)
self.t = 0
def step(self, params, grads):
if self.m is None:
self.m = np.zeros_like(params)
self.v = np.zeros_like(params)
self.t += 1
# 更新一阶矩和二阶矩
self.m = self.beta1 * self.m + (1 - self.beta1) * grads
self.v = self.beta2 * self.v + (1 - self.beta2) * (grads ** 2)
# 偏差修正(抵消初始化偏差)
m_hat = self.m / (1 - self.beta1 ** self.t)
v_hat = self.v / (1 - self.beta2 ** self.t)
# 原始Adam:L2正则化 + 自适应学习率(耦合在一起)
# 等价于:grads_l2 = grads + weight_decay * params
update = m_hat / (np.sqrt(v_hat) + self.eps) + self.weight_decay * params
return params - self.lr * update
class AdamW:
"""AdamW优化器——修正权重衰减的实现"""
def __init__(self, lr=0.001, betas=(0.9, 0.999), eps=1e-8, weight_decay=0.01):
self.lr = lr
self.beta1, self.beta2 = betas
self.eps = eps
self.weight_decay = weight_decay
self.m = None
self.v = None
self.t = 0
def step(self, params, grads):
if self.m is None:
self.m = np.zeros_like(params)
self.v = np.zeros_like(params)
self.t += 1
# 一阶矩和二阶矩更新
self.m = self.beta1 * self.m + (1 - self.beta1) * grads
self.v = self.beta2 * self.v + (1 - self.beta2) * (grads ** 2)
# 偏差修正
m_hat = self.m / (1 - self.beta1 ** self.t)
v_hat = self.v / (1 - self.beta2 ** self.t)
# AdamW:先做自适应学习率更新,再独立做权重衰减(解耦)
update = m_hat / (np.sqrt(v_hat) + self.eps)
new_params = params - self.lr * update # 自适应更新
new_params = new_params - self.lr * self.weight_decay * params # 解耦权重衰减
return new_params
# 对比Adam和AdamW的更新差异
print("=== Adam vs AdamW:解耦权重衰减 ===\n")
np.random.seed(42)
# 模拟一个参数更新的场景
param = np.array([0.5, 1.0, -0.8, 2.0, -1.5])
grad = np.array([0.1, 0.05, -0.03, 0.2, -0.1])
adam = Adam(lr=0.01, weight_decay=0.01)
adamw = AdamW(lr=0.01, weight_decay=0.01)
# 经过几轮迭代后观察差异
param_a = param.copy()
param_w = param.copy()
print(f"初始参数: {param}")
print(f"当前梯度: {grad}\n")
for step in range(5):
# 模拟梯度变化
g_a = grad + np.random.randn(5) * 0.02
g_w = g_a.copy()
param_a = adam.step(param_a, g_a)
param_w = adamw.step(param_w, g_w)
print(f"Round {step + 1}:")
print(f" Adam: {np.round(param_a, 4)}")
print(f" AdamW: {np.round(param_w, 4)}")
# 关键差异分析
print(f"\n【Adam和AdamW的关键差异】")
print(f"Adam: 权重衰减与自适应学习率耦合")
print(f" - 更新 = lr * (自适应缩放 + weight_decay * param)")
print(f" - 权重衰减强度受到自适应学习率缩放的影响")
print(f"AdamW: 权重衰减与自适应学习率解耦")
print(f" - 先做自适应缩放更新,再独立减去 weight_decay * param")
print(f" - 权重衰减强度恒定,不受梯度分布影响")
print(f"\n→ AdamW在泛化性能上通常优于Adam")
print(f"→ AdamW现已成为PyTorch的默认推荐优化器")
三、Lion与优化器全景对比
是什么(定义):Lion(EvoLved Sign Momentum)是Google于2023年通过"进化搜索"发现的优化器,已在Google的ViT、扩散模型等训练中验证。它的更新规则惊人地简单——只使用梯度和动量的符号(sign),不需要二阶矩估计:u_t = β₁·m_t + (1-β₁)·g_t(计算更新方向),θ_{t+1} = θ_t - η·sign(u_t)(只用符号),m_{t+1} = β₂·m_t + (1-β₂)·g_t(更新动量)。因为只有sign操作,Lion比AdamW节省了v(二阶矩)的显存,且sign的固定步长具有一定的正则化效果。
大白话 Lion是"大道至简"的极致。Adam像个精密仪器——记住梯度的"速度"(动量)和"加速度"(二阶矩),每个参数都精细调节。Lion则像个果断的将军——只问"往哪个方向走?"(sign),不问"走多远?"(每次都走固定步伐)。简单粗暴的设计居然效果更好——因为固定步长让模型对所有参数一视同仁,不会过度偏向"经常被更新的参数"(这恰好起到了正则化效果)。而且Lion还省了一半显存(不需要存二阶矩v)。
怎么做(完整优化器选择指南):
import numpy as np
# 所有主流优化器的对比实现
# 在一个多极值点的复杂损失面上演示
class SGDMomentum:
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr
self.momentum = momentum
self.v = 0
def step(self, param, grad):
self.v = self.momentum * self.v + grad
return param - self.lr * self.v
class RMSProp:
"""RMSProp:只用二阶矩,不用动量"""
def __init__(self, lr=0.001, beta=0.99, eps=1e-8):
self.lr = lr
self.beta = beta
self.eps = eps
self.v = 1.0 # 二阶矩的指数移动平均
def step(self, param, grad):
self.v = self.beta * self.v + (1 - self.beta) * (grad ** 2)
return param - self.lr * grad / (np.sqrt(self.v) + self.eps)
class AdamOptimizer:
def __init__(self, lr=0.001, betas=(0.9, 0.999), eps=1e-8):
self.lr = lr
self.beta1, self.beta2 = betas
self.eps = eps
self.m = 0
self.v = 0
self.t = 0
def step(self, param, 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)
return param - self.lr * m_hat / (np.sqrt(v_hat) + self.eps)
class LionOptimizer:
"""Lion优化器——简化的符号动量"""
def __init__(self, lr=1e-4, betas=(0.9, 0.99), weight_decay=0.01):
self.lr = lr
self.beta1, self.beta2 = betas
self.weight_decay = weight_decay
self.m = 0
def step(self, param, grad):
# 计算更新方向:β₁·m + (1-β₁)·g
update_direction = self.beta1 * self.m + (1 - self.beta1) * grad
# 只用符号决定方向
param_new = param - self.lr * np.sign(update_direction)
# 权重衰减(解耦)
param_new = param_new - self.lr * self.weight_decay * param
# 更新动量:β₂·m + (1-β₂)·g
self.m = self.beta2 * self.m + (1 - self.beta2) * grad
return param_new
# 对比所有优化器在多个初始点的收敛表现
print("=== 优化器全景对比 ===\n")
# 复杂的一维损失函数(多个极值点)
def complex_loss(x):
return x ** 2 + 3 * np.sin(3 * x) + 0.5 * np.cos(8 * x)
def complex_grad(x):
return 2 * x + 9 * np.cos(3 * x) - 4 * np.sin(8 * x)
# 对比三个不同的初始位置
init_positions = [2.0, 0.5, -2.5]
optimizers = {
'SGD+Momentum': SGDMomentum(lr=0.01),
'RMSProp': RMSProp(lr=0.01),
'Adam': AdamOptimizer(lr=0.01),
'Lion': LionOptimizer(lr=0.01),
}
for init_name, init_x in [("远点", 2.0), ("近点", 0.5), ("负远点", -2.5)]:
print(f"【初始位置: {init_name} x0={init_x}】")
for opt_name, opt_class in optimizers.items():
opt = opt_class
opt.__init__() # 重置优化器
x = init_x
losses = []
for _ in range(500):
g = complex_grad(x)
if opt_name == 'SGD+Momentum':
x = opt.step(x, g)
elif opt_name == 'RMSProp':
opt.__init__(lr=0.01)
x = opt.step(x, g)
elif opt_name == 'Adam':
opt.__init__(lr=0.01, betas=(0.9, 0.999))
x = opt.step(x, g)
elif opt_name == 'Lion':
opt.__init__(lr=0.01)
x = opt.step(x, g)
losses.append(complex_loss(x))
print(f" {opt_name:15s}: 最终位置={x:.4f}, 最终损失={losses[-1]:.4f}, 最低损失={min(losses):.4f}")
print()
# 显存和速度对比(定性)
print("【显存与速度对比(定性)】")
print(f"{'优化器':<15s} {'参数量倍数':>10s} {'速度':>8s} {'泛化性':>8s} {'调参难度':>8s}")
print("-" * 55)
print(f" {'SGD':<13s} {'1x':>10s} {'中等':>8s} {'最好':>8s} {'高':>8s}")
print(f" {'SGD+Momentum':<13s} {'1x':>10s} {'较快':>8s} {'很好':>8s} {'高':>8s}")
print(f" {'Adam':<13s} {'3x':>10s} {'快':>8s} {'一般':>8s} {'低':>8s}")
print(f" {'AdamW':<13s} {'3x':>10s} {'快':>8s} {'很好':>8s} {'低':>8s}")
print(f" {'Lion':<13s} {'2x':>10s} {'更快':>8s} {'很好':>8s} {'低':>8s}")
print("\n→ SGD+Momentum:泛化最好但需要精细调参,适合小型任务")
print("→ AdamW:默认推荐,通用性强,收敛快,泛化好")
print("→ Lion:大模型探索,显存效率高,适合ViT/扩散模型")
概念关系图谱
| 概念 | 核心含义 | 与AI的关系 | 关联概念 |
|---|---|---|---|
| SGD | 沿梯度反方向走固定步长 | 最基础的优化器,泛化好但收敛慢 | 随机梯度下降、学习率 |
| Momentum | 累积历史梯度(惯性)加速收敛 | 解决SGD在震荡方向效率低的问题 | SGD、NAG |
| Adam | 自适应学习率+动量 | 最通用的优化器,默认首选 | 动量、RMSProp |
| AdamW | 解耦权重衰减的Adam修正版 | PyTorch默认推荐,泛化优于Adam | 权重衰减、L2正则化 |
| Lion | 简化的符号动量优化器 | 大模型和视觉任务的新选择,省显存 | 符号梯度、进化搜索 |
| RMSProp | 二阶矩自适应学习率 | Hinton课程提出,是Adam的重要灵感 | 自适应学习率 |
重点答疑
Q1: 为什么Adam收敛快但SGD泛化好?
这是经典的"自适应优化器泛化差距"现象。原因:①Adam的自适应学习率使参数可以自由选择"快车道"和"慢车道",容易在训练数据上找到"捷径"(overfit),而SGD的均匀学习率隐式地偏好平坦的最小值(flat minima),这通常泛化更好;②Adam的二阶矩估计引入了噪声和偏差,可能导致训练和测试时的不一致。不过AdamW和现代正则化技术(数据增强、dropout等)已经大幅缩小了这一差距。
Q2: AdamW和Adam究竟差在哪里?值得切换吗?
核心差异:Adam中权重衰减(weight decay)和L2正则化等价,且耦合在自适应学习率中——更新量 = η * (自适应缩放 + λ * θ),λ的实际效果被自适应缩放扭曲。AdamW改为真正的解耦权重衰减:更新量 = η * 自适应缩放 - η * λ * θ,λ不受梯度分布影响。结论:应该切换——几乎所有主流框架(PyTorch 2.0+,HuggingFace Transformers)都已默认使用AdamW,它是Adam的直接升级版。
Q3: Lion应该在什么场景下使用?
Lion在Google的大规模实验中表现出色:①Vision Transformer(ViT)训练——比AdamW快且准确率相当;②扩散模型(Imagen)训练——生成质量更好;③语言模型——在中等规模(<10B参数)的llm预训练中有效。但Lion对学习率敏感——推荐lr=1e-4(比Adam的1e-3小一个数量级),且建议配合较大的batch size。小规模实验建议先用AdamW,大规模探索可尝试Lion。
章节单词汇总
| 英文 | 音标 | 术语/释义 |
|---|---|---|
| SGD (Stochastic Gradient Descent) | /stoʊˈkæstɪk/ | 随机梯度下降,每次用一个batch的梯度更新 |
| Momentum | /moʊˈmentəm/ | 动量,累积历史梯度以加速收敛和抑制震荡 |
| Nesterov Momentum | /neˈsterɒv/ | Nesterov加速梯度,在"前瞻"位置计算梯度 |
| AdaGrad | /ˈeɪdə ɡræd/ | 自适应学习率根据历史梯度平方累加衰减 |
| RMSProp | /ɑːr em es prɑːp/ | 使用指数移动平均替代AdaGrad的梯度平方累加 |
| Adam | /ˈædəm/ | 自适应矩估计,结合动量和自适应学习率 |
| AdamW | /ˈædəm ˈdʌbəljuː/ | 修正权重衰减实现的Adam变体 |
| Lion | /ˈlaɪən/ | 进化发现的符号动量优化器 |
面试练习
Q1 [单选] Adam相比SGD+Momentum的核心优势是什么?
- A. 计算速度更快
- B. 每个参数有独立的自适应学习率
- C. 不需要学习率
- D. 显存占用更少
解答:Adam的核心创新是自适应学习率——通过二阶矩估计v_t为每个参数独立调整学习率,梯度大的参数自动减小步长,梯度小的参数自动增大步长。这让Adam对超参数不敏感,在各种任务上开箱即用。
Q2 [单选] AdamW相对于Adam的关键改进是什么?
- A. 使用更大的学习率
- B. 引入二阶矩估计
- C. 将权重衰减与自适应学习率解耦
- D. 移除动量项
解答:AdamW将权重衰减(weight decay)从梯度更新中分离——先做自适应学习率更新,再独立做权重衰减。这纠正了原始Adam中L2正则化和自适应学习率耦合的问题,显著提升了泛化性能。
Q3 [多选] 以下哪些是SGD+Momentum相对于纯SGD的优势?
- A. 震荡方向被平滑,收敛更稳定
- B. 一致方向加速,收敛更快
- C. 更容易逃离局部最优
- D. 减少显存占用
- E. 对学习率敏感度降低
解答:动量通过EMA平滑梯度方向——震荡抵消、一致加速,同时也赋予了"惯性"帮助逃离浅的局部最优,对学习率有一定容忍度。显存占用方面和纯SGD一样(只多存一个动量向量)。
Q4 [单选] Momentum的典型系数β是多少?
- A. 0.5
- B. 0.9
- C. 0.99
- D. 0.999
解答:β=0.9是动量系数的最常用值,意味着当前梯度贡献10%,历史动量贡献90%。β越大动量越"顽固"(方向记忆越长),β越小越敏捷。0.9是经验最优值。
Q5 [多选] 关于Lion优化器,以下哪些说法正确?
- A. 只使用梯度和动量的符号(sign)
- B. 比Adam省显存(不需要存储二阶矩v)
- C. Google通过进化搜索发现
- D. 在Transformer和CNN上必定优于Adam
- E. 推荐学习率比Adam小一个数量级
解答:Lion只使用sign操作,无需二阶矩存储,通过进化搜索发现,推荐lr=1e-4(比Adam的1e-3小一个数量级)。Lion并非在所有任务上都优于AdamW——它在大模型和视觉任务上有优势,但小规模实验上不一定。
Q6 [单选] Adam中的偏差修正(bias correction)是为了解决什么问题?
- A. 梯度消失
- B. 学习率过大
- C. 初始时刻m和v接近0导致的估计偏差
- D. 梯度噪声
解答:Adam初始化m_0=0, v_0=0。在训练初期(t很小时),EMA估计m_t≈(1-β₁^t)·E[g],远小于真实梯度。偏差修正除以(1-β^t)补偿了初始偏向0的问题,保证早期更新方向正确。
Q7 [单选] 哪个优化器的显存占用最低?
- A. SGD(1倍参数量)
- B. Adam(3倍参数量)
- C. Lion(2倍参数量)
- D. AdamW(3倍参数量)
解答:SGD只存储参数本身(1倍)。SGD+Momentum需要额外的动量向量(2倍),Adam需要动量+二阶矩(3倍),Lion只需要动量(2倍)。实际大模型训练中,优化器显存开销是重要考虑因素。
Q8 [多选] 以下哪个场景最适合使用SGD+Momentum而非AdamW?
- A. 小型CNN在CIFAR-10上需要最佳泛化性能
- B. 有经验的研究者手动精细调参
- C. 快速原型实验
- D. Transformer预训练
- E. 与特定的数据增强和正则化配合使用
解答:SGD+Momentum在小任务上配合精细调参和强大的正则化/增强,往往能获得比AdamW更好的泛化性能(如经典的ResNet在ImageNet上用SGD训练)。但大模型和快速实验用AdamW更省心。
Q9 [单选] Adam的β₂参数通常设为多少?
- A. 0.9
- B. 0.999
- C. 0.99
- D. 0.5
解答:β₂=0.999控制二阶矩估计的指数衰减,这意味着二阶矩的半衰期约为1000步(ln(0.5)/ln(0.999)≈693)。β₁=0.9控制动量衰减,半衰期约6-7步。β₂更"长久"——自适应学习率需要更长时间的平均。
Q10 [多选] 以下哪些是Nesterov加速梯度(NAG)相对于标准Momentum的特点?
- A. 在当前参数加上动量后的"前瞻"位置计算梯度
- B. 理论上在凸优化中有更好的收敛保证
- C. NAG已经包含在Adam中
- D. "先看一步再走"比"盲走"更明智
- E. 显存占用比Momentum少
解答:NAG在θ_t + β·v_{t-1}(前瞻位置)计算梯度,而非当前θ_t。这种"先看一步"使其在凸问题上理论收敛更好。显存占用和Momentum一样(都是一个动量向量)。Adam包含的是标准Momentum而非NAG(NAdam是NAG版的Adam)。