浅谈Pytorch中的自动求导函数backward()所需参数的含义


Posted in Python onFebruary 29, 2020

正常来说backward( )函数是要传入参数的,一直没弄明白backward需要传入的参数具体含义,但是没关系,生命在与折腾,咱们来折腾一下,嘿嘿。

对标量自动求导

首先,如果out.backward()中的out是一个标量的话(相当于一个神经网络有一个样本,这个样本有两个属性,神经网络有一个输出)那么此时我的backward函数是不需要输入任何参数的。

import torch
from torch.autograd import Variable
 
a = Variable(torch.Tensor([2,3]),requires_grad=True)
b = a + 3
c = b * 3
out = c.mean()
out.backward()
print('input:')
print(a.data)
print('output:')
print(out.data.item())
print('input gradients are:')
print(a.grad)

运行结果:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

不难看出,我们构建了这样的一个函数:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

所以其求导也很容易看出:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

这是对其进行标量自动求导的结果.

对向量自动求导

如果out.backward()中的out是一个向量(或者理解成1xN的矩阵)的话,我们对向量进行自动求导,看看会发生什么?

先构建这样的一个模型(相当于一个神经网络有一个样本,这个样本有两个属性,神经网络有两个输出):

import torch
from torch.autograd import Variable
 
a = Variable(torch.Tensor([[2.,4.]]),requires_grad=True)
b = torch.zeros(1,2)
b[0,0] = a[0,0] ** 2 
b[0,1] = a[0,1] ** 3 
out = 2 * b
#其参数要传入和out维度一样的矩阵
out.backward(torch.FloatTensor([[1.,1.]]))
print('input:')
print(a.data)
print('output:')
print(out.data)
print('input gradients are:')
print(a.grad)

模型也很简单,不难看出out求导出来的雅克比应该是:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

因为a1 = 2,a2 = 4,所以上面的矩阵应该是:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

运行的结果:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

嗯,的确是8和96,但是仔细想一想,和咱们想要的雅克比矩阵的形式也不一样啊。难道是backward自动把0给省略了?

咱们继续试试,这次在上一个模型的基础上进行小修改,如下:

import torch
from torch.autograd import Variable
 
a = Variable(torch.Tensor([[2.,4.]]),requires_grad=True)
b = torch.zeros(1,2)
b[0,0] = a[0,0] ** 2 + a[0,1] 
b[0,1] = a[0,1] ** 3 + a[0,0]
out = 2 * b
#其参数要传入和out维度一样的矩阵
out.backward(torch.FloatTensor([[1.,1.]]))
print('input:')
print(a.data)
print('output:')
print(out.data)
print('input gradients are:')
print(a.grad)

可以看出这个模型的雅克比应该是:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

运行一下:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

等等,什么鬼?正常来说不应该是

浅谈Pytorch中的自动求导函数backward()所需参数的含义

么?我是谁?我再哪?为什么就给我2个数,而且是 8 + 2 = 10 ,96 + 2 = 98 。难道都是加的 2 ?想一想,刚才咱们backward中传的参数是 [ [ 1 , 1 ] ],难道安装这个关系对应求和了?咱们换个参数来试一试,程序中只更改传入的参数为[ [ 1 , 2 ] ]:

import torch
from torch.autograd import Variable
 
a = Variable(torch.Tensor([[2.,4.]]),requires_grad=True)
b = torch.zeros(1,2)
b[0,0] = a[0,0] ** 2 + a[0,1] 
b[0,1] = a[0,1] ** 3 + a[0,0]
out = 2 * b
#其参数要传入和out维度一样的矩阵
out.backward(torch.FloatTensor([[1.,2.]]))
print('input:')
print(a.data)
print('output:')
print(out.data)
print('input gradients are:')
print(a.grad)

浅谈Pytorch中的自动求导函数backward()所需参数的含义

嗯,这回可以理解了,我们传入的参数,是对原来模型正常求导出来的雅克比矩阵进行线性操作,可以把我们传进的参数(设为arg)看成一个列向量,那么我们得到的结果就是:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

在这个题目中,我们得到的实际是:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

看起来一切完美的解释了,但是就在我刚刚打字的一刻,我意识到官方文档中说k.backward()传入的参数应该和k具有相同的维度,所以如果按上述去解释是解释不通的。哪里出问题了呢?

仔细看了一下,原来是这样的:在对雅克比矩阵进行线性操作的时候,应该把我们传进的参数(设为arg)看成一个行向量(不是列向量),那么我们得到的结果就是:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

也就是:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

这回我们就解释的通了。

现在我们来输出一下雅克比矩阵吧,为了不引起歧义,我们让雅克比矩阵的每个数值都不一样(一开始分析错了就是因为雅克比矩阵中有相同的数据),所以模型小改动如下:

import torch
from torch.autograd import Variable
 
a = Variable(torch.Tensor([[2.,4.]]),requires_grad=True)
b = torch.zeros(1,2)
b[0,0] = a[0,0] ** 2 + a[0,1] 
b[0,1] = a[0,1] ** 3 + a[0,0] * 2
out = 2 * b
#其参数要传入和out维度一样的矩阵
out.backward(torch.FloatTensor([[1,0]]),retain_graph=True)
A_temp = copy.deepcopy(a.grad)
a.grad.zero_()
out.backward(torch.FloatTensor([[0,1]]))
B_temp = a.grad
print('jacobian matrix is:')
print(torch.cat( (A_temp,B_temp),0 ))

如果没问题的话咱们的雅克比矩阵应该是 [ [ 8 , 2 ] , [ 4 , 96 ] ]

好了,下面是见证奇迹的时刻了,不要眨眼睛奥,千万不要眨眼睛… 3 2 1 砰…

