叶子节点和梯度
/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):
- 它是计算出来的结果:
o由n经过tanh得到,所以它不是你直接创建的“叶子张量”(is_leaf: False)。 - 它继承了梯度追踪:因为它的“祖先”(
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 的存储则遵循“只有叶子张量默认存储”的规则。
- 点赞
- 收藏
- 关注作者
评论(0)