小李想买一台新电脑,要一万块。可他的钱包很小,一次最多装两千五,根本塞不下整叠钞票。于是他做了一个聪明的决定:每天往储蓄罐里存两千五,攒够四天,凑成一万块,再一次性付款。这个“分批存钱、攒够再花”的生活智慧,正是 AI 训练里“梯度累积”的日常翻版。 在深度学习的世界里,“钱包”是 GPU 显存,“大件商品”是大 batch 训练。显存不够时,想一次把 128 张图片喂给模型,程序会直接报“内存溢出”(OOM)崩溃。梯度累积(Gradient Accumulation)的做法很朴素:把大 batch 拆成 N 份小 batch(micro-batch),依次做前向计算和反向传播,每算完一份就把梯度暂时存进“储蓄罐”,攒满 N 份后,才用累积起来的梯度做一次参数更新。这样既享受了大 batch 训练梯度稳定的好处,又不需要一次性掏空显存。

岗位关联

掌握梯度累积,等于用一张小显存显卡撬动大 batch 训练的效果,省下几万块硬件钱——这是分布式训练工程师和大模型微调开发者的必备技能。

目录
  1. 可视化演示
  2. 核心原理
  3. 数学等价
  4. 步数设置
  5. 学习率调优
  6. 分布式配合
  7. 工程实现
  8. 踩坑注意
  9. 适用场景
  10. AI 应用场景
  11. Python 演示
  12. 常见误区
  13. 面试题

可视化演示

动态演示梯度累积突破显存限制技巧的核心机制,通过总览/传输/转换/输出四种模式展示关键概念与交互流程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

梯度累积突破显存限制技巧:梯度累积 梯度累积核心原理与数学等价性 累积步数设置与有效batch计 核心机制可视化 梯度累积突破显存限制技巧整体架构与关键组件

核心原理

小李家想买一台新电脑,要一万块。他的钱包很小,一次最多装两千五,装不下整叠钞票。于是他每天往储蓄罐里存两千五,攒四天凑够一万再一次性付款。这个“分批存钱、攒够再花”的智慧,就是梯度累积的日常版本。

AI 训练里,“钱包”是 GPU 显存,“大件商品”是等效大 batch。显存不够时,强行把 B 个样本一次喂给模型,程序会直接报“内存溢出”(OOM)崩溃。梯度累积(Gradient Accumulation)的做法是:把大 batch 拆成 N 份小 batch(micro-batch,即微批次),依次做前向计算和反向传播,每算完一份就把梯度暂存起来,攒满 N 份后,用累积的梯度做一次参数更新。

传统训练每个 batch 算完立即更新参数;梯度累积则把“计算梯度”和“更新参数”解耦,攒够 N 份才更新一次。它几乎没有额外显存开销——多出来的只是梯度本身的存储,而梯度尺寸和模型参数同阶,相比激活值占用小得多。这正是它“省钱”的关键。

所以梯度累积的本质,就是用小额分批的“存款”凑成一次大额支出,让模型既能享受大 batch 的稳定梯度,又不用一次性掏空显存。

θt+1=θt−η⋅1N∑i=1N∇Li\displaystyle \theta_{t+1} = \theta_t - \eta \cdot \frac{1}{N} \sum_{i=1}^{N} \nabla L_i
公式说:新的参数 = 旧参数 − 学习率 × N 份小 batch 梯度的平均值。这就是“攒够再花”的数学表达。
  1. 模型参数 θ模型里所有待学习的权重,对应“储蓄罐里的本金”,每次更新都希望它更接近最优解。
    θt\displaystyle \theta_t
  2. 第 i 份小 batch 梯度 ∇Lᵢ第 i 份“日薪”里应投入的本金份额,即损失对参数的偏导。
    ∇Li=∂Li∂θ\displaystyle \nabla L_i = \frac{\partial L_i}{\partial \theta}
  3. 加总 Σ把 N 份梯度累加,等于把 N 天的积蓄倒进同一个储蓄罐。
    ∑i=1N∇Li\displaystyle \sum_{i=1}^{N} \nabla L_i
  4. 平均与步长 1/N 和 η取平均防止 N 份钱被重复计算,再按学习率决定每次迈多大步。
    θt+1=θt−η⋅1N∑i=1N∇Li\displaystyle \theta_{t+1} = \theta_t - \eta \cdot \frac{1}{N} \sum_{i=1}^{N} \nabla L_i

