对于深度学习, 自动微分(AD)是一个十分关键的技术,而 MXNet 与 Pytorch 都拥有 autograd
包来实现该功能,下面我们来分别研究它们的使用方法。
MXNet 中的 autograd
参考:自动求梯度
from mxnet import autograd, nd
在 MXNet 中的张量使用 nd
来表示:
x = nd.arange(10).reshape((2, 5))
x
[[0. 1. 2. 3. 4.]
[5. 6. 7. 8. 9.]]
<NDArray 2x5 @cpu(0)>
- 为了求有关变量
x
的梯度,我们需要先调用attach_grad
函数来申请存储梯度所需要的内存。 - 为了减少计算和内存开销,默认条件下 MXNet 不会记录用于求梯度的计算。我们需要调用
record
函数来要求 MXNet 记录与求梯度有关的计算。
x.attach_grad()
with autograd.record():
y = 2 * nd.dot(x.T, x)
需要注意的是,如果 y
不是一个标量,MXNet 将默认先对 y
中元素求和得到新的变量,再求该变量有关 x
的梯度。backward()
实现自动微分:
y.backward()
求得 y
对 x
的微分:
x.grad
[[ 40. 40. 40. 40. 40.]
[140. 140. 140. 140. 140.]]
<NDArray 2x5 @cpu(0)>
通过调用 is_training
函数来查看程序的运行模式是预测模式还是训练模式?
print(autograd.is_training()) # 预测模式
with autograd.record():
print(autograd.is_training()) # 训练模式
False
True
.detach()
将张量从计算图中分离出来而不被追踪:
x1 = x.detach()
with autograd.record():
y = x + 1
y = x1+y
y.backward()
x.grad is None, x1.grad is None
(False, True)
x1.grad
没有被追踪,无法计算其梯度。
Pytorch 中的 autograd
torch.Tensor
是包的核心类。如果将其属性.requires_grad
设置为True,则会开始跟踪其上的所有操作。完成计算后,您可以调用.backward()
并自动计算所有梯度。此张量的梯度将累积到.grad
属性中。
要阻止张量跟踪历史记录,可以调用.detach()
将其从计算历史记录中分离出来,并防止将来的计算被跟踪。
要防止跟踪历史记录(和使用内存),您还可以使用 torch.no_grad()
包装代码块:在评估模型时,这可能特别有用,因为模型可能具有requires_grad = True
的可训练参数,但我们不需要梯度。
还有一个类对于 autograd
实现非常重要 - Function
。
Tensor 和 Function 互相连接并构建一个非循环图构建一个完整的计算过程。每个张量都有一个.grad_fn
属性,该属性引用已创建 Tensor 的 Function(除了用户创建的 Tensors - 它们的 grad_fn
为 None
)。
如果要计算导数,可以在Tensor上调用 .backward()
。如果 Tensor 是标量(即它包含一个元素数据),则不需要为 backward()
指定任何参数,但是如果它有更多元素,则需要指定一个梯度参数,该参数是匹配形状的张量。
torch.Tensor()
函数中设置参数:.requires_grad=True
来追踪运算。而和 MXNet 一样也是采用 .backward()
实现自动微分,使用 .grad
获得所需变量的梯度,.detach()
将会使得在计算图中追踪的 Tensor 分离出来,不再被追踪。with torch.no_grad()
与 MXNet 的 with autograd.record()
相对应,用在模型的测试模式之下,将 .requires_grad=True
的 Tensor 的追踪解除。
import torch
创建张量 x
并追踪:
x = torch.ones(2, 2, requires_grad=True) # 追踪张量 x
print(x)
y = x + 2 # 创建 Functional
print(y)
tensor([[1., 1.],
[1., 1.]], requires_grad=True)
tensor([[3., 3.],
[3., 3.]], grad_fn=<AddBackward0>)
print(y.grad_fn)
<AddBackward0 object at 0x000001D93D99DA58>
计算标量的微分:
a = torch.randn(2, 2)
a = ((a * 3) / (a - 1))
print(a.requires_grad)
a.requires_grad_(True) # 就地修改,使得张量 a 被追踪
print(a.requires_grad)
b = (a * a).sum()
print(b.grad_fn)
False
True
<SumBackward0 object at 0x000001D92CD01048>
z = y * y * 3
out = z.mean() # 一个标量
print(z, out)
out.backward() # 自动求微分, out 是一个标量
tensor([[27., 27.],
[27., 27.]], grad_fn=<MulBackward0>) tensor(27., grad_fn=<MeanBackward1>)
计算 ∂out∂x\frac{\partial{out}}{\partial{x}}∂x∂out:
print(x.grad)
tensor([[4.5000, 4.5000],
[4.5000, 4.5000]])
计算张量的雅各比矩阵
计算 ∂y∂x\frac{\partial{y}}{\partial{x}}∂x∂y:
x = torch.ones(2, 2, requires_grad=True) # 追踪张量 x
y = 2*x + 2
y.backward(x)
x.grad
tensor([[2., 2.],
[2., 2.]])
print(x.requires_grad)
print((x ** 2).requires_grad)
with torch.no_grad(): # 解除追踪
print((x ** 2).requires_grad)
True
True
False
共同学习,写下你的评论
评论加载中...
作者其他优质文章