测试torch.no_grad()的作用
torch.no_grad()两个作用:新增的tensor没有梯度,使带梯度的tensor能够进行原地运算。1.使带有梯度的tensor能够原地运算(更新tensor)from torch.autograd import Variablex = Variable(torch.ones(1, 2),requires_grad=True)x += 1 #或者调用x.add_(1) #注意add_下划线
torch.no_grad()两个作用:新增的tensor没有梯度,使带梯度的tensor能够进行原地运算。
1.使带有梯度的tensor能够原地运算(更新tensor)
from torch.autograd import Variable
x = Variable(torch.ones(1, 2),requires_grad=True)
x += 1 #或者调用x.add_(1) #注意add_下划线
报错:RuntimeError: a leaf Variable that requires grad has been used in an in-place operation.
下面通过torch.no_grad()进行运算
from torch.autograd import Variable
x = Variable(torch.ones(1, 2),requires_grad=True)
print(id(x)) #输出:140655195757952
print(x) #输出 :tensor([[1., 1.]], requires_grad=True)
with torch.no_grad():
x += 1 #或者调用x.add_(1) #注意add_下划线
print(id(x)) #输出:140655195757952 id没有改变
print(x)# 输出:tensor([[2., 2.]], requires_grad=True) 梯度还未True
x =x + 1
print(id(x)) #输出: 140655195756800 id改变
print(x) #输出:tensor([[3., 3.]], grad_fn=<AddBackward0>) 没有requires_grad=True状态
从输出中可以看到:x += 1
没有改变id,requires_grad
,但是执行x =x + 1
后id改变,且后边状态为grad_fn=<AddBackward0>
,记录了加法状态AddBackward0
,但是,这样之后执行backword()
后求梯度为变为None
,也就是普通赋值(非原地操作)不能对计算梯度的,如果想不用torch.no_grad()
,可以利用x.data
修改内容。如果:
from torch.autograd import Variable
import torch
x = Variable(torch.ones(1, 2),requires_grad=True)
y = x**2
c = x + 1 ##假如此处换为 x = x + 1,最后结果会输出什么?
z = y**2
z.sum().backward()
print(z.grad) ## 输出:None
print(y.grad) ##输出:None
print(x.grad) ##输出:tensor([[4., 4.]])
print(x) ##输出:tensor([[1., 1.]], requires_grad=True
print(y) ##:tensor([[1., 1.]], grad_fn=<PowBackward0>
print(z) ##:输出:tensor([[1., 1.]], grad_fn=<PowBackward0>)
print(c) ##:输出:tensor([[2., 2.]], grad_fn=<AddBackward0>)
从输出可以看到,只有requires_grad=True
状态的tensor才有梯度,如果将c = x + 1
替换为x = x + 1
后,x.grad
也会变为None
,打印x
为tensor([[2., 2.]], grad_fn=<AddBackward0>)
,所以,梯度输出为None
,其实,最开始的x
还是存在梯度。一旦tensor的requires_grad
不为True
时候,就能够随意原地操作了,没有梯度限制。
2.使带有梯度的tensor能够原地运算(更新tensor),新建的tensor不能传递梯度。
from torch.autograd import Variable
import torch
x = Variable(torch.ones(1, 10),requires_grad=True)
y1 = (x)**2
y2 = (x)**2
z = y1**2 + y2**2
z.sum().backward()
print(x.grad) ## 输出:tensor([[8., 8., 8., 8., 8., 8., 8., 8., 8., 8.]])
print(z) ##输出:tensor([[2., 2., 2., 2., 2., 2., 2., 2., 2., 2.]], grad_fn=<AddBackward0>)
x = Variable(torch.ones(1, 10),requires_grad=True)
y1 = (x)**2
with torch.no_grad():
y2 = (x)**2
z = y1**2 + y2**2
z.sum().backward()
print(x.grad) ##输出:tensor([[4., 4., 4., 4., 4., 4., 4., 4., 4., 4.]])
print(z)##输出:tensor([[2., 2., 2., 2., 2., 2., 2., 2., 2., 2.]], grad_fn=<AddBackward0>)
x = Variable(torch.ones(1, 10),requires_grad=True)
with torch.no_grad():
x += 1
y2 = (x)**2
y1 = (x)**2
z = y1**2 + y2**2
z.sum().backward()
print(x.grad)##输出tensor([[32., 32., 32., 32., 32., 32., 32., 32., 32., 32.]])
print(z)##输出tensor([[32., 32., 32., 32., 32., 32., 32., 32., 32., 32.]],grad_fn=<AddBackward0>)
第一次通过y1
和y2
的梯度全部计算;第二次仅仅计算了通过y1
的梯度,y2
没有传递梯度;第三次仅仅计算通过y1
的梯度,但是x
值改变导致y1
的导数和y1
改变,所以梯度为32
。
3.注意原地操作(更新tensor)之前不能用到相应tensor,如果用到,后面求梯度就不对之前用到tensor的tensor进行求导,否则报错:
from torch.autograd import Variable
import torch
x = Variable(torch.ones(1, 10),requires_grad=True)
y1 = (x)**2
with torch.no_grad():
x += 1
y2 = (x)**2
z = y1**2 + y2**2
z.sum().backward() ##这里会报错,报错内容见后面
print(x.grad)
print(z)
报错:RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation: [torch.FloatTensor [1, 10]] 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)
意思是需要求梯度的tensor已经进行原地操作,进行了改变,(之前的x
不存在了)不能求梯度,将y1 = (x)**2
放在z = y1**2 + y2**2
之前(with torch.no_grad():
之外),则没问题。
4.每轮训练需要梯度清零操作optimizer.zero_grad()
的原因
from torch.autograd import Variable
import torch
x = Variable(torch.ones(1, 2),requires_grad=True)
x.sum().backward()
print(x.grad) #输出:tensor([[1., 1.]])
x.sum().backward()
print(x.grad)#输出:tensor([[2., 2.]])
x.sum().backward()
print(x.grad)#输出:tensor([[3., 3.]])
从输出中可以看出,每执行一次backward()
,对应tensor的梯度都会自加1
,即梯度自动累加,所以需要每个batch_size
都执行梯度清零.zero_grad()
操作。
参考:
实践·pytorch梯度计算
with torch.no_grad() 详解
更多推荐
所有评论(0)