返回关卡地图
预判 0 / 3 闯关题:0 / 3 XP 0 / 300

Level 12 | 零基础导学关卡

梯度累积

Gradient Accumulation

先通过图解和小例子理解原理,再做预测与练习;不懂的地方可以反复尝试,最后把理解写进官方 Notebook。

本课官方 Notebook ↗
12_Gradient_Accumulation.ipynb 训练微调梯度累积显存优化
Mission 1 概念练习

先拆 batch:输入与标签必须切同一段

🎯 先猜一猜

batch_size=8、accum_steps=4,循环正在处理 idx=3。

这一轮 xb 应该取哪一段?

先猜一个答案,再对照下面的讲解。可以随时修改选择,猜错会得到解释。

先补的知识

  • 张量的第 0 维是 batch 维。Notebook 中 x.shape=[8,4]、y.shape=[8,2],同一行的 x 和 y 属于同一条样本。
  • accum_steps 表示把完整 batch 分成几段;micro_size=x.size(0)//accum_steps 表示每段有几条样本。
  • Python 切片 start:stop 包含 start、不包含 stop。普通切片会保留原张量的 dtype 和 device。
  • Notebook 已先检查 batch size 能被 accum_steps 整除,因此本题不需要处理最后一段大小不同的情况。

图解原理

把 8 份练习按顺序分成 4 叠,每叠 2 份。第 idx 轮只取第 idx 叠,而且题目 x 与答案 y 必须使用相同起止下标;否则模型会拿一条样本的输入去拟合另一条样本的目标。

完整 batchx 有 8 行,y 也有 8 行 每段大小micro_size = 8 // 4 = 2 第 idx 段start=idx×2,stop=(idx+1)×2 本轮 forward只处理对应的 2 行
对象完整 shapeidx=2 的切片切片后 shape
x → xb[8,4]x[4:6][2,4]
y → yb[8,2]y[4:6][2,2]

正确:沿样本维切

tensor[start:stop] 省略了其余维度,等价于沿第 0 维取行。

错误:沿特征维切

x[:, start:stop] 会改变每条样本的特征数,第一层 Linear 的输入维度会对不上。

语法热身:把 12 位读者分成 3 个阅读小组

pages = torch.randn(12, 5)
ratings = torch.randn(12, 1)
group_count = 3
group_size = pages.size(0) // group_count

for group_idx in range(group_count):
    start = group_idx * group_size
    stop = (group_idx + 1) * group_size
    page_group = pages[start:stop]
    rating_group = ratings[start:stop]
    print(page_group.shape, rating_group.shape)

把独立例子迁移回 Notebook

  • pages / ratings 对应 x / y;两者必须共享同一个 start:stop
  • group_count / group_idx 对应 accum_steps / idx
  • group_size 对应 Notebook 已经算好的 micro_size
  • page_group / rating_group 对应当前循环的 xb / yb

巩固一下

四轮切片 [0:2]、[2:4]、[4:6]、[6:8] 共同满足什么条件?

学完这一段,试着做

用一个小动作确认自己理解了;最后再进入官方题目。

Mission 2 概念练习

再攒梯度:先缩放 loss,再连续 backward

🎯 先猜一猜

四个等大 micro-batch 都用了 mean loss,但没有除以 accum_steps。

累积梯度相对完整 batch 的 mean 梯度最可能怎样?

先猜一个答案,再对照下面的讲解。可以随时修改选择,猜错会得到解释。

先补的知识

  • MSELoss(reduction='mean') 为每个 micro-batch 返回一个 0 维浮点 Tensor。
  • PyTorch 的 parameter.grad 默认会累加;连续 backward 不会自动清空前一次梯度。
  • 等大 micro-batch 的 full-batch mean 梯度,等于各段 mean 梯度的平均,而不是总和。
  • detach().item() 会得到只用于日志的 Python float;它没有计算图,不能调用 backward。

图解原理

四个 micro-batch 会给参数提交四份梯度。若每份都按完整权重相加,更新量会放大四倍;把每段 loss 先除以 accum_steps,相当于每份只占 1/4,四份累加后才与完整 batch 的平均梯度同尺度。

漏掉缩放

g1g2g3Σg

四段 mean 梯度直接求和,更新幅度约放大 accum_steps 倍。

先除 accum_steps

g1/Kg2/Kg3/KΣg/K

缩放后的梯度累积起来,等价于完整 batch 的 mean。

