02pytorch自动微分(来源官方文档)
autograd包是PyTorch中所有神经网络的核心。该autograd软件包为Tensors上的所有操作提供自动微分。
01Tensor
torch.Tensor是包的核心类。如果将其属性 .requires_grad 设置为 True,则会开始跟踪针对 tensor的所有操作。完成计算后,您可以调用 .backward() 来自动计算所有梯度。该张量的梯度将累积到.grad 属性中。
要停止 tensor 历史记录的跟踪,您可以调用 .detach(),它将其与计算历史记录分离,并防止将来的计算被跟踪。
要停止跟踪历史记录(和使用内存),您还可以将代码块使用 with torch.no_grad(): 包装起来。在评估模型时,这是特别有用,因为模型在训练阶段具有 requires_grad = True 的可训练参数有利于调参,但在评估阶段我们不需要梯度。
还有一个类对于 autograd 实现非常重要那就是 Function。Tensor 和 Function 互相连接并构建一个非循环图,它保存整个完整的计算过程的历史信息。每个张量都有一个 .grad_fn 属性保存着创建了张量的 Function 的引用,(如果用户自己创建张量,则grad_fn是None )。
如果你想计算导数,你可以调用 Tensor.backward()。如果 Tensor 是标量(即它包含一个元素数据),则不需要指定任何参数backward(),但是如果它有更多元素,则需要指定一个radient 参数来指定张量的形状。
import torch
x = torch.ones(2,2,requires_grad=True)
print(x)
# tensor([[1., 1.],
# [1., 1.]], requires_grad=True)
y = x+2
print(y)
# tensor([[3., 3.],
# [3., 3.]], grad_fn=<AddBackward0>)
z = y*y*3
print(z)
# tensor([[27., 27.],
# [27., 27.]], grad_fn=<MulBackward0>)
out = z.mean()
print(out)
# 进行反向传播
out.backward()
print(x.grad)
# tensor([[4.5000, 4.5000],
# [4.5000, 4.5000]])
# tensor(27., grad_fn=<MeanBackward0>)
在上面的程序中首先创建了一个需要被求导的张量x,然后对x进行了广播加2【grad_fn=AddBackward0】,然后进行进行了(x+2)(x+2)3【grad_fn=MulBackward0】,最后对张量的值进行了平均【grad_fn=MeanBackward0】。

2.对torch.autograd的理解
2.1首先对输出的标量值进行反向传播进行理解:
假设有如下神经网络:

则如果进行发反向传播的话,假如更新wij,则需要用到如下求导链式法则:
\begin{align}\notag \frac{\partial loss}{\partial wij} =\frac{\partial loss}{\partial y}*\frac{\partial y}{\partial wij} \end{align}
注意这里为了方便用wij和wi区分了从x到y的权值矩阵和从Y到loss的权值矩阵。 这个公式很好理解,我就不详细解释了,但是有一点我么需要注意当loss对y求导时,这是一个常数,不信你可以试一下,如:\begin{align}\notag \frac{\partial loss}{\partial w11} =\frac{\partial loss}{\partial y1}*\frac{\partial y1}{\partial w11} = w1*\frac{\partial y1}{\partial w11} \end{align}
w1是一个常量,所以我们可以得出一个结论,虽然反向传播需要一个链式法则,但是当我们知道中间变量y的值,并且知道y到loss的权重wi时,我们也可以通过上面公式进行求导操作。(这在下面用得到,需要理解)2.2 对输出的向量值进行反向传播进行理解:

