PyTorch自动求导Autograd详解|requires_grad、backward、grad、梯度清零

标签:pytorch、autograd、自动求导、反向传播

前言

神经网络训练本质就是:前向计算损失 → 反向求梯度 → 更新参数。

PyTorch的Autograd自动求导引擎帮我们自动完成链式求导,不用手写导数。

核心概念一句话:

只要张量设置requires_grad=True,PyTorch会自动构建计算图,调用backward()就能自动算出梯度。

一、基础入门:requires_grad

python 复制代码
import torch

# requires_grad=False 默认,不记录梯度
x = torch.tensor([1.0, 2.0], requires_grad=False)
# requires_grad=True,开启梯度追踪
w = torch.tensor([3.0, 4.0], requires_grad=True)

y = w * x
print(y)
print(y.requires_grad)
  • requires_grad=True:这个张量参与计算,会记录计算图,后续可以求导
  • 叶子张量:自己创建、不是运算生成的张量,比如上面的w
  • 非叶子张量:由运算得到的y,只保存计算关系,默认不保留梯度

注意:整数张量默认不能求导,必须是浮点型(float32/float64)

二、backward() 反向求导

举一个简单函数:out=w0∗x0+w1∗x1out = w_0*x_0 + w_1*x_1out=w0∗x0+w1∗x1

我们对w求导:∂out∂w=x\frac{\partial out}{\partial w}=x∂w∂out=x

python 复制代码
import torch

x = torch.tensor([1.0, 2.0])
w = torch.tensor([3.0, 4.0], requires_grad=True)

out = torch.sum(w * x)
# 反向传播,计算梯度
out.backward()

# 查看w的梯度
print(w.grad)

运行输出:tensor([1., 2.]),和我们手动求导结果一致。

⚠️ 重要规则:

  • 标量(单个数值)可以直接调用backward()
  • 如果out是向量,backward需要传入权重向量 backward(torch.ones_like(out))

三、计算图理解

上面例子的计算图:

w(叶子节点) → 乘法 → sum → out(标量)

Autograd沿着图反向走,链式法则求导,梯度存在.grad属性里。

四、梯度累加问题(高频踩坑点)

PyTorch梯度不会自动清零,会累加!

python 复制代码
w = torch.tensor([3.0, 4.0], requires_grad=True)
x = torch.tensor([1.0, 2.0])

out = torch.sum(w * x)
out.backward()
print(w.grad) # 第一次梯度

out = torch.sum(w * x)
out.backward()
print(w.grad) # 梯度叠加,变成 [2,4]

训练循环里必须手动清零梯度:

python 复制代码
w.grad.zero_()

带下划线_代表原地in-place操作

五、with torch.no_grad() 禁用梯度

预测、验证阶段不需要计算梯度,关闭autograd可以节省显存、提速:

python 复制代码
with torch.no_grad():
    res = w * x
print(res.requires_grad) # False

✅ 场景:模型推理、验证集评估,不要占用显存存计算图。

六、detach() 截断计算图

如果想要一个张量,数值和原张量一样,但是脱离计算图、不追踪梯度:

python 复制代码
y = w * x
y_detach = y.detach()
print(y_detach.requires_grad) # False

区别:

  • torch.no_grad():上下文管理器,里面所有运算都不建图
  • .detach():单独把某个张量剥离出计算图

七、叶子张量 & retain_graph

正常情况,backward跑完之后,计算图会被释放,不能二次backward。

如果需要多次反向传播,设置 retain_graph=True

python 复制代码
out.backward(retain_graph=True)

慎用,会占用更多显存,一般调试才用。

八、完整小demo(模拟一轮训练)

python 复制代码
import torch

# 参数
w = torch.tensor([1.0], requires_grad=True)
# 输入
x = torch.tensor([2.0])
# 标签
y_true = torch.tensor([5.0])

# 前向
y_pred = w * x
loss = (y_pred - y_true) ** 2

# 反向求导
loss.backward()
print("w的梯度:", w.grad)

# 参数更新(注意:更新参数要关掉梯度!)
with torch.no_grad():
    w -= 0.01 * w.grad

# 梯度清零,准备下一轮
w.grad.zero_()
print("更新后的w:", w)

这就是SGD梯度下降最简单的原生实现,后面optimizer底层就是这套逻辑。

九、常见坑汇总

问题 原因 解决
报错:Can only create a graph from a scalar backward的输出是向量,不是标量 传入backward(torch.ones_like(out))或者sum
梯度越来越大,loss爆炸 梯度没有清零,不断累加 每次循环末尾 xxx.grad.zero_()
推理时显存占用很高 还在构建计算图 用with torch.no_grad()包裹推理代码
报叶子节点错误 对非叶子张量尝试修改grad 只对自己定义的参数张量做梯度更新

十、小结

  1. requires_grad=True:开启梯度追踪,构建计算图
  2. backward():反向传播,自动求导,梯度存入.grad
  3. 梯度默认累加,训练循环必须清零
  4. torch.no_grad()推理关闭梯度,省显存
  5. 自动求导是后面神经网络、loss、优化器的底层基础
相关推荐
syagain_zsx1 小时前
ODB 与 MySQL:C++ 里的 ORM 实践(类即表、事务铁律与查询封装)
开发语言·数据库·c++·mysql·工具库·1024程序员节
小嘉丶学长1 小时前
Day08 · 字符串(二)
服务器·开发语言·python·学习·1024程序员节
小毕超1 小时前
从推理到训练:Intel Ultra 核显 GPU 加速 YOLO 模型训练
pytorch·yolo·gpu·intel
poiu12346572 小时前
使用AI辅助学习与资料整理的个人体会
人工智能·学习
4SAPI2 小时前
电商全链路内容自动化生成大模型 API 生产选型指南:4SAPI、硅基流动与 OpenRouter 的架构权衡
运维·人工智能·架构·自动化·1024程序员节
深圳老胡2 小时前
STM32F103 硬件 SPI 与软件 IO 模拟 SPI 最高速度差异详解
笔记·stm32·单片机·代码规范·1024程序员节
海绵宝宝的月光宝盒2 小时前
7-AutoCAD-参考线、多行文字、填充命令应用
经验分享·笔记·学习·职场和发展·课程设计·制造·cad
CappuccinoRose2 小时前
滚动交互体系
前端·javascript·交互·html5·1024程序员节
画中有画3 小时前
论可观测性架构在分布式系统中的应用
1024程序员节