我正在阅读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参数是什么?文件不是很清楚。
答案 0 :(得分:79)
对于神经网络,我们通常使用loss
来评估网络学习对输入图像(或其他任务)进行分类的程度。 loss
术语通常是标量值。为了更新网络的参数,我们需要计算loss
wrt到参数的梯度,这在计算图中实际上是leaf node
(顺便说一句,这些参数主要是权重和各种层的偏差,如卷积,线性等)。
根据链规则,为了计算loss
wrt到叶节点的梯度,我们可以计算loss
某些中间变量的导数,以及中间变量wrt到叶变量的梯度,做一个点积并总结所有这些。
gradient
backward()
方法的Variable
参数用于计算变量的每个元素与leaf Variable的加权和。< / strong>这些权重只是中间变量的每个元素的最终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
的结果是
2 0 0 0
[大小为1x4的torch.FloatTensor]
这正是z_1 w.r.t到x的衍生物。
第二个print
的结果是:
0 2 0 0
[大小为1x4的torch.FloatTensor]
是z_2 w.r.t到x的衍生物。
现在,如果使用[1,1,1,1]的权重来计算z w.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
[大小为1x4的torch.FloatTensor]
应该注意的是,权重向量[1,1,1,1]恰好是loss
w.r.t到z_1,z_2,z_3和z_4的导数。 loss
w.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
[大小为1x4的torch.FloatTensor]
答案 1 :(得分:36)
通常,您的计算图表有一个标量输出loss
。然后你可以计算loss
w.r.t的梯度。权重(w
)按loss.backward()
。默认参数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
。
答案 2 :(得分:25)
这里,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]
然后向后()计算dz / dx
答案 3 :(得分:6)
我在PyTorch网站上找不到的原始代码。
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(x.grad)
上面代码的问题没有基于计算梯度的函数。这意味着我们不知道有多少个参数(函数采用的参数)以及参数的维数。
为了完全理解这一点,我创建了几个与原始示例相似的示例:
示例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)
,参数是其中具有三个元素的张量。
但是还有另一种选择:
示例2:
import torch
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)
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
y.backward(gradients)
print(a.grad) # tensor(3.3003)
print(b.grad) # tensor(4.4004)
print(c.grad) # tensor(1.1001)
gradients = torch.FloatTensor([0.1, 1.0, 0.0001])
是累加器。
下一个示例将提供相同的结果。
示例3:
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)
gradients = torch.FloatTensor([0.1])
y.backward(gradients,retain_graph=True)
gradients = torch.FloatTensor([1.0])
y.backward(gradients,retain_graph=True)
gradients = torch.FloatTensor([0.0001])
y.backward(gradients)
print(a.grad) # tensor(3.3003)
print(b.grad) # tensor(4.4004)
print(c.grad) # tensor(1.1001)
您可能会听到PyTorch autograd系统计算与Jacobian乘积等效。
如果您有像我们一样的功能,
y=3*a + 2*b*b + torch.log(c)
Jacobian将是[3, 4*b, 1/c]
。但是,Jacobian并不是PyTorch所做的事情来计算特定点处的梯度。
对于上一个函数,PyTorch会为δy/δb
和b=1
做b=1+ε
,其中ε很小。因此,没有像符号数学这样的东西。
如果您没有在y.backward()
中使用渐变:
示例4
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.)
根据最初设置a
,b
,c
张量的方式,您可以简单地在某个时刻获得结果。
请注意如何初始化a
,b
,c
:
示例5:
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
,则每次可能会有不同的结果。
另外,音符梯度就像累加器,因此在需要时将它们归零。
示例6:
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在计算梯度时会创建一个动态计算图。这看起来很像一棵树。
所以您经常会听到这棵树的叶是输入张量,而 root 是输出张量
通过从根到叶跟踪图形并使用链规则以这种方式乘以每个梯度来计算梯度。