1pred = model(xb)输入 [micro_size,4],输出 [micro_size,2]
2criterion(pred, yb)得到连接计算图的标量 loss
3loss / accum_steps在反传前恢复大 batch 的梯度尺度
4loss.backward()把当前段梯度加进 parameter.grad
5loss.detach().item()只把数值加进 total_loss
计算图和日志是两条用途不同的路径:缩放后的 Tensor 负责 backward;detach 后的 Python 数值负责显示和返回。

语法热身:两批传感器样本共同形成一次平均更新

parts = [(sensor_a, target_a), (sensor_b, target_b)]
reported = 0.0

for part_x, part_y in parts:
    estimate = net(part_x)
    part_loss = criterion(estimate, part_y) / len(parts)
    part_loss.backward()
    reported += part_loss.detach().item()

把独立例子迁移回 Notebook

  • parts 的数量对应 accum_steps
  • part_x / part_y 对应 xb / ybestimate 对应 pred
  • part_loss 对应 Notebook 的 loss;除法必须发生在 backward 之前。
  • reported 对应 total_loss,只累计 detach 后的数值。

巩固一下

下面哪个对象应该调用 backward?

学完这一段,试着做

用一个小动作确认自己理解了;最后再进入官方题目。

Mission 3 概念练习

最后只更新一次:让参数更新真正等价

🎯 先猜一猜

若把 optimizer.step() 放进 micro-batch 循环,每一段后都更新参数。

为什么会破坏与 full batch 的等价性?

先猜一个答案,再对照下面的讲解。可以随时修改选择,猜错会得到解释。

先补的知识

  • optimizer.step() 读取参数当前的 .grad 并更新参数;它不会重新计算梯度。
  • optimizer.zero_grad() 清除已使用的梯度,为下一个有效 batch 做准备。
  • 一次完整 batch 更新只对应一次 optimizer.step,因此累积路径也必须在循环外 step 一次。
  • 测试从同一个 base_model 深拷贝两个模型,并给两边相同的 SGD 学习率,最后逐参数 allclose。

图解原理

micro-batch 循环只负责收集四份建议,不能中途改模型。四段都在同一组旧参数上完成 forward/backward 后,优化器才统一移动一次;这样才能和 full-batch 路径的一次更新比较。

周期开始zero_grad 清旧梯度 循环内部4 次 forward/backward,只累加 循环结束optimizer.step 更新一次 收尾zero_grad 并返回 total_loss

Full batch

8 条样本 → 1 次 backward → 1 次 step。

Accumulation

4×2 条样本 → 4 次 backward → 1 次 step。

核心断言每对参数都满足 torch.allclose(..., atol=1e-6) 测试含义切片、loss 缩放与 step 时机必须同时正确 返回值total_loss 用于显示,不直接替代参数一致性检查

语法热身:三段销量数据只触发一次参数更新

optimizer.zero_grad()
for sales_x, sales_y in sales_parts:
    prediction = model(sales_x)
    scaled = criterion(prediction, sales_y) / len(sales_parts)
    scaled.backward()

optimizer.step()
optimizer.zero_grad()

把独立例子迁移回 Notebook

  • sales_parts 循环对应 Notebook 的 micro-batch 循环。
  • 循环内部只有 forward、loss 和 backward;这里不能 step。
  • 循环后的 optimizer.step() 对应 TODO 3 的唯一参数更新。
  • 更新后清梯度,再返回前一任务累计好的 total_loss

巩固一下

Notebook 最终如何验收梯度累积?

学完这一段,试着做

用一个小动作确认自己理解了;最后再进入官方题目。

把理解变成自己的代码

准备好,去官方题目试一试

在本页用小例子建立直觉,再去官方 Notebook 完成实现。先运行你自己的测试,遇到困难时再查看官方提示与参考答案。

前往本课官方 Notebook ↗

这节课会遇到的代码对象

TinyRegressor · forward · slice_micro_batch · train_step_full_batch · train_step_with_accumulation · counted_full_step · counted_accum_step

先找题目里的输入、输出与 TODO,再把本课的手算过程对应进去;以官方题目中的函数说明和测试为准。

练习来源:Datawhale 官方仓库 · 518cc45。这里的入口直接打开官方版本,不读取或分享你的本地 Notebook。

完成 3 道闯关题后,本关即算完成;作业 checklist 用来辅助你回 notebook 练习。
进度只保存在当前浏览器 localStorage,分享 HTML 不会带走你的记录。
上一关:L11 下一关:L13