数学等价

小李想知道:四天里每天存的两千五,和一次性拿一沓一万块,购买力是否相同?数学上,N 份小 batch 梯度的平均值,和一次大 batch 算出的梯度方向近似相同。

设大 batch B 被切成 N 份小 batch,每份大小为 b = B / N。大 batch 的梯度是所有样本梯度的整体平均;而 N 份小 batch 梯度的平均,在累积期间模型参数没被更新的前提下,恰好是对同一组样本做“先分块平均、再对块平均”——两块算术平均的结果几乎一致。

注意:这是近似等价,不是完全相等。首先,累积期间模型参数实际上在微变,严格相等要求参数在整个累积窗口内冻结;其次,BatchNorm、Dropout 这类对 batch 有依赖的层,小 batch 的统计特性和大 batch 不完全一样。相当于分 N 天攒的每一份钱加起来就是整笔钱,但中间的“汇率波动”会让账目有小偏差。

∇LB≈1N∑i=1N∇Lb(i)\displaystyle \nabla L_B \approx \frac{1}{N} \sum_{i=1}^{N} \nabla L_b^{(i)}
大 batch 的梯度,近似等于 N 份小 batch 梯度的平均值。这是梯度累积能“以假乱真”的数学根基。
  1. 大 batch 梯度 ∇L_B一次性看 B 个样本得到的整体梯度方向,对应“整笔钱”的购买力。
    ∇LB\displaystyle \nabla L_B
  2. 小 batch 梯度 ∇L_b⁽ⁱ⁾第 i 份小 batch 单独算出的梯度,对应“一天的积蓄份额”。
    ∇Lb(i)\displaystyle \nabla L_b^{(i)}
  3. 取平均近似 ≈N 份小梯度取均值后逼近大梯度——但中间有“利息损耗”:攒钱期间参数其实在变。
    1N∑i=1N∇Lb(i)≈∇LB\displaystyle \frac{1}{N} \sum_{i=1}^{N} \nabla L_b^{(i)} \approx \nabla L_B

步数设置

攒几天合适?小李定下目标:一个月要凑出一万块,每天能存两千五,那 N = 4 天。同理,梯度累积步数由“目标等效 batch”和“显存能装下的 micro-batch”共同决定。

先定下目标等效 batch 大小 B_target(想要模拟的大 batch),再量出显存能容纳的 micro-batch 大小 b_micro,两者相除并向上取整,就是累积步数 N。

实践里有几条经验:目标等效 batch 通常落在 32 到 512 之间——太小梯度噪声大,太大收敛变慢;累积步数 N 别贪大,虽然多攒几份不占额外显存,但更新频率降低、训练步数变稀疏,收敛曲线会变“糙”,一般 N ≤ 16 比较稳妥。攒太久才花一次钱,容易错失最佳“购物时机”。

N=⌈Btargetbmicro⌉\displaystyle N = \left\lceil \frac{B_{\text{target}}}{b_{\text{micro}}} \right\rceil
累积步数 = 目标 batch ÷ 单次能放下的 micro-batch,除不尽就向上取整。
  1. 目标 batch B_target想模拟的等效 batch 大小,按任务经验挑选,比如分类任务常用 256。
    Btarget\displaystyle B_{\text{target}}
  2. 微 batch b_micro显存能装下的单次 batch 大小,取决于显卡容量和模型尺寸。
    bmicro\displaystyle b_{\text{micro}}
  3. 向上取整 ⌈⌉除不尽时向上取整,保证等效 batch 至少达到目标值。
    N=⌈Btargetbmicro⌉\displaystyle N = \left\lceil \frac{B_{\text{target}}}{b_{\text{micro}}} \right\rceil

学习率调优

攒的钱多了,消费方式要不要变?整笔钱支付时议价能力更强,步子可以迈大一点;但刚涨了“存款规模”就猛花钱,也可能买贵。这就是学习率为什么要随等效 batch 放大。

