0501-Variable

0501-Variable

pytorch完整教程目录:https://www.cnblogs.com/nickchen121/p/14662511.html

一、Variable

1.1 Variable 的数据结构

autograd 模块的核心数据结构是 Variable,它是对 tensor 的封装,并且会记录 tensor 的操作记录用来构建计算图。Variable 的数据结构如下图所示:

上图主要包含三个属性:

  • data:保存 variable 所包含的 tensor
  • grad:保存 data 对应的梯度,grad 也是 variable,而非 tensor,它与 data 形状一致
  • grad_fn:指向一个 Function,记录 variable 的操作历史,即它是什么操作的输出,用来构建计算图。如果某一变量是用户创建的,则它为叶子节点,对应的 grad_fn 为 None

Variable 的构造函数需要传入 tensor,也有两个可选的参数:

  • requires_grad(bool):是否需要对该 Variable 求导
  • volatile(bool):设置为 True,构建在该 Variable 之上的图都不会求导

1.2 反向传播

Variable 支持大部分 tensor 支持的函数,但不支持部分 inplace 函数,因为它们会修改 tensor 自身,然而在反向传播中,Variable 需要缓存原来的 tensor 计算梯度。如果想要计算各个 Variable 的梯度,只需要调用根结点的 backward 方法,autograd 则会自动沿着计算图反向传播,计算每一个叶子节点的梯度。

variable.backward(grad_variable=None, retain_fraph=None, create_graph=None)的参数解释如下:

  • grad_variables:形状与 Variable 一直,对于 y.backward(),grad_variables 相当于链式法则\(\frac{\partial{z}}{\partial{x}} = \frac{\partial{z}}{\partial{y}}\frac{\partial{y}}{\partial{x}}\)。grad_variables 也可以是 tensor 或序列
  • retain_graph:反向传播会缓存一些中间结果,反向传播之后,这些缓存就会清除,可通过这个参数指定不清除缓存,用来多次反向传播
  • create_graph:对反向传播过程中再次构建计算图
import torch as t
from torch.autograd import Variable as V
a = V(t.ones(3, 4), requires_grad=True)
a
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]], requires_grad=True)
b = V(t.zeros(3, 4))
b
tensor([[0., 0., 0., 0.],
        [0., 0., 0., 0.],
        [0., 0., 0., 0.]])
c = a.add(b)  # variable 函数的使用和 tensor 一致,等同于 c=a+b
c
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]], grad_fn=<AddBackward0>)
# 注:虽然没有指定 c 需要求导,但 c 依赖于 a,由于 a 需要求导,因此 c 的 requeires_grad 默认设置为 True
a.requires_grad, b.requires_grad, c.requires_grad
(True, False, True)
# 注:`c.data.sum()`是在去 data 后变为 tensor,从 tensor 计算sum;`c.sum()`计算后仍然是 variable
d = c.sum()
d.backward()  # 反向传播
a.grad
tensor([[1., 1., 1., 1.],
        [1., 1., 1., 1.],
        [1., 1., 1., 1.]])
# 由用户创建的 Variable 属于叶子节点,对应的 grad_fn 是 None
a.is_leaf, b.is_leaf, c.is_leaf
(True, True, False)
# 虽然 `c.requires_grad=True`,但是它的梯度计算完之后就会被释放
c.retain_grad()  # 对于非叶节点求导,需进行保存,否则会被自动释放,这里我们先保存然后再查看,如果没有这一行,会报错
c.grad is None
True

1.3 autograd 求导数和手动求导数

通过对函数\(y=x^2e^x\)求导,我们可以看看 autograd 求导数和自己写个方法求导数的区别。这个函数的导数如下:

\[\frac{dy}{dx}=2xe^x+x^2e^x \]

def f(x):
    """计算 y"""
    y = x**2 * t.exp(x)
    return y


def grad_f(x):
    """手动对函数求导"""
    dx = 2 * x * t.exp(x) + x**2 * t.exp(x)
    return dx


x = V(t.randn(3, 4), requires_grad=True)
y = f(x)
y
tensor([[0.1456, 0.0344, 0.5350, 0.0165],
        [0.9979, 0.3471, 0.5367, 0.4838],
        [0.2595, 0.0010, 0.2002, 0.2058]], grad_fn=<MulBackward0>)
y_grad_variables = t.ones(
    y.size())  # 由于dz/dy=1,并且grad_variables 形状需要与 y 一致,详解看下面的 3.4 小节
y.backward(y_grad_variables)
x.grad
tensor([[ 1.0434, -0.3003, -0.0624,  0.2895],
        [ 3.8373,  1.8350,  0.0467, -0.2063],
        [-0.4456,  0.0655, -0.4609, -0.4604]])
grad_f(x)  # autograd 计算的结果和利用公式计算的结果一致
tensor([[ 1.0434, -0.3003, -0.0624,  0.2895],
        [ 3.8373,  1.8350,  0.0467, -0.2063],
        [-0.4456,  0.0655, -0.4609, -0.4604]], grad_fn=<AddBackward0>)
posted @ 2021-04-21 17:26  B站-水论文的程序猿  阅读(1702)  评论(0编辑  收藏  举报