pytorch .detach() .detach_() 和 .data用于切断反向传播的实现


Posted in Python onDecember 27, 2019

当我们再训练网络的时候可能希望保持一部分的网络参数不变,只对其中一部分的参数进行调整;或者值训练部分分支网络,并不让其梯度对主网络的梯度造成影响,这时候我们就需要使用detach()函数来切断一些分支的反向传播

1   detach()[source]

返回一个新的Variable,从当前计算图中分离下来的,但是仍指向原变量的存放位置,不同之处只是requires_grad为false,得到的这个Variable永远不需要计算其梯度,不具有grad。

即使之后重新将它的requires_grad置为true,它也不会具有梯度grad

这样我们就会继续使用这个新的Variable进行计算,后面当我们进行反向传播时,到该调用detach()的Variable就会停止,不能再继续向前进行传播

源码为:

def detach(self):
    """Returns a new Variable, detached from the current graph.
    Result will never require gradient. If the input is volatile, the output
    will be volatile too.
    .. note::
     Returned Variable uses the same data tensor, as the original one, and
     in-place modifications on either of them will be seen, and may trigger
     errors in correctness checks.
    """
    result = NoGrad()(self) # this is needed, because it merges version counters
    result._grad_fn = None

 return result

可见函数进行的操作有:

  • 将grad_fn设置为None
  • 将Variable的requires_grad设置为False

如果输入 volatile=True(即不需要保存记录,当只需要结果而不需要更新参数时这么设置来加快运算速度),那么返回的Variable volatile=True。(volatile已经弃用)

注意:

返回的Variable和原始的Variable公用同一个data tensor。in-place函数修改会在两个Variable上同时体现(因为它们共享data tensor),当要对其调用backward()时可能会导致错误。

举例:

比如正常的例子是:

import torch

a = torch.tensor([1, 2, 3.], requires_grad=True)
print(a.grad)
out = a.sigmoid()

out.sum().backward()
print(a.grad)

返回:

(deeplearning) userdeMBP:pytorch user$ python test.py
None
tensor([0.1966, 0.1050, 0.0452])

当使用detach()但是没有进行更改时,并不会影响backward():

import torch

a = torch.tensor([1, 2, 3.], requires_grad=True)
print(a.grad)
out = a.sigmoid()
print(out)

#添加detach(),c的requires_grad为False
c = out.detach()
print(c)

#这时候没有对c进行更改,所以并不会影响backward()
out.sum().backward()
print(a.grad)

返回:

(deeplearning) userdeMBP:pytorch user$ python test.py
None
tensor([0.7311, 0.8808, 0.9526], grad_fn=<SigmoidBackward>)
tensor([0.7311, 0.8808, 0.9526])
tensor([0.1966, 0.1050, 0.0452])

可见c,out之间的区别是c是没有梯度的,out是有梯度的

如果这里使用的是c进行sum()操作并进行backward(),则会报错:

import torch

a = torch.tensor([1, 2, 3.], requires_grad=True)
print(a.grad)
out = a.sigmoid()
print(out)

#添加detach(),c的requires_grad为False
c = out.detach()
print(c)

#使用新生成的Variable进行反向传播
c.sum().backward()
print(a.grad)

返回:

(deeplearning) userdeMBP:pytorch user$ python test.py
None
tensor([0.7311, 0.8808, 0.9526], grad_fn=<SigmoidBackward>)
tensor([0.7311, 0.8808, 0.9526])
Traceback (most recent call last):
  File "test.py", line 13, in <module>
    c.sum().backward()
  File "/anaconda3/envs/deeplearning/lib/python3.6/site-packages/torch/tensor.py", line 102, in backward
    torch.autograd.backward(self, gradient, retain_graph, create_graph)
  File "/anaconda3/envs/deeplearning/lib/python3.6/site-packages/torch/autograd/__init__.py", line 90, in backward
    allow_unreachable=True)  # allow_unreachable flag
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn

如果此时对c进行了更改,这个更改会被autograd追踪,在对out.sum()进行backward()时也会报错,因为此时的值进行backward()得到的梯度是错误的:

import torch

a = torch.tensor([1, 2, 3.], requires_grad=True)
print(a.grad)
out = a.sigmoid()
print(out)

