pytorch .detach().detach_()和 .data 切断反向传播
当我们再训练网络的时候可能希望保持一部分的网络参数不变,只对其中一部分的参数进行调整;或者只训练部分分支网络,并不让其梯度对主网络的梯度造成影响,这时候我们就需要使用detach()函数来切断一些分支的反向传播。
不过在查看前人写的一些代码时,经常会看见.data,下面介绍一下区别:
.data
import torch
a = torch.tensor([1,2,3.], requires_grad =True)
out = a.sigmoid()
c = out.data #c取出out的tensor之后 require s_grad = False
print(out.requires_grad)
print(c.requires_grad)
print(c.zero_()) #改变c也会改变out 但是通过c改变out的值并不能被autograd追踪求微分
print(out)
out.sum().backward() #但却会影响out的求导 不给出报错
print(a.grad) # 这个结果很严重的错误,因为out已经改变了
True
False
tensor([0., 0., 0.])
tensor([0., 0., 0.], grad_fn=)
tensor([0., 0., 0.]) //这是一个不应该计算出来的错误结果
.detach()
c = out.detach() #c取出out的tensor之后 require s_grad = False
print(out.requires_grad)
print(c.requires_grad)
print(c.zero_()) #改变c也会改变out 但是通过c改变out的值 能被autograd追踪
print(out)
out.sum().backward() #会影响out的求导 但会给出报错 如果注释掉c.zero_()就不会出错,也不会报错啦
print(a.grad)
True
False
tensor([0., 0., 0.])
tensor([0., 0., 0.], grad_fn=)
RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation: [torch.FloatTensor [3]], which is output 0 of SigmoidBackward, is at version 1; expected version 0 instead. Hint: enable anomaly detection to find the operation that failed to compute its gradient, with torch.autograd.set_detect_anomaly(True).
注:就算后面更改了c的require s_grad也是不能成功求导的。
.detach_()
c = out.detach_() #c取出out的tensor之后 require s_grad = False
print(out.requires_grad)
print(c.requires_grad)
print(c.zero_()) #改变c也会改变out 但是通过c改变out的值 能被autograd追踪
print(out)
out.sum().backward() #会影响out的求导
print(a.grad)
False
False
tensor([0., 0., 0.])
tensor([0., 0., 0.])
RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn
总结
总而言之,.data是以前老版本的使用方法,现在使用.detach()就好啦。.detach()从当前计算图中分离下来的,但是仍指向原变量的存放位置,同之处只是requires_grad为false,得到的这个Variable永远不需要计算其梯度,不具有grad。进行反向传播时,到该调用detach()的Variable就会停止,不能再继续向前进行传播。可见函数进行的操作有:将grad_fn设置为None 将Variable的requires_grad设置为False。
detach()和detach_()很像,两个的区别就是detach_()是对本身的更改,detach()则是生成了一个新的变量,比如x->m->y中如果对m进行detach(),后面如果反悔想还是对原来的计算图进行操作还是可以的。但是如果是进行了detach_(),那么原来的计算图也发生了变化,x->m->y直接变成了x, m->y。
补充:.clone()
c = out.clone() #c取出out的tensor之后 require s_grad = Ture
print(out.requires_grad)
print(c.requires_grad)
print(c.zero_()) #改变c也会改变out 但是通过c改变out的值 能被autograd追踪
print(out)
out.sum().backward() #会影响out的求导 但是正确的
print(a.grad)
True
True
tensor([0., 0., 0.], grad_fn=)
tensor([0.7311, 0.8808, 0.9526], grad_fn=)
tensor([0.1966, 0.1050, 0.0452])
.clone()的数据require s_grad = Ture,改变c也会改变out,同时能够将梯度进行传播。