SGD与Adam

第一部分:理解优化的目标——梯度下降 (Gradient Descent)

在开始之前,我们必须明白一个最根本的概念:训练模型的目标是找到一组最优的参数(权重 w 和偏置 b),让模型的损失函数(Loss Function)的值最小化。

你站在一座连绵起伏的山上,天黑了,你的任务是尽快走到山谷的最低点。你看不清整个山脉的全貌,但你能知道你脚下这一点哪个方向是下山最陡峭的。

  • 山谷最低点:就是损失函数的最小值。
  • 你所在的位置:就是模型当前的参数。
  • 下山最陡峭的方向:就是损失函数在当前位置的梯度(Gradient)的反方向。梯度指向的是函数值上升最快的方向,那么梯度的反方向就是下降最快的方向。

梯度下降 (Gradient Descent, GD) 的核心思想很简单:计算出当前位置的梯度,然后沿着梯度的反方向迈一小步,不断重复这个过程,直到走到谷底。

这个“一小步”的大小,我们称之为学习率 (Learning Rate, lr)

  • lr 太大:你可能一步迈过了谷底,跑到对面山坡上,导致无法收敛。
  • lr 太小:你前进得太慢,要花非常长的时间才能走到谷底。

第二部分:SGD (随机梯度下降)——更轻快的下山方式

梯度下降有一个问题:要计算当前位置的梯度,需要把整个训练数据集(比如,一百万张图片)全部拿来计算一次损失,然后求平均梯度。如果数据集非常大,这个计算成本是无法接受的。这就好比,你为了看清脚下的路,非要把整座山的每一寸土地都勘探一遍才决定下一步,效率太低。

SGD (Stochastic Gradient Descent) 应运而生,它提出了一种绝妙的改进:

不计算全部数据的梯度,而是每次随机抽取一小部分数据(一个批次,Batch)来计算梯度,并用这个“局部”的梯度来近似“全局”的梯度,然后更新参数。

这就像你在山中,不再勘探整座山,而是随机朝一个方向看一小段路,判断出这个小范围内的下山方向,然后就立刻朝那个方向走一步。

优点:

  • 速度快:每次迭代的计算量大大减少,模型更新速度极快。
  • 引入随机性:由于每次只看一部分数据,计算出的梯度是有噪声的。这种“噪声”反而可能是好事,它可能帮助你跳出那些不是真正最低点的“小山谷”(局部最优解),从而有更大机会找到全局的最低点(全局最优解)。

缺点:

  • 收敛不稳定:随机性也带来了梯度方向的摇摆和震荡。你每一步都只是根据一小块地形来决策,你前进的路线会是曲折、摇摆的,而不是一条直线,导致收敛过程波动较大。
PyTorch代码理解SGD
import torch
import torch.nn as nn
import torch.optim as optim

# 1. 准备数据和模型 (示例)
# 假设我们有一个简单的线性回归模型
# 输入特征维度为10,输出为1
model = nn.Linear(10, 1) 
# 创建一些假数据
input_data = torch.randn(128, 10)  # 128个样本,每个样本10个特征
labels = torch.randn(128, 1)      # 对应的128个标签

# 2. 定义损失函数
criterion = nn.MSELoss() # 均方误差损失

# 3. 初始化SGD优化器
# 将模型的所有参数(model.parameters())交给优化器管理
# 设置学习率(lr)为0.01
optimizer_sgd = optim.SGD(model.parameters(), lr=0.01)

# --- 训练过程中的一步 ---
def train_step():
    # 在每次更新前,清空上一轮的梯度
    optimizer_sgd.zero_grad()
    
    # 前向传播:模型根据输入数据进行预测
    outputs = model(input_data)
    
    # 计算预测值和真实标签之间的损失
    loss = criterion(outputs, labels)
    
    # 反向传播:计算损失函数关于模型参数的梯度
    loss.backward()
    
    # 更新参数:优化器根据计算出的梯度,沿着反方向更新模型权重
    # 更新规则: new_weight = old_weight - lr * gradient
    optimizer_sgd.step()
    
    print(f"Loss: {loss.item()}")

