AUTOGRAD 相关
AUTOGRAD 包是pytorch中和神经网络关系最为密切的一部分。它可以为基于tensor的的所有操作提供自动微分的功能。它是一个在运行时定义(define-by-run)的框架,这意味着其中的反向传播是根据代码如何运行来决定的,并且每次迭代可以是不同的。
Tensor
torch.Tensor
是该包中最主要的一个class。如果我们设置其属性.requires_grad = True
,则torch会追踪到对该tensor上的所有操作(operation)。当计算结束后,可以通过.backward()
来自动计算所有的梯度。对这个tensor的梯度会累加在.grad
中。
如果想避免一个张量受历史数据的影响,可以通过.detach()
来将其从计算图历史中分开,以避免之后的计算被追踪。
为了防止追踪历史(且占用内存),我们可以将代码包装在with torch.no_grad():
中,这样很有效。因为在评估模型时,模型中可能会包含requires_grad = True
的可训练参数,但我们可能并不需要其梯度。
Function
对autograd来说,还有另外一个非常重要的class,Fucntion
Tensor
和Function
之间互相连接,构成了一个无环图(acyclic graph)。图中包括了计算的完整历史。每个张量都有.grad_fn
属性,该属性会引用创建该tensor的Function。(除了是由用户自己创建的tensor,它们的grad_fn
为None
)。
如果想要计算导数,我们可以在 Tensor 上调用 .backward()
。如果 Tensor 是一个标量(如它只包含一个元素的数据),则不需要为backward()
指定任何参数,但是如果它有更多的元素,则需要指定一个 gradient 参数,该参数是一个与该张量形状匹配的张量。
演示
创建一个tensor,并设置其 requires_grad=True 来追踪其计算图
import torch
x = torch.ones(2, 2, requires_grad=True)
print(x)
# output:
# tensor([[1., 1.],
# [1., 1.]], requires_grad=True)
然后对该tensor做操作
y = x + 2
print(y)
# output:
# tensor([[3., 3.],
# [3., 3.]], grad_fn=<AddBackward0>)
此处因为y是计算后的结果,所以它具有grad_fn
属性
print(y.grad_fn)
# output:
# <AddBackward0 object at 0x7f2451155828>
再看个其它的操作
z = y * y * 3
out = z.mean()
print(z, out)
# output:
# tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)
可见每一个tensor都对应找到得出该tensor的grad_fn
。
默认情况下,requires_grad
是False
。我们可以用.requires_grad_( ... )
来原地修改已有的tensor的requires_grad
状态。如下面代码所示:
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)
# output:
# False
# True
# <SumBackward0 object at 0x7f2451164208>
Gradients
现来来看一下上面例子中的反向传播是怎么做的。
因为 out
是一个标量(可见它最后输出了z的平均值27)。所以out.backward()
就相当于out.backward(torch.tensor(1.))
。这个相当于是怎么的个意思,不懂
anyway,对其计算导数,并输出对x的导数 d ( o u t ) / d x d(out)/dx d(out)/dx
out.backward()
print(x.grad)
# output:
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
那好,为什么是4.5呢?
首先,我们把out
看做张量" o o o",则我们有
∵ o = 1 4 ∑ i z i z i = 3 ( x i + 2 ) 2 且 z i ∣ x i = 1 = 27 ∴ ∂ o ∂ x i = 3 2 ( x i + 2 ) ∂ o ∂ x i ∣ x i = 1 = 9 2 = 4.54 \begin{aligned} \because \mathcal{o} &= \frac{1}{4}\sum_i z_i\\ z_i&=3(x_i + 2)^2 \ \ 且 \ \ z_i\bigg|_{x_i = 1} = 27 \\ \therefore \frac{\partial o}{\partial x_i}&= \frac{3}{2}(x_i + 2)\\ \frac{\partial o}{\partial x_i} \bigg|_{x_i = 1} &= \frac{9}{2} = 4.5 {4} \end{aligned} ∵ozi∴∂xi∂o∂xi∂o∣∣∣∣xi=1=41</