了解 PyTorch 中的反向传播

Posted

技术标签:

【中文标题】了解 PyTorch 中的反向传播【英文标题】:Understanding backpropagation in PyTorch 【发布时间】:2021-11-20 20:41:37 【问题描述】:

我在探索PyTorch,看不懂下面例子的输出:

# Initialize x, y and z to values 4, -3 and 5
x = torch.tensor(4., requires_grad = True)
y = torch.tensor(-3., requires_grad = True)
z = torch.tensor(5., requires_grad = True)

# Set q to sum of x and y, set f to product of q with z
q = x + y
f = q * z

# Compute the derivatives
f.backward()

# Print the gradients
print("Gradient of x is: " + str(x.grad))
print("Gradient of y is: " + str(y.grad))
print("Gradient of z is: " + str(z.grad))

输出

Gradient of x is: tensor(5.)
Gradient of y is: tensor(5.)
Gradient of z is: tensor(1.)

我毫不怀疑我的困惑源于一个小小的误解。有人可以逐步解释吗?

【问题讨论】:

【参考方案1】:

我希望你明白,当你做f.backward() 时,你在x.grad 中得到的是。

在你的情况下 。 所以,简单地说(通过初步微积分)

如果您输入 x、y 和 z 的值,就可以解释输出。

但是,这并不是真正的“反向传播”算法。这只是偏导数(这是您在问题中提出的所有问题)。

编辑: 如果您想了解其背后的反向传播机制,请参阅@Ivan 的回答。

【讨论】:

谢谢。我正在学习有关 DataCamp 的 PyTorch 课程,这是他们使用的示例之一。【参考方案2】:

您只需要了解什么是操作以及您应该使用哪些偏导数,例如:

x = torch.tensor(1., requires_grad = True)
q = x*x
q.backward()

print("Gradient of x is: " + str(x.grad))

会给你2,因为x*x的导数是2*x

如果我们以 x 为例,我们有:

q = x + y
f = q * z

可以修改为:

f = (x+y)*z = x*z+y*z

如果我们在 x 的函数中取 f 的偏导数,我们最终会得到 z。

要得出这个结果,您必须将所有其他变量视为常数并应用您已经知道的导数规则。

但请记住,pytorch 执行以获得这些结果的过程不是符号或数值微分,而是自动微分,这是一种有效获取梯度的计算方法。

仔细看看:

https://www.cs.toronto.edu/~rgrosse/courses/csc321_2018/slides/lec10.pdf

【讨论】:

【参考方案3】:

我可以就反向传播的 PyTorch 方面提供一些见解。

在处理需要梯度计算 (requires_grad=True) 的张量时,PyTorch 会跟踪反向传播的操​​作并构造计算图ad hoc

让我们看看你的例子:

q = x + y 
f = q * z

其对应的计算图可以表示为:

  x   -------\
              -> x + y = q ------\
  y   -------/                    -> q * z = f
                                 /
  z   --------------------------/ 

其中xyz 被称为叶张量。反向传播包括计算xyy的梯度,它们分别对应于:dL/dxdL/dydL/dz。其中L 是基于图形输出f 的标量值。执行的每个操作都需要实现一个后向函数(所有数学上可微分的 PyTorch 内置函数都是这种情况)。对于每个操作,此函数有效地用于计算输出w.r.t.输入的梯度。

向后传递将如下所示:

dL/dx <------\    
  x   -----\  \ 
            \ dq/dx 
             \  \ <--- dL/dq-----\
              -> x + y = q ----\  \
             /  /               \ df/dq
            / dq/dy              \  \ <--- dL/df ---
  y   -----/  /                   -> q * z = f
dL/dy <------/                   /  /
                                / df/dz
  z   -------------------------/  /
dL/dz <--------------------------/

第一个运算符的 "d(outputs)/d(inputs)" 术语是:dq/dx = 1dq/dy = 1。对于第二个运算符,它们是 df/dq = zdf/dz = q

反向传播归结为应用链式规则:dL/dx = dL/dq * dq/dx = dL/df * df/dq * dq/dx。直观地说,我们分解 dL/dx 的方式与反向传播实际所做的相反,即自下而上导航。

不考虑形状,我们从dL/df = 1开始。实际上dL/df 的形状为f(请参阅下面链接的我的其他答案)。这导致dL/dx = 1 * z * 1 = z。同样对于yz,我们有dL/dy = zdL/dz = q = x + y。你观察到的结果是什么。


我对相关主题的一些回答:

Understand PyTorch's graph generation

Meaning of grad_outputs in PyTorch's torch.autograd.grad

Backward function of the normalize operator

Difference between autograd.grad and autograd.backward

Understanding Jacobian tensors in PyTorch

【讨论】:

以上是关于了解 PyTorch 中的反向传播的主要内容,如果未能解决你的问题,请参考以下文章

学习笔记Pytorch十二损失函数与反向传播

pytorch前向传播和反向传播

如何使用 Pytorch 中的截断反向传播(闪电)在很长的序列上运行 LSTM?

pytorch 如何通过 argmax 反向传播?

pytorch自定义多分类损失函数怎么反向传播

PyTorch自动求导:反向传播的一切