# 执行一次训练步骤
train_step()

第三部分:Adam (自适应矩估计)——更智能的下山专家

SGD虽然高效,但它的“摇摆”问题和对学习率的高度敏感性(需要手动精细调整)促使了更先进的优化器的诞生。Adam是目前最流行、最常用的优化器之一,它结合了两种重要思想:动量(Momentum)和自适应学习率(Adaptive Learning Rate)

Adam就像一位更聪明的登山专家,他不仅看脚下,还具备了惯性和对地形的感知力。

模块1:动量 (Momentum)

SGD的每一步都只取决于当前计算出的梯度,这导致了摇摆。动量法则引入了“惯性”的概念。

它不仅仅考虑当前这一步的梯度,还会累加过去一段时间的梯度方向。想象一个从山上滚下来的球,它不会因为一块小石头就完全改变方向,它的动量会带着它冲过小的颠簸,继续沿着大致正确的方向前进。

  • 好处:在梯度方向一致的维度上,动量会加速下降;在梯度方向摇摆不定的维度上,动量可以抵消震荡,使得更新方向更稳定、更快速。
模块2:自适应学习率 (RMSprop思想)

SGD家族(包括动量法)的另一个问题是,对所有参数都使用同一个学习率 lr。但模型中不同的参数,其重要性和更新的尺度可能完全不同。有的参数可能已经接近最优值,需要微调(小学习率);有的参数还差得很远,需要大步前进(大学习率)。

Adam通过引入二阶矩估计来为每个参数自动调整学习率。简单来说:

  • 它会记录每个参数过去梯度值的平方的累积平均值
  • 如果某个参数的梯度一直很大(说明它总是在剧烈变化、不稳定),那么分母就会变大,从而使得这个参数的实际学习率变小,起到“踩刹车”的作用,让它稳定下来。
  • 反之,如果某个参数的梯度一直很小,分母就小,实际学习率会变大,鼓励它“大胆探索”。
Adam = 动量 + 自适应学习率

Adam将上述两种思想完美地结合在一起:

  1. 一阶矩估计(动量):它像动量法一样,会累积过去的梯度,计算出带有“惯性”的更新方向(记为 m)。
  2. 二阶矩估计(自适应学习率):它会累积过去梯度的平方,为每个参数计算一个自适应的“刹车”或“油门”(记为 v)。
  3. 最终更新:用带惯性的方向 m,除以自适应的调节项 v 的平方根,来对参数进行更新。

结果就是,Adam能够为每个参数计算出独立的、自适应的学习率,并且更新方向更加稳定,使得它在大部分情况下都能快速、稳健地收敛,且对初始学习率 lr 的选择不像SGD那么敏感。

PyTorch代码理解Adam
import torch
import torch.nn as nn
import torch.optim as optim

# 1. 同样的数据和模型
model = nn.Linear(10, 1)
input_data = torch.randn(128, 10)
labels = torch.randn(128, 1)

# 2. 同样的损失函数
criterion = nn.MSELoss()

# 3. 初始化Adam优化器
# 使用起来和SGD非常相似,只是换了个名字
# lr: 初始学习率
# betas: 用于计算一阶和二阶矩估计的指数衰减率,通常用默认值即可
# eps: 为了防止除以零而增加的一个极小值
optimizer_adam = optim.Adam(model.parameters(), lr=0.001, betas=(0.9, 0.999), eps=1e-8)

# --- 训练过程中的一步 (和SGD的调用方式完全一样) ---
def train_step_adam():
    optimizer_adam.zero_grad()
    outputs = model(input_data)
    loss = criterion(outputs, labels)
    loss.backward()
    
    # Adam在内部会自动处理动量和自适应学习率的复杂计算
    # 我们只需要调用 step() 即可
    optimizer_adam.step()
    
    print(f"Loss with Adam: {loss.item()}")

