Pytorch,梯度参数是什么

Pytorch, what are the gradient arguments

我正在阅读 PyTorch 的文档并找到了他们写的示例

gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)

其中 x 是初始变量,从中构造 y(3 向量)。问题是,梯度张量的 0.1、1.0 和 0.0001 参数是什么?文档对此不是很清楚。

在这里,forward() 的输出,即 y 是一个 3 向量。

这三个值是网络输出端的梯度。如果 y 是最终输出,它们通常设置为 1.0,但也可以有其他值,尤其是当 y 是更大网络的一部分时。

例如。如果 x 是输入,y = [y1, y2, y3] 是用于计算最终输出 z,

的中间输出

然后,

dz/dx = dz/dy1 * dy1/dx + dz/dy2 * dy2/dx + dz/dy3 * dy3/dx

所以在这里,向后的三个值是

[dz/dy1, dz/dy2, dz/dy3]

然后 backward() 计算 dz/dx

通常,您的计算图有一个标量输出表示 loss。然后你可以计算 loss w.r.t 的梯度。 loss.backward() 的权重 (w)。 backward() 的默认参数是 1.0.

如果您的输出有多个值(例如 loss=[loss1, loss2, loss3]),您可以计算损失的梯度 w.r.t。权重 loss.backward(torch.FloatTensor([1.0, 1.0, 1.0])).

此外,如果你想为不同的损失增加权重或重要性,你可以使用loss.backward(torch.FloatTensor([-0.1, 1.0, 0.0001]))

这意味着同时计算-0.1*d(loss1)/dw, d(loss2)/dw, 0.0001*d(loss3)/dw

说明

对于神经网络,我们通常使用 loss 来评估网络学习分类输入图像(或其他任务)的程度。 loss 项通常是标量值。为了更新网络的参数,我们需要计算lossw.r.t对参数的梯度,在计算图中实际上是leaf node(顺便说一句,这些参数主要是卷积层、线性层等各层的权重和偏置。

根据链式法则,为了计算lossw.r.t到叶节点的梯度,我们可以计算lossw.r.t一些中间变量的导数,和中间变量 w.r.t 到叶变量的梯度,做点积并将所有这些加起来。

Variablebackward() method is used to calculate a weighted sum of each element of a Variable w.r.t the leaf Variablegradient参数。这些权重只是最终lossw.r.t的导数中间变量的每个元素。

一个具体的例子

让我们举一个具体而简单的例子来理解这一点。

from torch.autograd import Variable
import torch
x = Variable(torch.FloatTensor([[1, 2, 3, 4]]), requires_grad=True)
z = 2*x
loss = z.sum(dim=1)

# do backward for first element of z
z.backward(torch.FloatTensor([[1, 0, 0, 0]]), retain_graph=True)
print(x.grad.data)
x.grad.data.zero_() #remove gradient in x.grad, or it will be accumulated

# do backward for second element of z
z.backward(torch.FloatTensor([[0, 1, 0, 0]]), retain_graph=True)
print(x.grad.data)
x.grad.data.zero_()

# do backward for all elements of z, with weight equal to the derivative of
# loss w.r.t z_1, z_2, z_3 and z_4
z.backward(torch.FloatTensor([[1, 1, 1, 1]]), retain_graph=True)
print(x.grad.data)
x.grad.data.zero_()

# or we can directly backprop using loss
loss.backward() # equivalent to loss.backward(torch.FloatTensor([1.0]))
print(x.grad.data)    

在上面的例子中,第一个print的结果是

2 0 0 0
[torch.FloatTensor of size 1x4]

这正是 z_1 w.r.t 对 x.

的导数

第二个print的结果是:

0 2 0 0
[torch.FloatTensor of size 1x4]

这是 z_2 w.r.t 对 x 的导数。

现在如果使用[1, 1, 1, 1]的权重来计算zw.r.t对x的导数,结果是1*dz_1/dx + 1*dz_2/dx + 1*dz_3/dx + 1*dz_4/dx。所以毫不奇怪,第 3 个 print 的输出是:

2 2 2 2
[torch.FloatTensor of size 1x4]

需要注意的是权重向量[1, 1, 1, 1]恰好是lossw.r.t对z_1,z_2,[=的导数77=] 和 z_4。 lossw.r.t对x的导数计算为:

d(loss)/dx = d(loss)/dz_1 * dz_1/dx + d(loss)/dz_2 * dz_2/dx + d(loss)/dz_3 * dz_3/dx + d(loss)/dz_4 * dz_4/dx

因此第 4 个 print 的输出与第 3 个 print 相同:

2 2 2 2
[torch.FloatTensor of size 1x4]

The original code I haven't found on PyTorch website anymore.

gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)

