Pytorch学习之梯度计算backward函数

Pytorch在梯度方面提供的功能,大多是为神经网络而设计的。而官方文档给出的定义和解释比较抽象。以下将结合实例,总结一下自己对Pytorch中梯度计算backward函数的理解。

1. 简单的神经网络构建

首先我们看一个非常简单的神经网络。

假设x1,x2是神经网络的中间层,y是我们的输出层,Y是真实值,L是loss。w1和w2是对应于x1和x2的weight。
上图用数学公式表示为:

  • \(x2= w1 * x1\)
  • \(y = w2 * x2\)
  • \(L = Y - y\)

通常我们会把x1,w1,w2,x2,y使用PyTorch的Tensor进行表示。L也可以用Tensor表示(维度可能与其他Tensor不同)。
其中,我们把需要自己设定的Tensor(即不是通过其他Tensor计算得来的)叫做叶子Tensor。比如x1,w1和w2就是所谓的叶子Tensor。
在pytorch中,我们把上述模型表示出来。

import torch
import numpy as np

x1 = torch.from_numpy( 2*np.ones((2, 2), dtype=np.float32) )
x1.requires_grad_(True)   #设置该tensor可被记录操作用于梯度计算
w1 = torch.from_numpy( 5*np.ones((2, 2), dtype=np.float32) )
w1.requires_grad_(True)
print("x1 =", x1)
print("w1 =", w1)

x2 = x1 * w1
w2 = torch.from_numpy( 6*np.ones((2,2), dtype=np.float32) )
w2.requires_grad_(True)
print("x2 =", x2)
print("w2 =", w2)

y = x2 * w2
Y = torch.from_numpy( 10*np.ones((2,2), dtype=np.float32) )
print("y =", y)
print("Y =", Y)

L = Y - y
x1 = tensor([[2., 2.],
        [2., 2.]], requires_grad=True)
w1 = tensor([[5., 5.],
        [5., 5.]], requires_grad=True)
x2 = tensor([[10., 10.],
        [10., 10.]], grad_fn=<MulBackward0>)
w2 = tensor([[6., 6.],
        [6., 6.]], requires_grad=True)
y = tensor([[60., 60.],
        [60., 60.]], grad_fn=<MulBackward0>)
Y = tensor([[10., 10.],
        [10., 10.]])

上述代码注意:

  1. 设置一个tensor的 requires_grad为True 会保存该Tensor是否记录所有操作用于计算梯度,可直接在创建tensor时指定属性requires_grad = True,也可以使用函数x.requires_grad_(True)。
  2. 通过运算得到的Tensor(非自己创建的tensor),会自动被赋值grad_fn属性。该属性表示梯度函数。

2. 反向传播的梯度计算

上述前向传播计算完成后,想要计算反向传播(BP)的梯度。基本原理即为求导的链式法则。上述网络的求导即为:

PyTorch提供了backward函数用于计算梯度 ,这一求解过程变为:

L.backward(torch.ones(2, 2, dtype=torch.float))

对于最后的Tensor L执行backward()函数,会计算之前参与运算并生成当前Tensor的叶子Tensor的梯度。其梯度值会保存在叶子Tensor的.grad属性中。
比如上述网络中,x1,w1和w2就是所谓的叶子Tensor。

print(x1.grad) # 查看L对于x1的梯度
print(w1.grad) # L对于w1的梯度
print(w2.grad)
tensor([[-30., -30.],
        [-30., -30.]])
tensor([[-12., -12.],
        [-12., -12.]])
tensor([[-10., -10.],
        [-10., -10.]])

1. backward函数的gradient参数解释

gradient 在PyTorch的官方文档上解释的比较晦涩,我理解这个参数表示的是网络的输出tensor对于调用backward()函数的Tensor的导数。

(1) 比如在我上述的模型输出tensor为L,当前要计算backward的tensor也为为L,则gradient表示为\(\frac{\partial L}{\partial L}=1\),也就是element全为1的Tensor。gradient维度需要调用backward()函数的Tensor的维度相同。即L.backward(torch.ones(2, 2, dtype=torch.float))

(2) 又比如,假设我们不知道L关于y的函数表示,但知道L关于y的梯度(即\(\frac{\partial L}{\partial y}=-1\))时,我们可以通过y.backward(-1 * torch.ones(2, 2, dtype=torch.float))来完成BP过程。 这样的设计通过链式法则,可以在特定位置求梯度值。

(3) 对于L为标量(常数)的情况,可不指定任何参数,默认参数为torch.tensor(1)。对于L为高于1维的情况,则需要明确指定backward()的第一个参数。

2. backward函数的其他注意点

(1) 默认同一个运算得到的Tensor仅能进行一次backward()。若要再次进行backward(),则要再次运算得到的Tesnor。

(2) 当多个Tensor从相同的源Tensor运算得到,这些运算得到的Tensor的backwards()方法将向源Tensor的grad属性中进行数值累加。
比如上述实例中,假设有另一个tensor L2是通过对x1的运算得到的,那么L2.backward()执行后梯度结果将累加到x1.grad中。

print("x1.grad =",x1.grad) # 原来x1的梯度
L2 = x1 * x1
L2.backward(torch.ones(2, 2, dtype=torch.float))
print("x1.grad =", x1.grad) # 计算L2的backward后梯度结果将累加到x1.grad中
x1.grad = tensor([[-26., -26.],
        [-26., -26.]])
