同一输入下torch.autograd.grad计算结果差异的原因探究
问题:torch.autograd.grad计算梯度结果不一致的原因
我尝试用torch.autograd.grad()计算一些点的梯度,代码如下:
for batch in data_loader: batch_coordinates = batch[0] out = model(batch_coordinates.requires_grad_()) x1, y1 = batch_coordinates[:, 0].reshape(-1, 1), batch_coordinates[:, 1].reshape(-1, 1) y = custom_output(out, x1, y1) grad_1 = torch.autograd.grad(y.sum(), batch_coordinates, create_graph=True) # 计算梯度 grad_2 = torch.autograd.grad(y.sum(), (x1, y1), create_graph=True) # 计算梯度
其中batch_coordinates是维度为torch.Size([n,2])的张量,n为点的数量;model是一个简单的三层神经网络;custom_output(out, x1, y1)是一个插值网络输出的函数,输入为点的x、y坐标和网络输出。
为什么grad_1和grad_2的计算结果存在差异(已经考虑了grad_2是列表的情况)?
输出结果如下:
GRAD MINIBATCH COORDINATES: (tensor([[-6.5256e-03, 1.5925e-03], [-6.1777e-03, -8.8977e-05], [-2.5146e-03, 3.1782e-03], [-4.3833e-03, 2.2729e-03], [-6.6386e-03, 3.9808e-04], [-5.9642e-03, -2.2408e-04], [ 1.0123e-03, 4.7309e-03], [ 3.8920e-02, -1.3823e-01], [-6.2750e+00, 1.4732e+00], [-5.8813e-03, -2.6810e-04]], grad_fn=<AddBackward0>),) GRAD X1, Y1: (tensor([[ 0.0000e+00], [ 0.0000e+00], [ 0.0000e+00], [ 0.0000e+00], [-2.0982e-05], [ 0.0000e+00], [-1.5622e-04], [ 4.5191e-02], [-6.2711e+00], [ 0.0000e+00]], grad_fn=<AddBackward0>), tensor([[ 0.0000e+00], [ 0.0000e+00], [ 0.0000e+00], [ 0.0000e+00], [ 7.6683e-06], [ 0.0000e+00], [-2.7294e-05], [-1.3825e-01], [ 1.4730e+00], [ 0.0000e+00]], grad_fn=<AddBackward0>))
解答
核心原因是**x1、y1是batch_coordinates的视图而非独立张量**,PyTorch自动微分系统对视图张量的梯度追踪范围和原张量不同:
- 对
batch_coordinates求梯度时,PyTorch会完整追踪从batch_coordinates到y.sum()的全路径——包括model前向传播和custom_output的插值逻辑,所以grad_1是完整的梯度值。 x1、y1是原张量的切片/重塑视图,和原张量共享底层数据,但计算图中model的输入是batch_coordinates而非x1/y1,因此对x1/y1求梯度时,PyTorch只会计算custom_output中直接依赖x1/y1的部分梯度,忽略model内部的反向传播路径,这就导致大部分点的梯度为0,仅少数因custom_output直接用到坐标才产生非零值。
要让两者结果一致,需让x1、y1关联到完整的计算图。可以通过克隆原张量再拆分实现:
batch_clone = batch_coordinates.clone().requires_grad_() x1, y1 = batch_clone[:, 0].reshape(-1, 1), batch_clone[:, 1].reshape(-1, 1) out = model(batch_clone) y = custom_output(out, x1, y1) grad_1 = torch.autograd.grad(y.sum(), batch_clone, create_graph=True) grad_2 = torch.autograd.grad(y.sum(), (x1, y1), create_graph=True)
此时x1、y1是克隆张量的视图,计算图会完整追踪所有路径,grad_2的结果会和grad_1的对应维度完全匹配。
内容的提问来源于stack exchange,提问作者eliss
相关产品推荐
相关产品推荐