梯度累积让等效 batch 扩大 N 倍,梯度的随机噪声被平均掉一部分,估计更平滑稳定,因此可以承受更大的学习率。最常用的是线性缩放规则:新学习率 = 原学习率 × N。保守一点,也可以用平方根缩放:新学习率 = 原学习率 × √N。无论哪种,都建议配合预热(warmup)策略——开头几百步用小学习率,让模型先适应“攒钱节奏”,再逐步放大。

钱多了可以花大点,但刚变富别立刻挥霍。实际调试时,可以先从线性缩放起步,如果 loss 震荡明显,就退回平方根缩放,并配合预热让模型平稳过渡。

ηnew=ηbase×N或ηnew=ηbase×N\displaystyle \eta_{\text{new}} = \eta_{\text{base}} \times N \qquad \text{或} \qquad \eta_{\text{new}} = \eta_{\text{base}} \times \sqrt{N}
等效 batch 放大 N 倍后,学习率也按 N(线性)或 √N(保守)放大,否则模型收敛会变慢。
  1. 基础学习率 η_base单卡小 batch 时调好的学习率,对应“原来的消费习惯”。
    ηbase\displaystyle \eta_{\text{base}}
  2. 累积步数 N等效 batch 放大的倍数,也是“存款规模”的放大倍数。
    N\displaystyle N
  3. 线性放大 ×N累积后的梯度更稳定,可以迈更大的步子,直接乘 N。
    ηnew=ηbase×N\displaystyle \eta_{\text{new}} = \eta_{\text{base}} \times N

分布式配合

小李拉上三个同事一起攒钱:每人每天各存各的,周末四人把积蓄汇总,再一起买电脑。这就是“数据并行 + 梯度累积”的配合方式。

数据并行(Data Parallelism)下,每张卡持有完整模型副本,各自处理一批数据。梯度累积与它配合的标准流程是:每张卡先在本地攒 N 步的梯度,攒够后做一次 AllReduce 操作,把各卡梯度加总(或取平均),同步完成后所有卡再统一更新一次参数。

注意 AllReduce 的频次:通常让梯度同步和参数更新同频——攒满 N 步才 AllReduce 一次。如果每个小步都全量同步,通信开销会吞掉累积省下的显存红利。一个人攒钱慢,一群人凑份子快;但凑份子要统一记账(梯度同步)才能公平分配。

Beff=bmicro×G×N\displaystyle B_{\text{eff}} = b_{\text{micro}} \times G \times N
多卡 + 梯度累积时,等效 batch = 单卡微 batch × 卡数 × 累积步数。三者相乘,轻轻松松凑出上千的大 batch。
  1. 单卡微 batch b_micro每张卡一次前向/反向计算的实际样本数。
    bmicro\displaystyle b_{\text{micro}}
  2. GPU 数 G参与数据并行的卡数,对应“一起攒钱的同事人数”。
    G\displaystyle G
  3. 累积步数 N每张卡本地攒几份梯度再同步一次。
    N\displaystyle N
  4. 三因子乘积总等效 batch 是三者的乘积,卡越多、攒越久,等效 batch 越大。
    Beff=bmicro×G×N\displaystyle B_{\text{eff}} = b_{\text{micro}} \times G \times N

工程实现

攒钱要记好账,梯度累积也要注意“记账姿势”:每份钱(梯度)怎么入账、多久结账一次、结账时注意什么。

PyTorch 里 backward() 默认是累加梯度的,所以最安全的实现是:每个 micro-batch 算出的 loss 先除以 N 再 backward(),这样累积结果恰好是 N 份梯度的平均;攒满 N 步后,再执行 optimizer.step() 并 optimizer.zero_grad()。

工程上还有两个要点:梯度裁剪(gradient clipping)要在累积完成后、step() 之前统一执行一次,而不是每个 micro-batch 都裁一遍——每步裁剪会破坏累积梯度的方向信息;如果用了混合精度训练,梯度缩放(gradient scale)也要按累积后的总梯度来操作。记账记对了,储蓄罐里的每一分钱才花得明明白白。

