计算图
反向传播
如对于计算 的
记 z = b^2 ,b=a-y ,a=< x, w >则反向求导是先计算 再计算 ,再计算
计算复杂度:O(n),内存复杂度O(n)
正向传播
在求复合函数的值的时候就用的是正向传播如f(g(x))先求的m=g(x)再求f(m)
一个简单的例子
import torch
x = torch.arange(4.0)
x.requires_grad_(True) # 等价于x=torch.arange(4.0,requires_grad=True)
x.grad # 默认值是None
x
y = 2 * torch.dot(x, x)
y
y.backward()
x.grad
x.grad == 4 * x
# 在默认情况下,PyTorch会累积梯度,我们需要清除之前的值
x.grad.zero_()
y = x.sum()
y.backward()
x.grad
y.backward()的前提
1.y是一个纯标量(即它的'shape'属性为空或为'()')
2.张量对象是叶子节点(即它们的 requires_grad
属性为 True
)
如果满足则会在计算图中执行反向传播算法。需要注意的是计算得到的每个叶子节点的梯度会会被累积到每个叶子节点的'grad'属性中。往往需要先x.grad.zero_()。需要获取梯度值时可以使用x.grad。
非标量变量的反向传播
# 对非标量调用backward需要传入一个gradient参数,该参数指定微分函数关于self的梯度。
# 本例只想求偏导数的和,所以传递一个1的梯度是合适的
x.grad.zero_()
y = x * x#得到 y 为tensor([0., 1., 4., 9.], grad_fn=<MulBackward0>)
# 等价于y.backward(torch.ones(len(x)))
y.sum().backward()
x.grad
在 PyTorch 中,张量对象的 grad_fn
属性指示了创建该张量的操作(即计算图中的操作),这个属性告诉 PyTorch 在进行反向传播时如何计算梯度。在这种情况下,MulBackward0
表示这个张量是通过乘法操作得到的,因此 PyTorch 将使用乘法的导数规则来计算梯度。
权重参数 torch.ones(len(x))
分离计算
x.grad.zero_()
y = x * x
u = y.detach()
z = u * x
z.sum().backward()
x.grad == u
y.detach()将u视为常数,即与x无关的量。
x.grad.zero_()
y.sum().backward()
x.grad == 2 * x
Python控制流的梯度计算
使用自动微分的一个好处是: [即使构建函数的计算图需要通过Python控制流(例如,条件、循环或任意函数调用),我们仍然可以计算得到的变量的梯度]。 在下面的代码中,while
循环的迭代次数和if
语句的结果都取决于输入a
的值。
def f(a):
b = a * 2
while b.norm() < 1000:
b = b * 2
if b.sum() > 0:
c = b
else:
c = 100 * b
return c
a = torch.randn(size=(), requires_grad=True)
d = f(a)
d.backward()
我们现在可以分析上面定义的f
函数。 请注意,它在其输入a
中是分段线性的。 换言之,对于任何a
,存在某个常量标量k
,使得f(a)=k*a
,其中k
的值取决于输入a
,因此可以用d/a
验证梯度是否正确。
a.grad == d / a
小结
- 深度学习框架可以自动计算导数:我们首先将梯度附加到想要对其计算偏导数的变量上,然后记录目标值的计算,执行它的反向传播函数,并访问得到的梯度。