前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Pytorch Autograd 基础(一)

Pytorch Autograd 基础(一)

作者头像
用户6021899
发布2022-04-15 08:27:35
2790
发布2022-04-15 08:27:35
举报

Autograd (自动梯度)是Pytorch能够快速又灵活地构建机器学习模型的关键。它能够用来快速而简单地计算复杂函数的多重偏导数,它是基于反向传播的神经网络学习的核心。

Autograd的强大之处在于,它能在程序运行时而不是编译时动态追踪计算,意味着即使你的模型的分支或者循环的长度或层数直到运行时才确定,它仍然能够正确的追踪计算,你将得到正确的梯度去驱动模型的学习。如果你的模型是用python构建的,在梯度计算上它就能比基于统计分析的结构固定的学习框架提供更强大的灵活度。

  • 我们用Autograd来干啥?

机器学习模型是一个有输入有输出的函数。在本篇的讨论范围内,我们把输入看做一个n维向量

, 把输出也看做是一个向量

(为什么可以当成向量呢?因为从广泛意义上讲,模型可以有任意数量的输出),则有

模型的Loss(损失或误差)则为

Loss是一个标量函数,它表达的是模型的预测值与实际的lables之间的差异。

从这里开始,后面我们会忽略y的向量符号,用

代替

在模型的训练中,我们希望使loss达到最小。最理想的情况是,我们调整模型的可调参数,即权重,能够使得对所有的输入,loss都为零。在真实世界里,它是指我们调整权重,使得对于宽泛的输入,模型的总损失足够小,在可接受的范围。

我们怎么判断权重该调整的方向和大小呢?损失L最小,意味着L的一阶偏导数等于0,

我们知道,损失L不是输入的直接函数,而是输出的之间函数,

。根据链式法则有

=

使得事情变得复杂。如果我们再用链式法则去展开表达式,需要涉及到模型中每个权重的偏导数,每个激活函数的偏导数,以及每个数学变换的偏导数。每个偏导数的完整表达式是计算图中的每个可能路径的局部梯度的乘积之和,以我们试图测量其梯度的变量结束。

我们对各学习权重的梯度感兴趣,它告诉我们该如何调整各个学习梯度,以使得损失趋向于零。

由于这种局部导数的数量(每一个都对应于通过模型计算图的一条单独路径)将倾向于随着神经网络的深度呈指数增长,因此计算它们的复杂性也是如此。这就是autograd的用武之地:它追踪每一次计算的历史。PyTorch模型中的每个计算张量都包含其输入张量的历史以及用于创建它的函数。结合作用于张量的PyTorch函数都有一个用于计算自身导数的内置实现这一事实,这大大加快了学习所需的局部导数的计算。

  • 一个简单的例子

先导入一些稍后会用到的库

代码语言:javascript
复制
import torch
import math
from matplotlib import pyplot as plt

像所有其它创建tensor的函数一样,linspace()接受一个可选参数requires_grad。

设置此标志为True意味着在接下来的每一次计算中,autograd将在该计算的输出张量中累积计算历史。

