Pytorch-获取中间变量/张量的梯度

时间:2018-11-09 03:49:50

标签: artificial-intelligence pytorch gradient-descent

作为pytorch框架(0.4.1)中的一个练习,我试图在一个简单的线性层(Z = X.W + B)中显示X的梯度(gX或dSdX)。为了简化我的玩具示例,我从Z的总和(不是损失)中倒退()。

总而言之,我希望gX(dSdX)为S = sum(XW + B)。

问题是Z的梯度(dSdZ)为无。结果,gX当然也是错误的。

import torch
X = torch.tensor([[0.5, 0.3, 2.1], [0.2, 0.1, 1.1]], requires_grad=True)
W = torch.tensor([[2.1, 1.5], [-1.4, 0.5], [0.2, 1.1]])
B = torch.tensor([1.1, -0.3])
Z = torch.nn.functional.linear(X, weight=W.t(), bias=B)
S = torch.sum(Z)
S.backward()
print("Z:\n", Z)
print("gZ:\n", Z.grad)
print("gX:\n", X.grad)

结果:

Z:
 tensor([[2.1500, 2.9100],
        [1.6000, 1.2600]], grad_fn=<ThAddmmBackward>)
gZ:
 None
gX:
 tensor([[ 3.6000, -0.9000,  1.3000],
        [ 3.6000, -0.9000,  1.3000]])

如果我使用nn.Module,则结果完全相同:

class Net1Linear(torch.nn.Module):
    def __init__(self, wi, wo,W,B):
        super(Net1Linear, self).__init__()
        self.linear1 = torch.nn.Linear(wi, wo)
        self.linear1.weight = torch.nn.Parameter(W.t())
        self.linear1.bias = torch.nn.Parameter(B)
    def forward(self, x):
        return self.linear1(x)
net = Net1Linear(3,2,W,B)
Z = net(X)
S = torch.sum(Z)
S.backward()
print("Z:\n", Z)
print("gZ:\n", Z.grad)
print("gX:\n", X.grad)

3 个答案:

答案 0 :(得分:2)

首先,您只需要为张量计算梯度,即可通过将requires_grad设置为True来启用梯度。

因此您的输出与预期的一样。您将获得X的渐变。

出于性能方面的考虑,PyTorch不保存中间结果的梯度。因此,您将获得将requires_grad设置为True的那些张量的梯度。

但是,您可以在计算过程中使用register_hook提取中间等级或手动保存它。在这里,我只是将其保存到张量grad的{​​{1}}变量中:

Z

这将输出:

import torch

# function to extract grad
def set_grad(var):
    def hook(grad):
        var.grad = grad
    return hook

X = torch.tensor([[0.5, 0.3, 2.1], [0.2, 0.1, 1.1]], requires_grad=True)
W = torch.tensor([[2.1, 1.5], [-1.4, 0.5], [0.2, 1.1]])
B = torch.tensor([1.1, -0.3])
Z = torch.nn.functional.linear(X, weight=W.t(), bias=B)

# register_hook for Z
Z.register_hook(set_grad(Z))

S = torch.sum(Z)
S.backward()
print("Z:\n", Z)
print("gZ:\n", Z.grad)
print("gX:\n", X.grad)

希望这会有所帮助!

顺便说一句:通常,您会希望为参数激活渐变-因此权重和偏差。因为您现在使用优化器时会做的是更改输入Z: tensor([[2.1500, 2.9100], [1.6000, 1.2600]], grad_fn=<ThAddmmBackward>) gZ: tensor([[1., 1.], [1., 1.]]) gX: tensor([[ 3.6000, -0.9000, 1.3000], [ 3.6000, -0.9000, 1.3000]]) 而不是权重X和偏差W。因此通常在这种情况下为BW激活梯度。

答案 1 :(得分:1)

有一种简单得多的方法。只需使用retain_grad()

https://pytorch.org/docs/stable/autograd.html#torch.Tensor.retain_grad

Z.retain_grad()

在致电backward()

之前

答案 2 :(得分:0)

蓝凤凰,谢谢您的回答。我很高兴听到关于register_hook()的消息。

使我认为我有一个错误的gX的原因是它独立于X的值。我将必须做数学运算才能理解它。但是,使用CCE Loss代替SUM可以使事情变得更加干净。因此,我为可能感兴趣的人更新了该示例。在这种情况下,使用SUM是个坏主意。

T_dec = torch.tensor([0, 1])
X = torch.tensor([[0.5, 0.8, 2.1], [0.7, 0.1, 1.1]], requires_grad=True)
W = torch.tensor([[2.7, 0.5], [-1.4, 0.5], [0.2, 1.1]])
B = torch.tensor([1.1, -0.3])
Z = torch.nn.functional.linear(X, weight=W.t(), bias=B)
print("Z:\n", Z)
L = torch.nn.CrossEntropyLoss()(Z,T_dec)
Z.register_hook(lambda gZ: print("gZ:\n",gZ))
L.backward()
print("gX:\n", X.grad)

结果:

Z:
 tensor([[1.7500, 2.6600],
        [3.0700, 1.3100]], grad_fn=<ThAddmmBackward>)
gZ:
 tensor([[-0.3565,  0.3565],
        [ 0.4266, -0.4266]])
gX:
 tensor([[-0.7843,  0.6774,  0.3209],
        [ 0.9385, -0.8105, -0.3839]])