PyTorch——backward函数详细解析

本文主要介绍PyTorch中backward函数和grad的各种用法


梯度的定义

  • \(y\)对\(x\)的梯度可以理解为: 当 \(x\) 增加1的时候, \(y\) 值的增加量
  • 如果\(x\)是矢量(矩阵或者向量等),那么计算时也需要看成是多个标量的组合来计算,算出来的值表示的也是 \(x\) 当前维度的值增加1的时候, \(y\) 值的增加量

backward基础用法

  • tensorflow是先建立好图,在前向过程中可以选择执行图的某个部分(每次前向可以执行图的不同部分,前提是,图里必须包含了所有可能情况)
  • pytorch是每次前向过程都会重新建立一个图,反向(backward)的时候会释放,每次的图可以不一样, 所以在Pytorch中可以随时使用if, while等语句
    • tensorflow中使用if, while就得在传入数据前(构建图时)告诉图需要构建哪些逻辑,然后才能传入数据运行
    • PyTorch中由于不用在传入数据前先定义图(图和数据一起到达,图构建的同时开始计算数据?)

计算标量对标量的梯度

  • 结构图如下所示

  • 上面图的代码构建如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    import torch
    from torch.autograd import Variable

    w1 = Variable(torch.Tensor([2]),requires_grad=True)
    w2 = Variable(torch.Tensor([3]),requires_grad=True)
    w3 = Variable(torch.Tensor([5]),requires_grad=True)
    x = w1 + w2
    y = w2*w3
    z = x+y
    z.backward()
    print(w1.grad)
    print(w2.grad)
    print(w3.grad)
    print(x.grad)
    print(y.grad)

    # output:
    tensor([1.])
    tensor([6.])
    tensor([3.])
    None
    None
    • 从图中的推导可知,梯度符合预期
    • \(x, y\)不是叶节点,没有梯度存储下来,注意可以理解为梯度计算了,只是没有存储下来,PyTorch中梯度是一层层计算的

计算标量对矢量的梯度

  • 修改上面的构建为

    • 增加变量 \(s = z.mean\),然后直接求取\(s\)的梯度
  • 结构图如下:

  • 代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    import torch
    from torch.autograd import Variable

    w1 = Variable(torch.ones(2,2)*2,requires_grad=True)
    w2 = Variable(torch.ones(2,2)*3,requires_grad=True)
    w3 = Variable(torch.ones(2,2)*5,requires_grad=True)
    x = w1 + w2
    y = w2*w3
    z = x+y
    # z.backward()
    s = z.mean()
    s.backward()
    print(w1.grad)
    print(w2.grad)
    print(w3.grad)
    print(x.grad)
    print(y.grad)
    # output:
    tensor([[0.2500, 0.2500],
    [0.2500, 0.2500]])
    tensor([[1.5000, 1.5000],
    [1.5000, 1.5000]])
    tensor([[0.7500, 0.7500],
    [0.7500, 0.7500]])
    None
    None
    • 显然推导结果符合代码输出预期
    • 梯度的维度与原始自变量的维度相同,每个元素都有自己对应的梯度,表示当当前元素增加1的时候, 因变量值的增加量

计算矢量对矢量的梯度

  • 还以上面的结构图为例

  • 直接求中间节点 \(z\) 关于自变量的梯度

  • 代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import torch
    from torch.autograd import Variable

    w1 = Variable(torch.ones(2,2)*2, requires_grad=True)
    w2 = Variable(torch.ones(2,2)*3, requires_grad=True)
    w3 = Variable(torch.ones(2,2)*5, requires_grad=True)
    x = w1 + w2
    y = w2*w3
    z = x+y
    z_w1_grad = torch.autograd.grad(outputs=z, inputs=w1, grad_outputs=torch.ones_like(z))
    print(z_w1_grad)
    • 在因变量是矢量时,grad_outputs参数不能为空,标量时可以为空(grad_outputs为空时和grad_outputs维度为1时等价)
    • grad_outputs的维度必须和outputs参数的维度兼容

关于autograd.grad函数

grad_outputs参数详解
  • 在因变量是矢量时,grad_outputs参数不能为空,标量时可以为空(grad_outputs为空时和grad_outputs维度为1时等价)
  • grad_outputs的维度必须和outputs参数的维度兼容
    [待更新]