上面代码的问题是没有基于如何计算梯度的函数。这意味着我们不知道有多少参数(函数采用的参数)和参数的维度。

为了充分理解这一点,我创建了一个接近原始示例的示例:

Example 1:

a = torch.tensor([1.0, 2.0, 3.0], requires_grad = True)
b = torch.tensor([3.0, 4.0, 5.0], requires_grad = True)
c = torch.tensor([6.0, 7.0, 8.0], requires_grad = True)

y=3*a + 2*b*b + torch.log(c)    
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients,retain_graph=True)    

print(a.grad) # tensor([3.0000e-01, 3.0000e+00, 3.0000e-04])
print(b.grad) # tensor([1.2000e+00, 1.6000e+01, 2.0000e-03])
print(c.grad) # tensor([1.6667e-02, 1.4286e-01, 1.2500e-05])

我假设我们的函数是y=3*a + 2*b*b + torch.log(c),参数是包含三个元素的张量。

你可以把gradients = torch.FloatTensor([0.1, 1.0, 0.0001])想成这样就是累加器

你可能听说过,PyTorch autograd 系统计算等同于 Jacobian 乘积。

如果你有像我们一样的功能:

y=3*a + 2*b*b + torch.log(c)

Jacobian 会是 [3, 4*b, 1/c]。但是,这 Jacobian 并不是 PyTorch 在某个点计算梯度的方式。

PyTorch 串联使用正向传播和 backward mode automatic differentiation (AD)。

不涉及符号数学,也没有数值微分。

Numerical differentiation would be to calculate δy/δb, for b=1 and b=1+ε where ε is small.

如果您不在 y.backward() 中使用渐变:

Example 2

a = torch.tensor(0.1, requires_grad = True)
b = torch.tensor(1.0, requires_grad = True)
c = torch.tensor(0.1, requires_grad = True)
y=3*a + 2*b*b + torch.log(c)

y.backward()

print(a.grad) # tensor(3.)
print(b.grad) # tensor(4.)
print(c.grad) # tensor(10.)

您将根据最初设置 abc 张量的方式简单地在某个点获得结果。

注意如何初始化 abc:

Example 3:

a = torch.empty(1, requires_grad = True, pin_memory=True)
b = torch.empty(1, requires_grad = True, pin_memory=True)
c = torch.empty(1, requires_grad = True, pin_memory=True)

y=3*a + 2*b*b + torch.log(c)

gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)

print(a.grad) # tensor([3.3003])
print(b.grad) # tensor([0.])
print(c.grad) # tensor([inf])

如果您使用 torch.empty() 而不使用 pin_memory=True,您每次可能会有不同的结果。

此外,请注意渐变就像累加器,因此在需要时将它们归零。

Example 4:

a = torch.tensor(1.0, requires_grad = True)
b = torch.tensor(1.0, requires_grad = True)
c = torch.tensor(1.0, requires_grad = True)
y=3*a + 2*b*b + torch.log(c)

y.backward(retain_graph=True)
y.backward()

print(a.grad) # tensor(6.)
print(b.grad) # tensor(8.)
print(c.grad) # tensor(2.)

关于 PyTorch 使用的术语的最后几点提示:

PyTorch 在计算前向传递中的梯度时创建了一个动态计算图。这看起来很像一棵树。

所以你会经常听到这棵树的叶子输入张量输出张量

梯度是通过从根到叶跟踪图形并使用链式法则以这种方式乘以每个梯度来计算的。这种乘法发生在反向传递中。

前段时间我创建了 PyTorch Automatic Differentiation tutorial,您可能会感兴趣地解释有关 AD 的所有微小细节。