L′=1N∑i=1NLi\displaystyle \mathcal{L}' = \frac{1}{N} \sum_{i=1}^{N} \mathcal{L}_i
缩放后的损失 = 各 micro-batch 损失的平均。这一步是关键,否则累积梯度会变成大 batch 的 N 倍。
  1. 子损失 Lᵢ第 i 个 micro-batch 算出的损失值,对应“一天的收入流水”。
    Li\displaystyle \mathcal{L}_i
  2. 除以 N把损失平均,让 backward 累加的梯度等于均值而非总和。
    1N∑i=1NLi\displaystyle \frac{1}{N} \sum_{i=1}^{N} \mathcal{L}_i
  3. 缩放后损失 L'经过正确记账的“入账金额”,传给 backward 的最终损失。
    L′=1N∑i=1NLi\displaystyle \mathcal{L}' = \frac{1}{N} \sum_{i=1}^{N} \mathcal{L}_i

踩坑注意

攒钱路上有暗坑:以为攒的每一笔都能原样相加,却忽略“汇率波动”(学习率)、“记账方式”(BN 统计量)等干扰因素。

第一个坑是 BatchNorm。BatchNorm 层在训练时用的是当前 micro-batch 的均值和方差做归一化,小 batch 的统计量噪声大,和大 batch 的系统性统计量有偏差——好比拿一天的零碎收入估算整月水平,误差自然不小。这会导致带 BN 的模型在梯度累积下收敛变慢甚至不收敛。

第二个坑:不放大学习率,模型白白浪费了累积带来的稳定性。第三个坑:EMA(指数移动平均,用来平滑模型权重的一种技巧)和 BN 的滑动统计量通常每个 step 更新一次,累积模式下更新过于频繁,应把更新频率降到每 N 步。第四个坑:只盯着 step 数看收敛,正确做法是监控等效 epoch(每攒满一个目标 batch 才算见完一批数据)。出门前把坑都踩熟,攒钱这条路才能走得稳。

x^i=xi−μBσB2+ε\displaystyle \hat{x}_i = \frac{x_i - \mu_{\mathcal{B}}}{\sqrt{\sigma_{\mathcal{B}}^2 + \varepsilon}}
每个样本 = 减去当前 micro-batch 均值,再除以当前 micro-batch 标准差。batch 越小,这个估计越抖。
  1. 样本值 xᵢ当前 batch 里第 i 个样本的特征值,对应“一天的消费流水”。
    xi\displaystyle x_i
  2. 均值 μ_B当前 micro-batch 的平均值,小 batch 时它噪声更大。
    μB\displaystyle \mu_{\mathcal{B}}
  3. 方差 σ_B²当前 micro-batch 的方差,用来做归一化分母。
    σB2\displaystyle \sigma_{\mathcal{B}}^2
  4. 归一化结果 x̂ᵢ减均值除方差后的稳定特征,但统计量本身可能有偏。
    x^i=xi−μBσB2+ε\displaystyle \hat{x}_i = \frac{x_i - \mu_{\mathcal{B}}}{\sqrt{\sigma_{\mathcal{B}}^2 + \varepsilon}}

适用场景

不是所有人都需要攒钱买大件。出差吃顿饭、买瓶水,直接付就行。梯度累积也不是万能的,它有明确的适用边界。

最典型的场景有三个:一是显存受限——消费级显卡想跑大模型,这是梯度累积的主战场;二是小 batch 导致 BatchNorm 统计不稳、模型不收敛,需要把等效 batch 撑大;三是卡不够多,但又必须凑出足够大的等效 batch。此外,梯度检查点(Gradient Checkpointing,用重计算换显存)和 DeepSpeed ZeRO(把优化器状态切分到多卡)可以和梯度累积叠加使用,互补而不是互斥。

工具没有好坏,只有合不合适。梯度累积解决的是“显存小还想跑大 batch”的真痛点——当你的需求不在这个痛点里,就别强行攒钱,直接小 batch 硬跑反而更简单。