代码语言:javascript
复制
>>> a = torch.linspace(0., 2. * math.pi, steps=25, requires_grad=True)
>>> a
tensor([0.0000, 0.3307, 0.6614, 0.9921, 1.3228, 1.6535, 1.9842, 2.3149, 2.6456,
        2.9762, 3.3069, 3.6376, 3.9683, 4.2990, 4.6297, 4.9604, 5.2911, 5.6218,
        5.9525, 6.2832], requires_grad=True
代码语言:javascript
复制
>>> b = torch.sin(a)
>>> b
tensor([ 0.0000e+00,  3.2470e-01,  6.1421e-01,  8.3717e-01,  9.6940e-01,
         9.9658e-01,  9.1577e-01,  7.3572e-01,  4.7595e-01,  1.6459e-01,
        -1.6459e-01, -4.7595e-01, -7.3572e-01, -9.1577e-01, -9.9658e-01,
        -9.6940e-01, -8.3717e-01, -6.1421e-01, -3.2470e-01,  1.7485e-07],
       grad_fn=<SinBackward>)
# 注意 grad_fn处 是 <SinBackward>
代码语言:javascript
复制
>>> c = 2 * b
>>> c
tensor([ 0.0000e+00,  6.4940e-01,  1.2284e+00,  1.6743e+00,  1.9388e+00,
         1.9932e+00,  1.8315e+00,  1.4714e+00,  9.5189e-01,  3.2919e-01,
        -3.2919e-01, -9.5189e-01, -1.4714e+00, -1.8315e+00, -1.9932e+00,
        -1.9388e+00, -1.6743e+00, -1.2284e+00, -6.4940e-01,  3.4969e-07],
       grad_fn=<MulBackward0>)
# 注意 grad_fn处 是 <MulBackward0>
代码语言:javascript
复制
>>> d = c + 1
>>> d
tensor([ 1.0000,  1.6494,  2.2284,  2.6743,  2.9388,  2.9932,  2.8315,  2.4714,
         1.9519,  1.3292,  0.6708,  0.0481, -0.4714, -0.8315, -0.9932, -0.9388,
        -0.6743, -0.2284,  0.3506,  1.0000], grad_fn=<AddBackward0>)
# 注意 grad_fn处 是 <AddBackward0>
代码语言:javascript
复制
>>> out = d.sum()
>>> out
tensor(20.0000, grad_fn=<SumBackward0>)
# 注意 grad_fn处 是 <SumBackward0>
代码语言:javascript
复制
>>> d.grad_fn # 对应 d = c + 1
<AddBackward0 object at 0x00000274BEB678D0>
>>> d.grad_fn.next_functions # 对应 c = 2 * b
((<MulBackward0 object at 0x00000274E60235F8>, 0), (None, 0))
>>> d.grad_fn.next_functions[0][0].next_functions #对应 b = torch.sin(a)
((<SinBackward object at 0x00000274E60235F8>, 0), (None, 0))
>>> d.grad_fn.next_functions[0][0].next_functions[0][0].next_functions
((<AccumulateGrad object at 0x00000274BEB678D0>, 0),)
代码语言:javascript
复制
>>> c.grad_fn
<MulBackward0 object at 0x00000274E692A898>
代码语言:javascript
复制
>>> b.grad_fn
<SinBackward object at 0x00000274E60235F8>
代码语言:javascript
复制
>>> a.grad_fn
None # 叶子节点没有grad_fn
代码语言:javascript
复制
代码语言:javascript
复制
# d.backward() # RuntimeError("grad can be implicitly created only for scalar outputs")
>>> out.backward() # 对输出调用backward函数后
>>> a.grad  # 接口获得其对叶子节点的梯度
tensor([ 2.0000,  1.8916,  1.5783,  1.0939,  0.4910, -0.1652, -0.8034, -1.3546,        -1.7589, -1.9727, -1.9727, -1.7589, -1.3546, -0.8034, -0.1652,  0.4910,         1.0939,  1.5783,  1.8916,  2.0000])
>>> b.grad  # 如果访问非叶子节点的梯度,则返回None,并触发一个警告
None
UserWarning: The .grad attribute of a Tensor that is not a leaf Tensor is being accessed. Its .grad attribute won't be populated during autograd.backward(). If you indeed want the gradient for a non-leaf Tensor, use .retain_grad() on the non-leaf Tensor. If you access the non-leaf Tensor by mistake, make sure you access the leaf Tensor instead. See github.com/pytorch/pytorch/pull/30531 for more information.

在这个简单的例子中, 仅仅只有输入 a 是一个叶子节点, 所以只有它才有梯度计算。

绘图

代码语言:javascript
复制
# 直接用 a 和 b 绘图会报错:
# RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
plt.plot(a.data, b.data, "r-", label="Original") 
# 或用 plt.plot(a.detach(), b.detach(), "r-") 
# detach 函数返回一个新的张量,从当前的计算图脱离
# plt.show()
plt.plot(a.data, a.grad.data, 'b-', label='Grad')
plt.legend()
plt.show()
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-03-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Python可视化编程机器学习OpenCV 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档