首页优化地图方法索引基础概念
返回技术汇总表 ↗

M039 · 独立详解

梯度累积与同步频率:小批次怎样组成一次正确的更新

从有效批量、Loss权重和参数更新时间讲起,理解梯度累积省了什么,以及 DDP 何时可以少同步。

调度与并行通信与互联内存与数据搬运详解已编写
视频讲解待制作

这篇文章的小视频尚未制作;可以先阅读下方文字详解。

本页目录

01 · 目标批次装不下时,分几次计算

假设希望一次参数更新使用8条样本,但设备一次只能承受2条样本的激活。可以在同一参数版本下依次处理4个小批次,每次前向和反向都把梯度加到同一缓冲里,最后只更新一次参数。

这里的小批次称为 microbatch。一次更新所涵盖的数据是有效批量。梯度累积通常减少同时需要保留的激活,而不是减少8条样本需要的总前向/反向计算,也不会自动缩小权重、优化器状态或梯度缓冲。

02 · 把一次更新的完整边界画清楚

教学伪代码 / 示意 · 不代表真实通信接口
开始一个更新窗口:清空梯度
  microbatch 1:前向 → 计算Loss → 反向 → 累加梯度
  microbatch 2:前向 → 计算Loss → 反向 → 累加梯度
  microbatch 3:前向 → 计算Loss → 反向 → 累加梯度
  microbatch 4:前向 → 计算Loss → 反向 → 累加梯度
全部贡献就绪 → 必要的梯度同步 → unscale/检查/裁剪 → 更新参数
下一更新窗口再清空梯度

这4次反向使用相同的参数;若每次都 optimizer.step,就是4次独立更新,已经不是同一个“大批次分开算”。通常也应在反向后释放本次不再需要的计算图,避免把所有 microbatch 的激活都攒到最后一次反向。

03 · 有效批量乘哪些维度

等大小、等积累轮次的常见情况:全局样本批量 = 每个 DP 副本的 microbatch 样本数 × 每次更新的累积次数 × DP 副本数。例如每副本2条、累积4次、DP=2,合计16条。

配置一次更新的全局样本数为什么
单副本,2条×4次8同一模型依次看到8条。
DP=2,每副本2条×4次16两个副本处理不同样本。
再加TP=2、PP=2,总8卡仍是16TP/PP协作处理这些样本,不额外产生新数据。

LLM 训练还常按有效 token 数定义目标归一化:样本长度、padding和mask不同,16条样本可能不是固定数量的训练 token。更改累积次数或DP度还会改变更新频率、调度器进度与学习率策略,不能仅按卡数机械缩放。

04 · 为什么有时除以累积次数仍然算错

如果各小批次的 Loss 都是相同数量有效项的平均值,可把每次平均 Loss 除以累积次数。但当有效 token 数不同,需要按全局目标的分母加权。

microbatch有效token数本批次平均Loss加权贡献
A212/8×1=0.25
B636/8×3=2.25

正确的全体 token 平均 Loss 是 (2×1+6×3)/8=2.5;简单把两个平均值各除以2,得到2.0,相当于人为让两批次权重相同。梯度也应对同一个目标作相应加权。

多 DP 副本的有效 token 数不等时,需要统一分母,并考虑梯度归约究竟求和还是平均。例如若普通 DDP 最终把梯度除以 D,可让每个副本的本地 Loss 总和乘 D/N_global,从而在归约后得到全体有效 token 的平均梯度。这是归一化推导,不是所有框架都应照搬的代码。

05 · DDP 为什么可以只在最后一次同步

普通复制式 DDP 的副本各自累积本地梯度。如果每个 microbatch 反向都做归约,会增加重复同步轮次。DDP 的 no_sync 可以让前几轮先保留本地贡献,在最后一轮反向同步累积结果。它减少这些梯度同步,不代表张量并行、流水线或FSDP参数通信也被关闭。

