核心要点

  • 因为 backward 是把梯度累加到 .grad,不清零会让上一 batch 梯度残留、等价错误的大 batch

  • 标准时机是每个训练 step 开始、backward 之前;放错位置(如每 epoch)会直接训崩

  • optimizer.zero_grad 清零该优化器管的参数,model.zero_grad 清零模型全部参数,常用前者

  • 梯度累积是唯一例外:故意攒 N 个小 batch 梯度再 step,中间步不 zero_grad 以模拟大 batch

简要回答

PyTorchoptimizer.zero_grad()model.zero_grad() 用于清零梯度

原因loss.backward() 将梯度累加param.grad(便于梯度累积)

标准回答

PyTorch 中 optimizer.zero_grad()model.zero_grad() 用于清零梯度

原因loss.backward() 将梯度累加param.grad(便于梯度累积)。若不清零,上一 batch 的梯度残留,等价错误的大 batch 或发散训练。

标准时机——每个训练 step 开始前

python
for x, y in loader:
    optimizer.zero_grad()      # ① 清零
    loss = criterion(model(x), y)
    loss.backward()            # ② 累加梯度
    optimizer.step()           # ③ 更新

set_to_none=True(推荐):optimizer.zero_grad(set_to_none=True) 释放 .grad 引用,略省内存,下轮 backward 重新分配。

例外——梯度累积:每 N 步才 step(),中间步 zero_grad,手动累积 N 个小 batch 梯度后一次更新,模拟大 batch。详见 深度学习训练技巧

常见误区

⚠️ 常见踩坑

每 epoch 而非每 step 清零;梯度累积时误每步清零;backward 后忘记 step 只清零。

追问

追问 1zero_grad 和 grad.zero_() 区别?

optimizer.zero_grad() 是封装好的高层接口,遍历它管理的所有参数并清零(默认 set_to_none=True 把 .grad 置 None);param.grad.zero_() 是手动对单个张量的 .grad 原地清零,只在你想精细控制某些参数时用,且若 .grad 为 None 会报错。日常用前者。

追问 2忘记 zero_grad 有什么现象?

梯度逐 step 累加越来越大,更新步长失控,表现为 loss 剧烈震荡甚至变 NaN、不收敛。它不会报错,所以很隐蔽——训练发散且找不到代码错误时,先检查是否漏了 zero_grad。

追问 3推理需要 zero_grad 吗?

不需要。推理不做反向传播、不产生梯度,自然无需清零。正确做法是用 with torch.no_grad()(或 inference_mode)关闭梯度记录并 model.eval(),既省显存又避免 BN/Dropout 行为出错。

延伸学习

与本题相关的知识库文章、术语、工具与行业资讯。

🛠️ AI 工具

  • Pytorch

    Meta 开源的深度学习框架,100K+ stars。以动态计算图和 Pythonic 风格著称,在学术界和工业界都有广泛应用,支持分布式训练、移动端部署和 ONNX 导出

  • Tensorflow

    全球最流行的机器学习框架之一,195K+ stars。Google 开源的端到端 ML 平台,支持 TensorFlow、Keras 等多种 API,覆盖深度学习、强化学习、移动端部署等全场景,是 AI 工程师的必备工具