32≤N×bmicro≤512\displaystyle 32 \leq N \times b_{\text{micro}} \leq 512
经验法则:等效 batch(N 与 b_micro 的乘积)最好落在 32 到 512 之间,太小噪声大、太大收敛慢。
  1. 等效 batch 下界 32太小则梯度噪声大,攒钱攒了半天反而亏本。
    N×bmicro≥32\displaystyle N \times b_{\text{micro}} \geq 32
  2. 等效 batch 上界 512太大则收敛变慢、泛化能力下降,超过收益拐点。
    N×bmicro≤512\displaystyle N \times b_{\text{micro}} \leq 512
  3. 乘积 N × b_micro这才是每次参数更新真正“消费”的样本量。
    32≤N×bmicro≤512\displaystyle 32 \leq N \times b_{\text{micro}} \leq 512

AI 应用场景

大模型微调:16GB 显卡跑 7B 模型

阶段:模型训练与微调

解决的问题:传统的全参微调或 LoRA 微调中,大模型的前向激活值会撑爆显存,连 batch size = 1 都勉强。若想用更大的 batch 稳定微调,普通做法是升级硬件,成本极高。梯度累积让开发者用单张 16GB 消费卡,通过累积 8 步梯度,等效出 batch size = 8 的稳定更新效果。

具体案例:一位开发者用单张 RTX 3090 微调 7B 对话模型,直接训练 batch = 4 时 OOM。改用 batch = 1、累积步数 N = 4 后,显存占用稳定在 14GB 以内,loss 曲线平滑下降,微调效果与 4 卡 A100 跑 batch = 4 的基线相当。

目标检测模型训练:稳定小 batch 下的 BN

阶段:模型训练调优

解决的问题:检测模型(如 YOLO、Faster R-CNN)常用大 batch 训练来稳定 BatchNorm 统计量。显存不够时 batch 被迫降到 4,BN 统计量剧烈抖动,mAP 掉点。梯度累积把 4 份 batch = 4 的梯度攒成一次更新,等效 batch = 16,恢复 BN 稳定性。

具体案例:某安防团队训练工业缺陷检测模型,batch = 4 时验证集 mAP 只有 71.2%;设置累积步数 4 后等效 batch = 16,mAP 提升到 78.6%,且训练时间几乎不变(只增加少量梯度存储开销)。

多卡集群训练的兜底方案

阶段:分布式训练

解决的问题:数据并行集群中,如果总样本量小或卡数不足,单卡 batch 会很小。梯度累积让“单卡 batch × 卡数 × 累积步数”灵活凑出目标等效 batch,无需改动数据并行框架,只需在训练循环里加累积逻辑。

具体案例:某团队用 4 张 A100 训练推荐模型,目标等效 batch = 1024,单卡能放下 64,则累积步数 N = 1024 ÷ (64×4) = 4。训练一周后模型 AUC 与大 batch 基线一致,省下了再买 4 张卡的钱。

Python 演示

python46 行
import torchimport torch.nn as nn  class TinyModel(nn.Module):    def __init__(self):        super().__init__()        self.fc1 = nn.Linear(16, 32)        self.fc2 = nn.Linear(32, 4)     def forward(self, x):        return self.fc2(torch.relu(self.fc1(x)))  def build_dataloader():    xs = torch.randn(32, 16)    ys = torch.randint(0, 4, (32,))    return [(xs[i:i+4], ys[i:i+4]) for i in range(0, 32, 4)]  def main():    model = TinyModel()    optimizer = torch.optim.SGD(model.parameters(), lr=0.02)    criterion = nn.CrossEntropyLoss()     accum_steps = 4  # 攒 4 个小 batch 再更新一次    dataloader = build_dataloader()     model.train()    optimizer.zero_grad()  # 初始清零, 之后每 accum_steps 步才清零一次     for step, (x, y) in enumerate(dataloader, 1):        loss = criterion(model(x), y)        loss = loss / accum_steps  # 关键: 损失缩放, 等价于梯度取平均        loss.backward()  # 梯度累加到 .grad         if step % accum_steps == 0:            torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)            optimizer.step()       # 用累积后的梯度更新参数            optimizer.zero_grad()  # 清空梯度, 攒下一轮     print("梯度累积训练完成")  if __name__ == "__main__":    main()
目的:展示 PyTorch 中梯度累积的标准写法——损失缩放、延迟更新、统一裁剪,四步完成一次“攒钱式”参数更新。
  1. loss = loss / accum_steps把每个 micro-batch 的损失除以 N,让累积结果恰好是平均梯度而非总和。
  2. loss.backward()PyTorch 的 backward 默认累加梯度到 .grad 属性,不需要手动维护梯度列表。
  3. if step % accum_steps == 0:攒满 N 份才进入更新分支,实现“延迟更新”。
  4. torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)在 step 前对累积后的总梯度统一裁剪,防止梯度爆炸。
  5. optimizer.step()` + `optimizer.zero_grad()用累积梯度更新参数后立刻清空,开启下一轮累积。