#添加detach(),c的requires_grad为False
c = out.detach()
print(c)
c.zero_() #使用in place函数对其进行修改

#会发现c的修改同时会影响out的值
print(c)
print(out)

#这时候对c进行更改,所以会影响backward(),这时候就不能进行backward(),会报错
out.sum().backward()
print(a.grad)

返回:

(deeplearning) userdeMBP:pytorch user$ python test.py
None
tensor([0.7311, 0.8808, 0.9526], grad_fn=<SigmoidBackward>)
tensor([0.7311, 0.8808, 0.9526])
tensor([0., 0., 0.])
tensor([0., 0., 0.], grad_fn=<SigmoidBackward>)
Traceback (most recent call last):
  File "test.py", line 16, in <module>
    out.sum().backward()
  File "/anaconda3/envs/deeplearning/lib/python3.6/site-packages/torch/tensor.py", line 102, in backward
    torch.autograd.backward(self, gradient, retain_graph, create_graph)
  File "/anaconda3/envs/deeplearning/lib/python3.6/site-packages/torch/autograd/__init__.py", line 90, in backward
    allow_unreachable=True)  # allow_unreachable flag
RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation

2   .data

如果上面的操作使用的是.data,效果会不同:

这里的不同在于.data的修改不会被autograd追踪,这样当进行backward()时它不会报错,回得到一个错误的backward值

import torch

a = torch.tensor([1, 2, 3.], requires_grad=True)
print(a.grad)
out = a.sigmoid()
print(out)


c = out.data
print(c)
c.zero_() #使用in place函数对其进行修改

#会发现c的修改同时也会影响out的值
print(c)
print(out)

#这里的不同在于.data的修改不会被autograd追踪,这样当进行backward()时它不会报错,回得到一个错误的backward值
out.sum().backward()
print(a.grad)

返回:

(deeplearning) userdeMBP:pytorch user$ python test.py
None
tensor([0.7311, 0.8808, 0.9526], grad_fn=<SigmoidBackward>)
tensor([0.7311, 0.8808, 0.9526])
tensor([0., 0., 0.])
tensor([0., 0., 0.], grad_fn=<SigmoidBackward>)
tensor([0., 0., 0.])

上面的内容实现的原理是:

In-place 正确性检查

所有的Variable都会记录用在他们身上的 in-place operations。如果pytorch检测到variable在一个Function中已经被保存用来backward,但是之后它又被in-place operations修改。当这种情况发生时,在backward的时候,pytorch就会报错。这种机制保证了,如果你用了in-place operations,但是在backward过程中没有报错,那么梯度的计算就是正确的。

⚠️下面结果正确是因为改变的是sum()的结果,中间值a.sigmoid()并没有被影响,所以其对求梯度并没有影响:

import torch

a = torch.tensor([1, 2, 3.], requires_grad=True)
print(a.grad)
out = a.sigmoid().sum() #但是如果sum写在这里,而不是写在backward()前,得到的结果是正确的
print(out)


c = out.data
print(c)
c.zero_() #使用in place函数对其进行修改

#会发现c的修改同时也会影响out的值
print(c)
print(out)

#没有写在这里
out.backward()
print(a.grad)

返回:

(deeplearning) userdeMBP:pytorch user$ python test.py
None
tensor(2.5644, grad_fn=<SumBackward0>)
tensor(2.5644)
tensor(0.)
tensor(0., grad_fn=<SumBackward0>)
tensor([0.1966, 0.1050, 0.0452])

3   detach_()[source]

将一个Variable从创建它的图中分离,并把它设置成叶子variable

其实就相当于变量之间的关系本来是x -> m -> y,这里的叶子variable是x,但是这个时候对m进行了.detach_()操作,其实就是进行了两个操作:

  • 将m的grad_fn的值设置为None,这样m就不会再与前一个节点x关联,这里的关系就会变成x, m -> y,此时的m就变成了叶子结点
  • 然后会将m的requires_grad设置为False,这样对y进行backward()时就不会求m的梯度

这么一看其实detach()和detach_()很像,两个的区别就是detach_()是对本身的更改,detach()则是生成了一个新的variable

