天天看点

Pytorch系列:backward()函数介绍1. torch.Tensor.backward()2. torch.autograd.backward()

点击这里了解反向传播算法

1. torch.Tensor.backward()

(1)函数的作用

  • 获取计算图中某个tensor的叶子节点的梯度(无法获取非叶子节点的梯度)

计算图:一个函数构成了一个计算图,计算图的根节点是函数的输出,叶子节点是函数的输入

叶子节点:图结构中没有子节点的节点

Pytorch系列:backward()函数介绍1. torch.Tensor.backward()2. torch.autograd.backward()

上述代码定义了一个复合函数:

x1 = 1, x2 = 2
y1 = x1 + 1, y2 = x2 + 1
z = 0.5 * (y1^2 + y2^2)
           

z.backward()

表示对

z

的叶子节点求导,也就是对

x1

x2

求导,依据链式求导法则,可以求得两者梯度分别为

2

3

,与输出一致。值得注意,

y1

y2

是函数

z

的中间变量(非叶子节点),因此其导数值为

None

(2)参数解释

  • gradient(Tensor or None)

    :对调用backward()的非标量提供父节点的梯度信息

①函数

z

的值为标量(scalar)时,反向对函数的叶子节点求导,不需要gradient参数(设置为

None

),如上所示

②函数

z

的值为非标量(no-scalar)时,需要与函数值形状相同的tensor作为gradient的梯度,这里gradient参数表示函数

z

的父节点传递至

z

的梯度(下述代码中将该梯度赋值为)

Pytorch系列:backward()函数介绍1. torch.Tensor.backward()2. torch.autograd.backward()

上述代码定义了一个复合函数:

x1 = 1, x2 = 2
y1 = x1 + 1, y2 = x2 + 1
z1 = y1^2, z2 = y2^2
           

其中,

z1

关于

x1

的导数值是

4

,再乘以父节点传回的梯度

1

,最终计算出的

z1

关于

x1

的导数值是

4

Pytorch系列:backward()函数介绍1. torch.Tensor.backward()2. torch.autograd.backward()

z2

关于

x2

的导数值是

6

,再乘以父节点传回的梯度

0.1

,最终计算出的

z1

关于

x1

的导数值是

0.6

Pytorch系列:backward()函数介绍1. torch.Tensor.backward()2. torch.autograd.backward()
  • retain_graph (bool, optional)

    :用于多次反向传播时累加梯度信息

①一般情况下,进行一次反向传播更新各个参数后,参数的梯度信息会被释放,再前向传播计算损失值,再反向传播,即一次反向传播和一次前向传播是交替出现的,但是在有些网络中会存在多个子网络对同一个参数进行反向传播累加梯度值再更新该参数的情况,这种情况下,就不能在一次反向传播结束后就立即释放梯度信息了,retain_graph参数就是用来处理这种情况的,retain_graph的默认参数与create_graph相同。当retain_graph为

True

时,保留叶子节点的梯度信息。

Pytorch系列:backward()函数介绍1. torch.Tensor.backward()2. torch.autograd.backward()

经过计算可以发现,在第二次反向传播时,确实累加了第一次反向传播时叶子节点的梯度信息

  • create_graph (bool, optional)

    :用于高阶求导
import torch

x = torch.tensor([1.0], dtype=torch.float32, requires_grad=True)
y = x + 1
z = x + y**2
z.backward(retain_graph=False, create_graph=True) 
print(x.grad) # 输出[5.0]                                
torch.autograd.backward(x.grad)
print(x.grad) # 输出[7.0]
           
从输出可以判断,确实求出了

z

关于

x

的二阶导数,但令我疑惑的是,为什么第二次的输出值是一阶导和二阶导的累加值,我明明将

retain_graph

参数设置成

False

了呀

2. torch.autograd.backward()

(1)函数的作用

  • 计算某tensor叶子节点的梯度
  • 若该tensor为标量,则无需

    grad_tensors

    参数,若tensor为非标量,则需提供

    grad_tensors

    参数,

    grad_tensors

    参数形状必须与该tensor形状相同(可将传入的

    grad_tensors

    参数视为该tensor父节点回传的梯度)

    ①tensor为标量

    Pytorch系列:backward()函数介绍1. torch.Tensor.backward()2. torch.autograd.backward()
    ②tensor为非标量(不传入

    grad_tensors

    参数,报错)
    Pytorch系列:backward()函数介绍1. torch.Tensor.backward()2. torch.autograd.backward()
    Pytorch系列:backward()函数介绍1. torch.Tensor.backward()2. torch.autograd.backward()
    ③tensor为非标量(正确传入

    grad_tensors

    参数)
import torch

x = torch.tensor([1.0, 2.0], dtype=torch.float32, requires_grad=True)
y = x + 1
z = x + y
w = torch.autograd.backward(z, grad_tensors=torch.tensor([0.1, 0.2], dtype=torch.float32))
print(x.grad)
print(y.grad)
           
Pytorch系列:backward()函数介绍1. torch.Tensor.backward()2. torch.autograd.backward()

(2)参数解释

  • tensors (sequence of Tensor)

    需要计算叶子节点梯度的tensor
  • grad_tensors (sequence of (Tensor or None))

    计算非标量tensor叶子节点的梯度时需要提供该参数
  • retain_graph (bool, optional)

    是否保留反向传播的梯度信息以便二次反向传播时累加梯度,默认值与传入的

    create_graph

    值相同
import torch

x = torch.tensor([1.0], dtype=torch.float32, requires_grad=True)
y = x + 1
z = x + y
torch.autograd.backward(z, retain_graph=True) # 将retain_graph参数设置为,True以便下一次反向传播时累加梯度
print(x.grad) # 输出为[2.0]
torch.autograd.backward(z)
print(x.grad) # 输出为[4.0]
           
  • create_graph (bool, optional)

    用于保存计算图以便计算高阶导数
import torch

x = torch.tensor([1.0], dtype=torch.float32, requires_grad=True)
y = x + 1
z = x + y**2
torch.autograd.backward(z, create_graph=True) 
print(x.grad)     # 输出[5.0]                            
torch.autograd.backward(x.grad) # 再上次构建的反向传播的计算图上再次反向传播
print(x.grad)     # 输出[7.0],这里的输出是x的一阶导与二阶导的累加值
           
import torch

x = torch.tensor([1.0], dtype=torch.float32, requires_grad=True)
y = x + 1
z = x + y**2
torch.autograd.backward(z, retain_graph=False, create_graph=True) # 将retain_graph设置为True也没用
print(x.grad)    # 输出[5.0]                             
torch.autograd.backward(x.grad)
print(x.grad)    # 输出[7.0]
           
我也不清楚为啥将

create_graph

设置为

True

后再次对

x.grad

反向传播求出的值是一阶导和二阶导的累加值,欢迎懂的朋友指正

继续阅读