Pytch 梯度参数是什么

我正在阅读 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参数是什么?相关文件对此并不十分清楚。

25953 次浏览

这里,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]

然后 back ()计算 dz/dx

通常,计算图有一个标量输出 loss。然后你可以用 loss.backward()计算出 loss的梯度值和权重(w)。其中 backward()的默认参数是 1.0

如果你的输出有多个值(例如 loss=[loss1, loss2, loss3]) ,你可以用 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项通常是一个标量值。为了更新网络的参数,我们需要计算 loss. r.t 对参数的梯度,实际上是计算图中的 leaf node(顺便说一下,这些参数主要是卷积、线性等各层的权值和偏差)。

根据链式规则,为了计算 loss w.r.t 对叶节点的梯度,可以计算 loss w.r.t 对某个中间变量的导数,以及中间变量 w.r.t 对叶节点的梯度,做点乘,求出所有这些导数。

Variablebackward()方法的 gradient参数用于 计算变量 w.r.t 的每个元素的加权和 < a href = “ https://討 s.pytorch.org/t/leaf-Variable-was-used-in-an-inplace-operation/308/2? u = jdhao”rel = “ noReferrer”> leaf 浮动变量 。。这些权重只是中间变量的最终 loss的每个元素的导数。

一个具体的例子

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

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的结果是

两千
[手电筒。浮动张量的大小1x4]

正好是 z _ 1 wr.t 对 x 的导数。

第二次 print的结果是:

0200
[手电筒。浮动张量的大小1x4]

是 z _ 2 wr.t 对 x 的导数。

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

2222
[手电筒。浮动张量的大小1x4]

值得注意的是,权向量[1,1,1,1]是 loss. r.t 对 z _ 1,z _ 2,z _ 3和 z _ 4的精确导数。loss. 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的输出是一样的:

2222
[手电筒。浮动张量的大小1x4]

我在 PyTorch 网站上找不到的原始代码。

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

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

为了充分理解这一点,我创建了一个类似于原版的例子:

例子一:

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 自学系统计算等价于 Jacobian 乘积。

Jacobian

如果你有一个函数,像我们一样:

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

Jacobian 应该是 [3, 4*b, 1/c]。但是,这个 雅各布安并不是 PyTorch 计算某个点上的渐变的方法。

PyTorch 使用前传和 后向模式自动微分(AD)串联。

没有象征性的数学和数值微分。

数值微分是计算 δy/δb,对于 ε 很小的 b=1b=1+ε

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

例子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:

例三:

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,你可能每次都会得到不同的结果。

另外,注意渐变就像累加器,所以在需要的时候将它们归零。

例子四:

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 在计算向前传递的梯度时创建一个 动态计算图动态计算图,这看起来很像一棵树。

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

梯度是通过跟踪图形从根到叶子和乘以每个梯度的方式使用 链式法则。这种乘法发生在向后传递过程中。

早些时候,我创建了 火炬自动微分教程,你可能会检查有趣的解释所有关于广告的微小细节。