常见误区

误区我只要设好累积步数 N,梯度累积的效果就和一次性跑大 batch 一模一样,代码都不用改。

正解数学上梯度平均是近似的,但 BatchNorm 用 micro-batch 统计量、Dropout 的随机性、学习率匹配等因素都会造成实际差异。攒钱整笔买和分期付款的“利息”不一样。实践中需要微调学习率、关注 BN 表现,才可能逼近大 batch 的效果。

为何常见:公式上 N 份梯度平均 ≈ 大梯度,很容易让人忽略框架层面的统计量差异。

误区直接把每个小 batch 的 loss 都 backward,心想“PyTorch 会帮我处理累加”。

正解PyTorch 的 backward 确实累加梯度,但累加的是“总和”而不是“平均”。不除以 N,累积梯度就是大 batch 梯度的 N 倍,相当于学习率被偷偷放大了 N 倍,极易震荡。正确做法是每个 micro-batch 的 loss 先除以 N 再 backward。

为何常见:手写循环时只关注“攒满再更新”的时机,忽略了数学上要取平均的运算。

误区我每个小 batch 都更新一次参数,多更新几次不就是累积吗?

正解这不是梯度累积,而是普通的小 batch 训练,每一步都在用不稳定的梯度更新,噪声很大。真正的累积要求攒满 N 步才 step 一次,期间只做 backward 累加,绝不触碰参数。

为何常见:把“累积”理解成“连续多次更新”,与“累积后统一更新”的语义混淆。

误区显存小,那就把累积步数设成 100、1000,反正不占显存,等效 batch 越大越稳。

正解等效 batch 过大会导致收敛变慢、泛化下降,BatchNorm 统计量偏差也更严重。经验上等效 batch 落在 32~512 区间最合适,累积步数一般 N ≤ 16。攒钱攒太久,会错失最佳“消费时机”。

为何常见:觉得“模拟大 batch = batch 越大越好”,忽略了训练动态与泛化之间的平衡。

误区保险起见,每个 micro-batch backward 后都 clip_grad_norm_ 一次,防止梯度爆炸。

正解每步裁剪会扭曲梯度方向,破坏累积的统计学意义。正确时机是在累积完成后、step 之前统一裁剪一次——这时候面对的是“完整的积蓄”,才值得做预算控制。

为何常见:把单步训练中“每步裁剪”的习惯直接迁移到累积模式,没有意识到裁剪对象从“局部”变成了“整体”。

面试题

Q1. 梯度累积的核心思想是什么?

单选题 · easy

  • A.增大单步显存占用
  • B.多次小batch梯度累加模拟大batch← 正确答案
  • C.降低模型参数总规模
  • D.减少训练的总轮次数

答案:B。梯度累积像攒钱买大件——把大 batch 拆成 N 份小 batch,每份算出的梯度先存起来,攒够 N 份再统一更新参数,从而用小显存模拟大 batch 的效果。A 说反了,C、D 与梯度累积无关。

Q2. 梯度累积时,为什么要将每个 micro-batch 的 loss 除以 N?

单选题 · mid

  • A.避免梯度爆炸
  • B.因backward累加梯度,除N等价取平均← 正确答案
  • C.加快前向传播速度
  • D.减小显存占用比例

答案:B。PyTorch 的 backward 默认把梯度累加到 .grad,不除 N 的话累积结果会是梯度的 N 倍总和,等于学习率被偷偷放大 N 倍。除以 N 让累加结果恰好是平均梯度,与数学推导一致。A、C、D 都不是 loss 缩放的原因。

Q3. 梯度累积训练中,optimizer.zero_grad() 应该在什么时机调用?