x1.grad = tensor([[-22., -22.],
        [-22., -22.]])

(3) 只有叶子tensor(自己创建不是通过其他Tensor计算得来的)才能计算梯度。否则对于非叶子的x1执行L.backwar()后,x1.grad将为None。定义叶子节点时需注意要直接用torch创建且不能经过tensor计算。例如将实例中的x1定义改为x1 = 2 * torch.ones(2, 2, requires_grad=True, dtype=torch.float) 其实它已经做了tensor计算,x1将不再是叶子,表达式中torch.ones()才是叶子。

原文地址:https://www.cnblogs.com/laiyaling/p/12343844.html

时间: 2024-08-30 14:27:18

Pytorch学习之梯度计算backward函数的相关文章

Pytorch学习-训练CIFAR10分类器

output_10_1.png TRAINING A CLASSIFIER 参考Pytorch Tutorial:Deep Learning with PyTorch: A 60 Minute Blitz 在学会了以下后: 定义神经网络 计算损失函数 更新权重 What about data Generally, when you have to deal with image, text, audio or video data, you can use standard python pac

Pytorch学习--编程实战:猫和狗二分类

Pytorch学习系列(一)至(四)均摘自<深度学习框架PyTorch入门与实践>陈云 目录: 1.程序的主要功能 2.文件组织架构 3. 关于`__init__.py` 4.数据处理 5.模型定义 6.工具函数 7.配置文件 8.main.py 9.使用 1.程序的主要功能: 模型定义    数据加载    训练和测试 2.文件组织架构: ```├── checkpoints/├── data/│   ├── __init__.py│   ├── dataset.py│   └── get_

[pytorch学习]2. 官网60分钟教程摘要

https://pytorch.org/tutorials/beginner/deep_learning_60min_blitz.html 1. Pytorch的基本单元,tensor,本质上和numpy的操作类似:不同的主要在与可以自动计算微分和梯度(Autograd): 2. 每个tensor的requires_grad设置为True时,就能够自动计算梯度:操作时,只能修改枝叶变量的requires_grad: 3. Pytorch中建立神经网络的基本步骤: 1) 在Net(nn.Modul

集成学习之梯度提升树(GBDT)算法

梯度提升树(GBDT)的全称是Gradient Boosting Decision Tree.GBDT还有很多的简称,例如GBT(Gradient Boosting Tree), GTB(Gradient Tree Boosting ),GBRT(Gradient Boosting Regression Tree), MART(Multiple Additive Regression Tree)等,其实都是指的同一种算法,本文统一简称GBDT. GBDT 也是 Boosting 算法的一种,但是

nBodyCS&lt;I&gt;学习笔记之计算着色器

nBodyCS<I>学习笔记之计算着色器 Nvidia-SDK(1) 版权声明:本文为博主原创文章,未经博主允许不得转载. DirectX一直是Windows上图形和游戏开发的核心技术.DirectX提供了一种在显卡上运行的程序--着色器(Shader).从DirectX11开始,DirectX增加了一种计算着色器(Compute Shader),它是专门为与图形无关的通用计算设计的.因此DirectX就变成了一个通用GPU计算的平台.鉴于GPU拥有极其强大的并行运算能力,学习使用Direct

IOS学习之路-- 指针&amp;宏函数

如果*p被()包住,说明指针变量p将来指向的是函数 //声明一个指针变量 //int (*p)(int, int) = sum; int (*p)(int, int); p = sum; // 如果*p被()包住,说明指针变量p将来指向的是函数 // 最左边的void说明p指向的函数没有返回值 // 最右边的()说明p指向的函数没有形参 void (*p)(); // 函数名test就是test函数的地址 // 将test函数的地址赋值给了指针变量p // 指针变量p成功指向了test函数 p

Oracle学习(四)_SQL函数

--第一部分:SQL基础 --ch1 简单查询 --ch2 查询基本概念 --ch3 数据过滤 --第二部分:多表操作 --ch4 集合理论 --ch5 内连接 --ch6 外连接 --ch7 子查询 --第三部分:数据分组 --ch8 简单统计 --ch9 数据分组 --ch10 分组数据过滤 --第四部分:SQL函数 --ch11 内置函数 --ch12 case表达式 --第五部分:DML语句 --ch13 插入数据 --ch14 修改数据 --ch15 删除数据 ------------

好程序员大数据学习路线分享高阶函数

好程序员大数据学习路线分享高阶函数,我们通常将可以做为参数传递到方法中的表达式叫做函数 高阶函数包含:作为值的函数.匿名函数.闭包.柯里化等等. 定义函数时格式:val 变量名 =?(输入参数类型和个数)?=>?函数实现和返回值类型和个数 "="表示将函数赋给一个变量 "=>"左面表示输入参数名称.类型和个数,右边表示函数的实现和返回值类型和参数个数 作为值的函数 定义函数 scala> val func = (x:Int) => x * x

学习之&quot;setjmp和longjmp函数&quot;

Linux学习之"setjmp和longjmp函数" 转贴,原文地址:http://www.cnblogs.com/lq0729/archive/2011/10/23/2222117.html nsetjmp和longjmp函数实现函数之间的跳转(需包含头文件"setjmp.h"): 函数原型:int setjmp(jmp_buf env);   void longjmp(jmp_buf env, int val); setjmp函数用于设置跳转的目的位置,long