如上图,输出的是一个向量y,这时候在对wij进行求导更新时便不能轻易求到了(一个矩阵对另外一个矩阵求导我不会,不详细说了)。
这在pytorch中,开发者也考虑到了这个问题,在他们的官方文档中,是这样描述的,在计算的过程中存在一个雅可比矩阵,我直接映射到如图上的变量显示:
\begin{align}\notag \begin{bmatrix} &\frac{\partial y1}{\partial w11} &\frac{\partial y2}{\partial w11} &\frac{\partial y3}{\partial w11} &\frac{\partial y4}{\partial w11} \\ &\frac{\partial y1}{\partial w12} &\frac{\partial y2}{\partial w12} &\frac{\partial y3}{\partial w12} &\frac{\partial y4}{\partial w12} \\ &\frac{\partial y1}{\partial w13} & ... &... &...\\ &...&... &...&... \\ &...&... &...&... \\ &...&... &...&...\\ &\frac{\partial y1}{\partial w34} &\frac{\partial y2}{\partial w34} &\frac{\partial y3}{\partial w34} &\frac{\partial y4}{\partial w34} \end{bmatrix} \end{align}
当涉及到一个矩阵对另外一个矩阵求导时,即调用backward(self, gradient=None)时,需要传递一个gradiient,这个gradient便是上面的loss对y的求导得出的向量。 那如何理解这个gradient呢? 网上的理解方法比较多,有的说这是权重,通过以上的描述也可以理解为yi到映射输出的Loss的权重,有的说gradient应该为全1矩阵,对y进行相加从而得到标量进行反向传播。 通过上述描述,我的理解就是,当涉及一个矩阵对另一个矩阵求导时,其可以想象为一个标量对求导,这时候通过2.1描述所说,后面的loss对y的求导只是一个常量,我们可以自己设计他的值,从而得到求导的结果。如下面公式演示可得。\begin{align}\notag \begin{bmatrix} &\frac{\partial y1}{\partial w11} &\frac{\partial y2}{\partial w11} &\frac{\partial y3}{\partial w11} &\frac{\partial y4}{\partial w11} \\ &\frac{\partial y1}{\partial w12} &\frac{\partial y2}{\partial w12} &\frac{\partial y3}{\partial w12} &\frac{\partial y4}{\partial w12} \\ &\frac{\partial y1}{\partial w13} & ... &... &...\\ &...&... &...&... \\ &...&... &...&... \\ &...&... &...&...\\ &\frac{\partial y1}{\partial w34} &\frac{\partial y2}{\partial w34} &\frac{\partial y3}{\partial w34} &\frac{\partial y4}{\partial w34} \end{bmatrix}\times \begin{bmatrix} \frac{\partial loss}{\partial y1}\\ \frac{\partial loss}{\partial y2}\\ \frac{\partial loss}{\partial y3}\\ \frac{\partial loss}{\partial y4}\\ \end{bmatrix}=\begin{bmatrix} \frac{\partial loss}{\partial w11}\\ \frac{\partial loss}{\partial w12}\\ \frac{\partial loss}{\partial w13}\\ ...\\ \frac{\partial loss}{\partial w34}\\ \end{bmatrix} \end{align}
从而loss对Y的求导值便是我们设计的,这个值一般设计为1,下面时官方文档的描述。 03torch中关于求导张量的属性设置在
required_grad可以查看变量是否能够求导
import torch
a = torch.randn(3,2)
# 当a加上requires_grad=True时结果为True
a = ((a*3)/(a-1))
print(a.requires_grad)
# False
a.requires_grad_(True)
print(a.requires_grad)
# True
04 雅可比矩阵的例子
x = torch.randn(3, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:
y = y * 2
print(y)
# tensor([ 919.0666, -111.9718, -739.7840], grad_fn=<MulBackward0>)
现在在这种情况下,y不再是一个标量。torch.autograd不能够直接计算整个雅可比,但是如果我们只想要雅可比向量积,只需要简单的传递向量给backward作为参数。
import torch
x = torch.randn(3, requires_grad=True)
y = x * 2
while y.data.norm() < 1000:
y = y * 2
print(y)
# tensor([ 752.1347, -588.1050, 532.4743], grad_fn=<MulBackward0>)
v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)
print(x.grad)
# tensor([5.1200e+01, 5.1200e+02, 5.1200e-02])
你可以通过将代码包裹在 with torch.no_grad(),来停止对从跟踪历史中的quires_grad=True 的张量自动求导。【这个可以直接理解为在torch.no_grad()中的代码不能够进行求导操作】
a = torch.rand(2,3,requires_grad=True)
b = torch.rand(2,3,requires_grad=True)
z = a+b
print(z.requires_grad)
# True
with torch.no_grad():
z = a+b
print(z.requires_grad)
# False

浙公网安备 33010602011771号