# 执行一次训练步骤
train_step_adam()

总结与对比

特性SGD (随机梯度下降)Adam (自适应矩估计)
核心思想每次随机用一小批数据计算梯度来更新。结合了动量自适应学习率
学习率所有参数共享一个固定的学习率,需要手动精细调整。每个参数计算一个自适应的学习率。
收敛性速度较慢,收敛过程波动大、不稳定速度快,收敛过程平稳,通常能快速找到一个较好的解。
优点简单,内存占用小。其随机性可能帮助跳出局部最优,找到更好的解。鲁棒性强,对初始学习率不敏感,在大多数任务中表现都很好,是默认首选
缺点对学习率敏感,训练过程不稳定,后期收敛可能较慢。可能会错过最优解(有研究表明其泛化能力有时不如精调的SGD)。计算和内存开销比SGD稍大。
适用场景当你有足够的时间和经验去精细调整学习率时;或者在一些研究中发现其泛化能力更好时。绝大多数情况下的首选优化器,尤其是在项目初期和对模型性能没有极致要求时。

希望这个分步、带代码的解析能让你对SGD和Adam有一个清晰、深入的理解!

大型模型训练中,一个看似更“笨”、更基础的方法(SGD),在特定条件下,其效果能够超越被广泛使用的高级方法(Adam)。

核心问题:为何“笨办法”有时更有效?

在顶尖高手的较量中,为什么有时看似“笨”的方法反而能取得更好的效果?

两个主角:Adam vs. SGD (笨办法)
  • Adam:是当前人工智能领域,尤其是大型语言模型训练中,一个非常主流和先进的优化器 。
  • SGD:是一种相对更基础的优化方法。在文中,它被视为那个有效的“笨办法” 。
关键变量:批量大小 (Batch Size)

决定哪个方法更优越的关键,在于一个叫做“批量大小”(Batch Size)的参数 。这指的是模型在一次更新中处理的数据样本量。

  • 传统情况 (小批量):在较小的批量大小下,Adam通常是首选。
  • 新的发现 (大批量):有研究者意外发现,当把批量大小提升到一个很高的数值时(文中提到了1024 ),情况发生了逆转。
“大力出奇迹”
  1. SGD在大批量下的逆袭:研究表明,在非常大的批量大小条件下,SGD这个“笨办法”不仅能够有效训练,其最终效果甚至可以超越更复杂的Adam优化器 。
  2. 资源是前提:这种“大力出奇迹”的策略是建立在拥有足够强大的计算资源之上的 。因为只有算力充足,才能支撑起如此巨大的批量来进行训练。
  3. 重新审视方法论:这一发现挑战了“高级方法一定更好”的固有观念。它揭示了,方法的选择和其所处的计算资源环境密切相关。在小批量、计算有限的条件下,SGD的成功是有限的 。但在资源充裕、可以使用大批量的情况下,它为模型训练“开了一片天” ,展现了惊人的潜力。

在训练AI模型时,Adam就像一个聪明的徒步者,他会根据路况(梯度)不断调整自己的步伐(学习率),走得很稳很快。而SGD则像一个朴实的徒步者,步伐大小固定。

过去,大家通常都走在小路上(小批量),聪明的徒步者Adam自然更有优势。但现在,我们有了超级计算机这样能开出一条“高速公路”(大批量)的工具 。在这条又宽又直的高速公路上,那个只会埋头迈开大步向前冲的“笨”徒步者SGD,反而因为其简单、直接的策略,最终能比需要不断调整的Adam更快、更好地到达终点 。

Is your batch size the problem? Revisiting the Adam-SGD gap in language modeling
[Max Planck Institute for Intelligent Systems]
https://arxiv.org/abs/2506.12543

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

frostmelody

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值