DDP 接入示意 · 假设K个等大小批次、普通FP32更新、各rank使用相同更新边界
from contextlib import nullcontext
optimizer.zero_grad(set_to_none=True)
for i, batch in enumerate(equal_size_microbatches):
    sync_context = nullcontext() if i == K - 1 else ddp_model.no_sync()
    with sync_context:
        # forward 也必须位于 no_sync 的作用域内
        loss = mean_loss(ddp_model(batch), batch.targets)
        (loss / K).backward()
optimizer.step()

若使用 AMP,完成全部累积后再按配方 unscale、检查与裁剪,最后调用 scaler.step/update。FSDP、ZeRO或自定义通信钩子有不同语义和内存行为,不能只看到 no_sync 这个名称就套用普通复制式DDP的容量结论。

依据:PyTorch · DDP 分桶接口 ↗ · PyTorch · AMP 示例(main) ↗

06 · 分批累积与大批次何时等价

当损失可按样本或 token 相加、参数在窗口内不变、归一化一致时,梯度累积可对应一次大批次梯度。实际还会有浮点求和顺序、dropout随机数、依赖批次统计的层,以及按批次构造的对比损失等差异。需要按模型判断。

  • 把梯度裁剪放在完整目标梯度就绪后;每小批次各裁剪一次通常不是同一更新。
  • 记录最后不足K批的窗口,并按实际贡献处理,不能默认分母仍为K。
  • DP各rank保持兼容的同步边界,避免某个rank提前结束而其他rank等待集合通信。
  • 测试学习率调度、Adam状态与weight decay的推进频率;它们通常随参数更新而非每个microbatch推进。

不要仅比较一条 Loss 曲线就宣布严格等价。可先固定样本和参数,比较合并批次与累积批次的梯度、一次更新后的权重,然后检查真实训练行为。

07 · 一个可运行的梯度对照

下方标准库例子用线性回归和平方损失推导梯度,把5条样本切为2条与3条两批。它对比完整批次、正确按样本数加权累积,以及错误地平均两份梯度,还展示每批都更新会得到不同结果。它不依赖自动微分或GPU。

它帮助检查数学目标,真正框架还需要处理梯度缓冲、混合精度、通信后端和训练状态。流水线调度也会把一个更新窗口分成多个microbatch;相同的参数版本和梯度归一化要求仍适用。

依据:PyTorch · DDP 分桶接口 ↗

08 · 收益、代价和跨平台条件

可能的收益需要观察的代价
降低同时驻留的激活,支持目标有效批量小矩阵利用率可能较低,更多轮次有启动开销。
按支持的实现减少DP梯度同步最后同步的暴露时间、通信分桶与重叠可能改变。
调整更新频率与数据组织全局批量变化可能影响收敛;不等于无条件训练加速。

在 NVIDIA 或昇腾上都先确认所用训练框架的归一化、梯度通信和分片模式。DDP示意是PyTorch接口逻辑,不是NCCL/HCCL的配置指令。设备通信实现、混合精度和支持组合需要按安装版本核对。

测试应同时记录峰值内存、一次更新耗时、每秒有效token与目标质量。只比较每个小批次变短,会漏掉一个窗口需执行更多次的事实。

09 · 用四个问题检查训练边界

  • 累积期间为什么不能每批次都更新参数?
  • TP和PP度增加,为什么不直接乘进有效样本批量?
  • 长度不同的小批次,为什么loss/K可能不正确?
  • no_sync包住backward而没包住forward,为什么不符合DDP接口要求?

流程位置与关联方法

这个方法可能出现在多个位置;计算线包含模型计算与算子实现,执行线包含运行时、内存和通信。

资料与核查范围

原理依据以原始论文和官方文档为主;算例与时间线是教学推导。文档核查日期:2026-10-10。引用 main / latest 文档时,实际接口以安装版本为准。

代码为接入片段或教学伪代码;本页没有声称完成 NVIDIA / 昇腾硬件实测。

← 返回总表中的 M039继续阅读:流水线调度:1F1B、交错、DualPipe 类 →
AI Infra 知识库 · 总览 → 独立讲解网站更新 2026-10-08
搜索知识库

搜索方法与基础概念 · Esc 关闭