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 相关文章推荐
python使用cookielib库示例分享
Mar 03 Python
Python中逗号的三种作用实例分析
Jun 08 Python
使用pandas批量处理矢量化字符串的实例讲解
Jul 10 Python
django.db.utils.ProgrammingError: (1146, u“Table‘’ doesn’t exist”)问题的解决
Jul 13 Python
使用Python获取并处理IP的类型及格式方法
Nov 01 Python
python读取txt文件,去掉空格计算每行长度的方法
Dec 20 Python
python 限制函数执行时间,自己实现timeout的实例
Jan 12 Python
jupyter notebook 中输出pyecharts图实例
Apr 23 Python
linux下python中文乱码解决方案详解
Aug 28 Python
Python线程协作threading.Condition实现过程解析
Mar 12 Python
Python基于jieba, wordcloud库生成中文词云
May 13 Python
互斥锁解决 Python 中多线程共享全局变量的问题(推荐)
Sep 28 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脚本的10个技巧(6)
2006/10/09 PHP
php.ini中的request_order推荐设置
2015/05/10 PHP
PHP操作XML中XPath的应用示例
2019/07/04 PHP
Javascript 读书笔记索引贴
2010/01/11 Javascript
在jQuery1.5中使用deferred对象 着放大镜看Promise
2011/03/12 Javascript
js获取本机的外网/广域网ip地址完整源码
2013/08/12 Javascript
jquery解决客户端跨域访问问题
2015/01/06 Javascript
使用jQuery获得内容以及内容的属性
2015/02/26 Javascript
Vue.js组件使用开发实例教程
2016/11/01 Javascript
微信小程序 使用canvas制作K线实例详解
2017/01/12 Javascript
在Vue methods中调用filters里的过滤器实例
2018/08/30 Javascript
15个顶级开源JavaScript框架和库
2018/10/10 Javascript
JavaScript数组去重实现方法小结
2020/01/17 Javascript
vue绑定数字类型 value为数字的实例
2020/08/31 Javascript
[37:21]完美世界DOTA2联赛PWL S2 Inki vs Magma 第二场 11.22
2020/11/24 DOTA
Python编写一个优美的下载器
2018/04/15 Python
python程序封装为win32服务的方法
2021/03/07 Python
PyCharm 创建指定版本的 Django(超详图解教程)
2019/06/18 Python
Python web如何在IIS发布应用过程解析
2020/05/27 Python
python三引号如何输入
2020/07/06 Python
keras分类之二分类实例(Cat and dog)
2020/07/09 Python
Python模块常用四种安装方式
2020/10/20 Python
HTML5标签嵌套规则详解【必看】
2016/04/26 HTML / CSS
贝尔帐篷精品店:Bell Tent Boutique
2019/06/12 全球购物
英国亚马逊官方网站:Amazon.co.uk
2019/08/09 全球购物
Mountain Warehouse德国官网:英国户外零售商
2019/08/11 全球购物
超市开店计划书
2014/04/26 职场文书
我的画教学反思
2014/04/28 职场文书
企业年度评优方案
2014/06/02 职场文书
护理目标管理责任书
2014/07/25 职场文书
银行催款通知书
2015/04/17 职场文书
2015年社区妇联工作总结
2015/04/21 职场文书
机械生产实习心得体会
2016/01/22 职场文书
利用python做表格数据处理
2021/04/13 Python
python使用shell脚本创建kafka连接器
2022/04/29 Python
MySQL一劳永逸永久支持输入中文的方法实例
2022/08/05 MySQL