单选题 · mid

  • A.每个 micro-batch 之后
  • B.攒满N步 step 之后← 正确答案
  • C.只在训练刚开始时
  • D.每两个 micro-batch 之间

答案:B。zero_grad 的作用是清空梯度,为下一轮累积做准备。如果每个 micro-batch 后都清零,梯度就攒不起来;只有攒满 N 步并 step 之后清零,才能保证梯度跨 batch 累积。

Q4. 目标等效 batch 为 32,显存只能装下 batch = 8,累积步数 N 应为多少?

单选题 · easy

  • A.2
  • B.4← 正确答案
  • C.8
  • D.16

答案:B。N = 目标 batch ÷ 微 batch = 32 ÷ 8 = 4。也就是每天攒 8 份,攒 4 天凑齐 32 份再花。其他数值都不满足等式的整数解。

Q5. 线性缩放规则下,累积步数从 1 变为 4,学习率应如何调整?

单选题 · mid

  • A.保持不变
  • B.除以 4
  • C.乘以 4← 正确答案
  • D.乘以 2

答案:C。线性缩放规则是“等效 batch 放大几倍,学习率就放大几倍”。步数从 1 变 4,等效 batch 放大 4 倍,η 相应乘以 4,让模型在更稳定的梯度上迈更大的步子。

Q6. 梯度累积与数据并行分布式训练结合时,正确的说法有哪些?

多选题 · hard

  • A.各卡先本地累积,最后AllReduce同步← 正确答案
  • B.等效batch=单卡微batch×卡数×累积步数← 正确答案
  • C.每个小步都做AllReduce同步
  • D.累积步数可当作梯度同步的间隔← 正确答案

答案:ABD。多卡配合时,每卡先本地攒 N 步梯度,攒满后 AllReduce 一次再 step,所以累积步数就是梯度同步间隔,等效 batch = 单卡微 batch × 卡数 × N。若每个小步都 AllReduce,通信开销巨大,失去省显存的意义,因此 C 错误。

Q7. 梯度累积训练大模型时,BatchNorm 为什么容易出问题?

单选题 · mid

  • A.BN用单批统计量,与大batch估计有偏差← 正确答案
  • B.BN在CNN中不可实现
  • C.BN参数量比卷积层还大
  • D.BN只在推理阶段才运行

答案:A。BatchNorm 训练时用当前 micro-batch 的均值、方差归一化,小 batch 统计量噪声大,与大 batch 的系统性估计有偏差,导致模型收敛不稳。B、C、D 都是对 BN 的错误描述。

Q8. 哪些是梯度累积的典型适用场景?

多选题 · hard

  • A.16GB 单卡想训练大模型← 正确答案
  • B.小batch导致BatchNorm统计不稳← 正确答案
  • C.集群吞吐已饱和只需加硬件
  • D.显存有限却要等效大batch训练← 正确答案

答案:ABD。梯度累积解决“显存小还想跑大 batch”的痛点:单卡显存受限、小 batch 导致 BN 不稳定时都适用。C 的场景是加硬件解决吞吐问题,与梯度累积无关。

Q9. 关于梯度累积中的梯度裁剪,正确做法是什么?

单选题 · hard

  • A.每个小步backward后立即裁剪
  • B.累积完成后更新参数前裁一次← 正确答案
  • C.梯度累积训练中从不使用裁剪
  • D.在损失缩放之前先进行裁剪

答案:B。裁剪的对象应该是“完整的累积梯度”。每步裁剪会破坏各个 micro-batch 梯度的方向构成,使累积失去意义;裁剪也要在 loss 缩放、累积完成之后、step 之前统一执行一次。

Q10. 梯度累积训练中,模型参数在哪个时刻发生变化?

单选题 · easy

  • A.每个小batch前向传播之后
  • B.每个小batch反向传播之后
  • C.攒满N步且step之后才变化← 正确答案
  • D.每次加载新一批数据之后

答案:C。梯度累积把“算梯度”和“更新参数”解耦。前向、反向只是往储蓄罐里存钱,只有攒满 N 步执行 optimizer.step() 时,参数才会真正被更新——这正是“延迟更新”的含义。