为了账号安全,请及时绑定邮箱和手机立即绑定

MXNet 与 Pytorch 的 autograd

标签:
Python

对于深度学习, 自动微分(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)>
  1. 为了求有关变量 x 的梯度,我们需要先调用 attach_grad 函数来申请存储梯度所需要的内存。
  2. 为了减少计算和内存开销,默认条件下 MXNet 不会记录用于求梯度的计算。我们需要调用 record 函数来要求 MXNet 记录与求梯度有关的计算。
x.attach_grad()

with autograd.record():
    y = 2 * nd.dot(x.T, x)

需要注意的是,如果 y 不是一个标量,MXNet 将默认先对 y 中元素求和得到新的变量,再求该变量有关 x 的梯度。backward() 实现自动微分:

y.backward()

求得 yx 的微分:

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

如果要计算导数,可以在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}}xout

print(x.grad)
tensor([[4.5000, 4.5000],
        [4.5000, 4.5000]])

计算张量的雅各比矩阵

计算 ∂y∂x\frac{\partial{y}}{\partial{x}}xy

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
点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消