修改Tensor的.data属性后计算图与梯度计算的困惑
.data修改Tensor后梯度异常的原因 首先明确torch.autograd.grad(out, a, weight)的计算逻辑:这个函数计算的是加权和sum(weight * out)对a的梯度,对于每个元素a[i],梯度值为weight[i] * d(out[i])/d(a[i])。而sigmoid函数的导数公式是:d(sigmoid(x))/dx = sigmoid(x) * (1 - sigmoid(x)),也就是out[i] * (1 - out[i])(因为out[i] = sigmoid(a[i]))。
第一段代码的梯度计算
你通过c = out.data拿到了out的底层存储引用,直接修改c的数值后,out的实际值已经变成了[1, 3, 4](.data绕过了计算图,直接修改原Tensor的数值)。反向传播时,PyTorch会用out的当前数值来计算导数:
- 第一个元素:
1 * (1 - 1) = 0,乘以weight 1,得到0; - 第二个元素:
3 * (1 - 3) = -6,乘以weight 1,得到-6; - 第三个元素:
4 * (1 - 4) = -12,乘以weight 1,得到-12;
这和你得到的d = tensor([0., -6., -12.])完全匹配。
第二段代码的梯度计算
同样,你用c.zero_()把out的数值改成了[0, 0, 0],反向传播时计算导数:
- 每个元素都是
0 * (1 - 0) = 0,乘以weight 1后结果都是0;
所以得到d = tensor([0., 0., 0.])。
核心原因
.data直接操作Tensor的底层存储,不会被计算图追踪,这会导致正向传播的计算记录和反向传播时使用的Tensor数值不一致。虽然两段代码的正向流程看起来一样,但你修改了out的实际数值,反向传播时导数计算依赖的是修改后的out值,自然会得到不同的梯度结果。
注意事项
现在PyTorch官方已经不推荐使用.data,建议用.detach()获取脱离计算图的Tensor,但要注意.detach()返回的Tensor和原Tensor共享存储,修改它依然会影响原Tensor。如果需要修改且不影响原Tensor,应该用.detach().clone()。
内容的提问来源于stack exchange,提问作者BaeHann