浅谈Pytorch中的自动求导函数backward()所需参数的含义

好了,现在总结一下:因为经过了复杂的神经网络之后,out中每个数值都是由很多输入样本的属性(也就是输入数据)线性或者非线性组合而成的,那么out中的每个数值和输入数据的每个数值都有关联,也就是说【out】中的每个数都可以对【a】中每个数求导,那么我们backward()的参数[k1,k2,k3…kn]的含义就是:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

也可以理解成每个out分量对an求导时的权重。

对矩阵自动求导

现在,如果out是一个矩阵呢?

下面的例子也可以理解为:相当于一个神经网络有两个样本,每个样本有两个属性,神经网络有两个输出。

import torch
from torch.autograd import Variable
from torch import nn

a = Variable(torch.FloatTensor([[2,3],[1,2]]),requires_grad=True)
w = Variable( torch.zeros(2,1),requires_grad=True )
out = torch.mm(a,w)
out.backward(torch.FloatTensor([[1.],[1.]]),retain_graph=True)
print("gradients are:{}".format(w.grad.data))

如果前面的例子理解了,那么这个也很好理解,backward输入的参数k是一个2x1的矩阵,2代表的就是样本数量,就是在前面的基础上,再对每个样本进行加权求和。结果是:

浅谈Pytorch中的自动求导函数backward()所需参数的含义

如果有兴趣,也可以拓展一下多个样本的多分类问题,猜一下k的维度应该是【输入样本的个数 * 分类的个数】

好啦,纠结我好久的pytorch自动求导原理算是彻底搞懂啦~~~

以上这篇浅谈Pytorch中的自动求导函数backward()所需参数的含义就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python实现基本进制转换的方法
Jul 11 Python
深入理解python函数递归和生成器
Jun 06 Python
tf.truncated_normal与tf.random_normal的详细用法
Mar 05 Python
python如何压缩新文件到已有ZIP文件
Mar 14 Python
符合语言习惯的 Python 优雅编程技巧【推荐】
Sep 25 Python
Python读取csv文件分隔符设置方法
Jan 14 Python
python 读取修改pcap包的例子
Jul 23 Python
pytorch 改变tensor尺寸的实现
Jan 03 Python
Python json格式化打印实现过程解析
Jul 21 Python
Python批量删除mysql中千万级大量数据的脚本分享
Dec 03 Python
使用tkinter实现三子棋游戏
Feb 25 Python
如何使用Python对NetCDF数据做空间相关分析
Apr 21 Python
python数据预处理 :样本分布不均的解决(过采样和欠采样)
Feb 29 #Python
python实现门限回归方式
Feb 29 #Python
Python3.9又更新了:dict内置新功能
Feb 28 #Python
python实现logistic分类算法代码
Feb 28 #Python
python GUI库图形界面开发之PyQt5打印控件QPrinter详细使用方法与实例
Feb 28 #Python
使用sklearn的cross_val_score进行交叉验证实例
Feb 28 #Python
彻底搞懂 python 中文乱码问题(深入分析)
Feb 28 #Python
You might like
PHP substr 截取字符串出现乱码问题解决方法[utf8与gb2312]
2011/12/16 PHP
php中require和require_once的区别说明
2014/02/27 PHP
PHP常用函数之格式化时间操作示例
2019/10/21 PHP
javascript 禁止复制网页
2009/06/11 Javascript
Javascript常用字符串判断函数代码分享
2014/12/08 Javascript
js实现跟随鼠标移动且带关闭功能的图片广告实例
2015/02/26 Javascript
浅谈jQuery中setInterval()方法
2015/07/07 Javascript
Bootstrap+jfinal退出系统弹出确认框的实现方法
2016/05/30 Javascript
JavaScript职责链模式概述
2016/09/17 Javascript
微信公众号 客服接口的开发实例详解
2016/09/28 Javascript
AngularJs表单校验功能实例代码
2017/02/09 Javascript
vue项目中axios使用详解
2018/02/07 Javascript
React 组件间的通信示例
2018/06/14 Javascript
微信小程序下拉框功能的实例代码
2018/11/06 Javascript
jQuery实现为table表格动态添加或删除tr功能示例
2019/02/19 jQuery
关于Vue源码vm.$watch()内部原理详解
2019/04/26 Javascript
JavaScript实现栈结构Stack过程详解
2020/03/07 Javascript
基于javascript的无缝滚动动画1
2020/08/07 Javascript
vue项目打包后提交到git上为什么没有dist这个文件的解决方法
2020/09/16 Javascript
基于numpy.random.randn()与rand()的区别详解
2018/04/17 Python
解决pycharm remote deployment 配置的问题
2019/06/27 Python
基于Jquery和Css3代码制作可以缩放的搜索框
2015/11/19 HTML / CSS
Staples加拿大官方网站:办公用品一站式采购
2016/09/25 全球购物
机械专业个人求职自荐信格式
2013/09/21 职场文书
财务会计人员求职的自我评价
2014/01/13 职场文书
迟到检讨书5000字
2014/01/31 职场文书
简历的自我评价
2014/02/03 职场文书
采购部部长岗位职责
2014/02/06 职场文书
毕业生就业协议书
2014/04/11 职场文书
应聘英语教师求职信
2014/04/24 职场文书
个人查摆剖析材料
2014/10/16 职场文书
2014年医院后勤工作总结
2014/12/06 职场文书
白鹤梁导游词
2015/02/06 职场文书
2016党员党课心得体会
2016/01/07 职场文书
Python 如何将integer转化为罗马数(3999以内)
2021/06/05 Python
Android实现图片九宫格
2022/06/28 Java/Android