Pytorch系列:backward()函数介绍

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

1. torch.Tensor.backward()

backward(gradient=None, retain_graph=None, create_graph=False)

(1)函数的作用

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

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

在这里插入图片描述
上述代码定义了一个复合函数:

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

z.backward()表示对z的叶子节点求导,也就是对x1x2 求导,依据链式求导法则,可以求得两者梯度分别为23,与输出一致。值得注意,y1y2是函数z的中间变量(非叶子节点),因此其导数值为None

(2)参数解释

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

①函数z的值为标量(scalar)时,反向对函数的叶子节点求导,不需要gradient参数(设置为None),如上所示
②函数z的值为非标量(no-scalar)时,需要与函数值形状相同的tensor作为gradient的梯度,这里gradient参数表示函数z的父节点传递至z的梯度(下述代码中将该梯度赋值为)
在这里插入图片描述
上述代码定义了一个复合函数:

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

其中,z1关于x1的导数值是4,再乘以父节点传回的梯度1,最终计算出的z1关于x1的导数值是4
在这里插入图片描述
z2关于x2的导数值是6,再乘以父节点传回的梯度0.1,最终计算出的z1关于x1的导数值是0.6
在这里插入图片描述

  • retain_graph (bool, optional):用于多次反向传播时累加梯度信息

①一般情况下,进行一次反向传播更新各个参数后,参数的梯度信息会被释放,再前向传播计算损失值,再反向传播,即一次反向传播和一次前向传播是交替出现的,但是在有些网络中会存在多个子网络对同一个参数进行反向传播累加梯度值再更新该参数的情况,这种情况下,就不能在一次反向传播结束后就立即释放梯度信息了,retain_graph参数就是用来处理这种情况的,retain_graph的默认参数与create_graph相同。当retain_graph为True时,保留叶子节点的梯度信息。
在这里插入图片描述
经过计算可以发现,在第二次反向传播时,确实累加了第一次反向传播时叶子节点的梯度信息

  • 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()

torch.autograd.backward(tensors, grad_tensors=None, retain_graph=None, create_graph=False, grad_variables=None)

(1)函数的作用

  • 计算某tensor叶子节点的梯度
  • 若该tensor为标量,则无需grad_tensors参数,若tensor为非标量,则需提供grad_tensors参数,grad_tensors参数形状必须与该tensor形状相同(可将传入的grad_tensors参数视为该tensor父节点回传的梯度)
    ①tensor为标量
    在这里插入图片描述
    ②tensor为非标量(不传入grad_tensors参数,报错)
    在这里插入图片描述
    在这里插入图片描述
    ③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)

在这里插入图片描述

(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反向传播求出的值是一阶导和二阶导的累加值,欢迎懂的朋友指正

  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值