叶子节点和梯度

举报
黄生 发表于 2026/10/12 12:21:45 2026/10/12
【摘要】 /tmp/ipykernel_98887/3612698947.py:1: UserWarning:The .grad attribute of a Tensor that is not a leaf Tensor is being accessed. Its .grad attribute won't be populated during autograd.backward(). If ...
/tmp/ipykernel_98887/3612698947.py:1: UserWarning:
The .grad attribute of a Tensor that is not a leaf Tensor is being accessed. Its .grad attribute won't be populated during autograd.backward(). If you indeed want the .grad field to be populated for a non-leaf Tensor, use .retain_grad() on the non-leaf Tensor. If you access the non-leaf Tensor by mistake, make sure you access the leaf Tensor instead.
See github.com/pytorch/pytorch/pull/30531 for more information. (Triggered internally at /pytorch/build/aten/src/ATen/core/TensorBody.h:489.)
  o.grad.item()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[10], line 1
----> 1 o.grad.item()

AttributeError: 'NoneType' object has no attribute 'item'

因为正在访问一个**非叶子张量(non-leaf tensor)**的 .grad 属性,而 PyTorch 默认不会为非叶子张量保留梯度,所以 .grad 是 None。

  • 叶子张量(leaf tensor):通常是用户直接创建的、requires_grad=True 的张量(例如模型参数、输入)。反向传播后它们的 .grad 会被填充。
  • 非叶子张量(non-leaf tensor):由其他张量经过运算得到的中间结果。为了节省内存,PyTorch 默认不保存它们的梯度(是不保存,没有说不计算),所以 .grad 是 None。

方案 1:如果 o 是中间结果,想拿到它的梯度 :用 retain_grad()

o = some_computation(x)   # o 是非叶子张量
o.retain_grad()           # 显式要求保留梯度
loss = o.sum()            # 或其他后续计算
loss.backward()

print(o.grad.item())      # 现在可以正常访问

注意:retain_grad() 必须在 backward() 之前调用。

方案 2:如果 o 是最终标量损失 → 用 o.item() 而不是 o.grad.item()

print(o.item())   # 直接取数值

损失本身没有 .grad,只有参与计算的张量才有。

方案 3:如果你想看叶子张量的梯度

loss.backward()
print(x.grad.item())   # x 是叶子张量(如 torch.randn(3, requires_grad=True))

快速判断表

你的目的 应该用
拿中间张量 o 的梯度 o.retain_grad() 后再 o.grad.item()
拿 loss 的数值 o.item()
拿叶子张量(参数/输入)的梯度 leaf.grad.item()

排查建议

在报错那行之前加一段调试代码:

print("o.is_leaf:", o.is_leaf)
print("o.requires_grad:", o.requires_grad)
print("o.grad is None:", o.grad is None)
  • 如果 is_leaf=False → 用 retain_grad()
  • 如果 requires_grad=False → 该张量根本没参与梯度计算
  • 如果 grad is None 且 is_leaf=True → 说明还没调用 backward(),或该张量不在计算图中

附:requires_grad=True 的真正含义

requires_grad 这个属性只表示一件事:这个张量是否属于自动求导计算图的一部分,也就是说,PyTorch 是否需要为它追踪梯度。

对于 o = torch.tanh(n):

  1. 它是计算出来的结果:o 由 n 经过 tanh 得到,所以它不是你直接创建的“叶子张量”(is_leaf: False)。
  2. 它继承了梯度追踪:因为它的“祖先”(x1, w1, b 等)都设置了 requires_grad=True,PyTorch 在计算 o 时,为了以后能对祖先求导,就必须把 o 也纳入计算图中。所以 o.requires_grad 自然就是 True。

简单说:o.requires_grad=True 表示梯度可以通过 o 回传到之前的变量,但这不意味着 o 自己会保存一份梯度。

为什么有那个 UserWarning?

PyTorch 的开发者(在UserWarning里引用的那个 PR #30531 里)特意加了这个警告,就是为了防止这种误解。当你访问一个非叶子张量的 .grad 时,它会提醒你:“你正在访问一个不会自动填充梯度的属性,这可能是你的失误。” 这正好印证了:requires_grad=True 的中间变量,其 .grad 依然是 None,这是一个很容易踩的坑。

如果你确实需要 o 这个中间结果的梯度值,需要在反向传播前显式地告诉 PyTorch 保存它:

o = torch.tanh(n)
o.retain_grad()   # 手动要求保留 o 的梯度
o.backward()
print(o.grad)     # 现在能打印出值了

这个设计意味着 requires_grad 只管“能不能求导”,而 .grad 的存储则遵循“只有叶子张量默认存储”的规则。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。