比如x -> m -> y中如果对m进行detach(),后面如果反悔想还是对原来的计算图进行操作还是可以的

但是如果是进行了detach_(),那么原来的计算图也发生了变化,就不能反悔了

参考:https://pytorch-cn.readthedocs.io/zh/latest/package_references/torch-autograd/#detachsource

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
MySQLdb ImportError: libmysqlclient.so.18解决方法
Aug 21 Python
Python中解析JSON并同时进行自定义编码处理实例
Feb 08 Python
python常见的格式化输出小结
Dec 15 Python
python通过pip更新所有已安装的包实现方法
May 19 Python
基于python(urlparse)模板的使用方法总结
Oct 13 Python
Python3 伪装浏览器的方法示例
Nov 23 Python
Python设计模式之观察者模式简单示例
Jan 10 Python
python基础教程项目五之虚拟茶话会
Apr 02 Python
Python之pandas读写文件乱码的解决方法
Apr 20 Python
python-str,list,set间的转换实例
Jun 27 Python
在python中将list分段并保存为array类型的方法
Jul 15 Python
Python time库的时间时钟处理
May 02 Python
python操作gitlab API过程解析
Dec 27 #Python
python  ceiling divide 除法向上取整(或小数向上取整)的实例
Dec 27 #Python
python使用协程实现并发操作的方法详解
Dec 27 #Python
Python调用.NET库的方法步骤
Dec 27 #Python
IronPython连接MySQL的方法步骤
Dec 27 #Python
python基于三阶贝塞尔曲线的数据平滑算法
Dec 27 #Python
python3获取文件中url内容并下载代码实例
Dec 27 #Python
You might like
PHP 中执行系统外部命令
2006/10/09 PHP
phpinfo 系统查看参数函数代码
2009/06/05 PHP
简单的cookie计数器实现源码
2013/06/07 PHP
php实现微信公众号企业转账功能
2018/10/01 PHP
php中如何执行linux命令详解
2018/11/06 PHP
解决3.01版的jquery.form.js中文乱码问题的解决方法
2012/03/08 Javascript
javascript 使用 NodeList需要注意的问题
2013/03/04 Javascript
子窗体与父窗体传值示例js代码
2013/08/01 Javascript
将json当数据库一样操作的javascript lib
2013/10/28 Javascript
js解析json读取List中的实体对象示例
2014/03/11 Javascript
JavaScript代码性能优化总结篇
2016/05/15 Javascript
Node.js DES加密的简单实现
2016/07/07 Javascript
详解ES6中的let命令
2020/04/05 Javascript
关于使用axios的一些心得技巧分享
2017/07/02 Javascript
Bootstrap Table 在指定列中添加下拉框控件并获取所选值
2017/07/31 Javascript
vue移动端弹框组件的实例
2018/09/25 Javascript
使用JS判断页面是首次被加载还是刷新
2019/05/26 Javascript
jquery 插件重新绑定的处理方法分析
2019/11/23 jQuery
AI小程序之语音听写来了,十分钟掌握百度大脑语音听写全攻略
2020/03/13 Javascript
python通过exifread模块获得图片exif信息的方法
2015/03/16 Python
Python实现的破解字符串找茬游戏算法示例
2017/09/25 Python
Python实现简单遗传算法(SGA)
2018/01/29 Python
Python浅复制中对象生存周期实例分析
2018/04/02 Python
numpy按列连接两个维数不同的数组方式
2019/12/06 Python
python使用re模块爬取豆瓣Top250电影
2020/10/20 Python
Python3+Appium安装及Appium模拟微信登录方法详解
2021/02/16 Python
Django后端按照日期查询的方法教程
2021/02/28 Python
Visual-Click葡萄牙:欧洲领先的在线眼镜商
2020/02/17 全球购物
EJB的几种类型
2012/08/15 面试题
学期自我鉴定
2013/11/04 职场文书
旅游管理专业个人求职信范文
2013/12/24 职场文书
少先队学雷锋活动月总结
2014/03/09 职场文书
我爱幼儿园演讲稿
2014/09/11 职场文书
初中班主任心得体会
2016/01/07 职场文书
MySQL RC事务隔离的实现
2022/03/31 MySQL
vue自定义右键菜单之全局实现
2022/04/09 Vue.js