autograd:自动微分

目录


一、autograd:自动微分

PyTorch 中所有神经网络的核心是 autograd 包,autograd 包对 tensor 上的所有运算提供了自动微分。它是一个 difine-by-run 框架,意味着代码如何运行定义了反向传播,并且每一次迭代操作都可以不同。

二、Tensor

torch.Tensor 是包的核心类。若设置属性 requires_grad=True,则会开始跟踪其上的所有操作。当计算完成后,调用 backward() 自动计算所有的梯度。tensor 上的梯度会累积到 .grad属性中。

要停止 tensor 跟踪历史记录,可调用 .detach() 将其从计算历史中分离,并阻止将来的计算被跟踪。

为了阻止跟踪历史记录(和使用内存),可以 torch.no_grad(): 包装代码块。在评估模型时,这可能特别有用。因为模型可能包含属性 requires_grad=True 的参数,但是我们并不需要其梯度。

对于autograd的实现,另一个类非常重要——Function

tensorFunction 相互连接构成一个非循环图,编码完整的计算历史。每个 tensor 有一个 .grad_fn 属性,指向创建该 tensorFunction(除了用户创建的 tensor ,它们的grad_fnNone)。

若想要计算导数,可以对该 tensor 调用 backward() 。若该 tensor 是一个标量(即只包含一个元素),不需要为 backward() 指定任何参数。然而,若该 tensor 包含多个元素,需要指定一个梯度参数,该参数是一个 tensor 并且形状匹配。

示例

生成一个tensor,并设置 requires_grad=True 以跟踪其上的运算

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>)

y 作为运算结果被创建,所有它有grad_fn

print(y.grad_fn)

输出:

<AddBackward0 object at 0x7f77ae1835c0>

继续进行运算

z = y * y * 3
out = z.mean()

print(z, out)

输出:

tensor([[27., 27.],
        [27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward0>)

.requires_grad_(...) 原地改变 tensorrequires_grad 属性,若不指定,默认值为 False

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)

输出:

False
True
<SumBackward0 object at 0x7f77ae183e10>

三、梯度

因为 out 只包含一个标量,因此 out.backward() 等价于out.backward(torch.tensor(1.))

out.backward()

打印梯度:d(out)/dx

print(x.grad)

输出:

tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])

得到一个元素为4.5的2x2矩阵。记 out 为“ o o o”,则 o = 1 4 ∑ i z i o = \frac{1}{4}\sum_i z_i o=41izi z i = 3 ( x i + 2 ) 2 z_i = 3(x_i+2)^2 zi=3(xi+2)2 z i ∣ x i = 1 = 27 z_i\bigr\rvert_{x_i=1} = 27 zixi=1=27。因为, ∂ o ∂ x i = 3 2 ( x i + 2 ) \frac{\partial o}{\partial x_i} = \frac{3}{2}(x_i+2) xio=23(xi+2),所以 ∂ o ∂ x i ∣ x i = 1 = 9 2 = 4.5 \frac{\partial o}{\partial x_i}\bigr\rvert_{x_i=1} = \frac{9}{2} = 4.5 xioxi=1=29=4.5


从数学上讲,对于函数 y ⃗ = f ( x ⃗ ) \vec{y}=f(\vec{x}) y =f(x ) y ⃗ \vec{y} y 关于 x ⃗ \vec{x} x 的梯度是一个Jacobian 矩阵:

J = ( ∂ y 1 ∂ x 1 ⋯ ∂ y 1 ∂ x n ⋮ ⋱ ⋮ ∂ y m ∂ x 1 ⋯ ∂ y m ∂ x n ) \begin{aligned}J=\left(\begin{array}{ccc} \frac{\partial y_{1}}{\partial x_{1}} &amp; \cdots &amp; \frac{\partial y_{1}}{\partial x_{n}}\\ \vdots &amp; \ddots &amp; \vdots\\ \frac{\partial y_{m}}{\partial x_{1}} &amp; \cdots &amp; \frac{\partial y_{m}}{\partial x_{n}} \end{array}\right)\end{aligned} J=x1y1x1ymxny1xnym

通常来说,torch.autograd 是一个计算向量Jacobian 乘积的引擎。即,给定任意的向量 v = ( v 1 v 2 ⋯ v m ) T v=\left(\begin{array}{cccc} v_{1} &amp; v_{2} &amp; \cdots &amp; v_{m}\end{array}\right)^{T} v=(v1v2vm)T,计算 v T ⋅ J v^{T}\cdot J vTJ。如果 v v v恰好是一个标量函数 l = g ( y ⃗ ) l=g\left(\vec{y}\right) l=g(y )的梯度,即, v = ( ∂ l ∂ y 1 ⋯ ∂ l ∂ y m ) T v=\left(\begin{array}{ccc}\frac{\partial l}{\partial y_{1}} &amp; \cdots &amp; \frac{\partial l}{\partial y_{m}}\end{array}\right)^{T} v=(y1lyml)T,根据链式法则,向量和Jacobian的乘积将会是 l l l 关于 x ⃗ \vec{x} x 的梯度:

J T ⋅ v = ( ∂ y 1 ∂ x 1 ⋯ ∂ y m ∂ x 1 ⋮ ⋱ ⋮ ∂ y 1 ∂ x n ⋯ ∂ y m ∂ x n ) ( ∂ l ∂ y 1 ⋮ ∂ l ∂ y m ) = ( ∂ l ∂ x 1 ⋮ ∂ l ∂ x n ) \begin{aligned}J^{T}\cdot v=\left(\begin{array}{ccc} \frac{\partial y_{1}}{\partial x_{1}} &amp; \cdots &amp; \frac{\partial y_{m}}{\partial x_{1}}\\ \vdots &amp; \ddots &amp; \vdots\\ \frac{\partial y_{1}}{\partial x_{n}} &amp; \cdots &amp; \frac{\partial y_{m}}{\partial x_{n}} \end{array}\right)\left(\begin{array}{c} \frac{\partial l}{\partial y_{1}}\\ \vdots\\ \frac{\partial l}{\partial y_{m}} \end{array}\right)=\left(\begin{array}{c} \frac{\partial l}{\partial x_{1}}\\ \vdots\\ \frac{\partial l}{\partial x_{n}} \end{array}\right)\end{aligned} JTv=x1y1xny1x1ymxnymy1lyml=x1lxnl
注意 v T ⋅ J v^{T}\cdot J vTJ 给出一个行向量,可以通过取 J T ⋅ v J^{T}\cdot v JTv 将其视为列向量)

向量和Jacobian乘积的这种特性使得将外部梯度馈送到具有非标量输出的模型中非常方便。


向量和Jacobian乘积示例

x = torch.randn(3, requires_grad=True)

y = x * 2
while y.data.norm() < 1000:
    y = y * 2

print(y)

输出:

tensor([-806.5612,  620.5370, 1196.6376], grad_fn=<MulBackward0>)

上述例子中,y 不是一个标量,torch.autograd 不能直接计算完整的Jacobian,但是如果我们只想要求向量和Jacobian的乘积,只需将向量作为参数向后传递:

v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)

print(x.grad)

输出:

tensor([2.0480e+02, 2.0480e+03, 2.0480e-01])

也可以使用 torch.no_grad() 包装代码块,停止 autograd 跟踪属性为 requires_grad=Truetensor 的历史记录。

print(x.requires_grad)
print((x ** 2).requires_grad)

with torch.no_grad():
    print((x ** 2).requires_grad)

输出:

True
True
False
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值