在训练过程中由于loss.backward() 会将计算图的隐藏变量梯度清除,从而释放空间 而在测试的时候没有这一机制,因此有可能随着测试的进行 中间变量越来越多,从而导致out of memory的发生
注:
在测试时首先要将model.eval() ,否则会影响测试时batchNorm层和Dropout层的结果 具体影响有待添加
解决方案:
regular_input = Variable(torch.randn(5, 5))
>>> volatile_input = Variable(torch.randn(5, 5), volatile=True)
>>> model = torchvision.models.resnet18(pretrained=True)
>>> model(regular_input).requires_grad
True
>>> model(volatile_input).requires_grad
False
>>> model(volatile_input).volatile
True
x = torch.tensor([1], requires_grad=True)
>>> with torch.no_grad():
... y = x * 2
>>> y.requires_grad
False
>>> @torch.no_grad()
... def doubler(x):
... return x * 2
>>> z = doubler(x)
>>> z.requires_grad
False
在torch.no_grad() 会影响pytorch的反向传播机制,在测试时因为确定不会使用到反向传播因此 这种模式可以帮助节省内存空间。
同理对于 torch.set_grad_enable(grad_mode)也是这样
x = torch.tensor([1], requires_grad=True)
>>> is_train = False
>>> with torch.set_grad_enabled(is_train):
... y = x * 2
>>> y.requires_grad
False
>>> torch.set_grad_enabled(True)
>>> y = x * 2
>>> y.requires_grad
True
>>> torch.set_grad_enabled(False)
>>> y = x * 2
>>> y.requires_grad
False
对于require_grad默认值为false,如果不做额外处理,那么所有与这些叶子节点连接的生成节点require_grad均为false,例如:
如果不增加 c=Variable((a**2).data,requires_grad=True) 那么所有的节点 a,b,c,d 均为 requires_grad=False ,但是可以通过中间层修改使得从某一中间层开始变得需要计算梯度
或者说在某一层中新增加节点 该节点的required_grad为true 那么和该节点有关的后续节点都会变为true
a= Variable(torch.tensor(np.array([1,2,3],dtype=np.float)),requires_grad=False)
b=Variable(torch.tensor(np.array([4,5,6],dtype=np.float)),requires_grad=False)
c=Variable((a**2).data,requires_grad=True)
d=c+b
d=sum(d)
d.backward()
# c.backward(torch.Tensor([1,1,1]))
print(c.grad)
features_blobs = []
def hook_feature(module, input, output):
features_blobs.append(output.data.cpu().numpy())
net._modules.get(final_conv).register_forward_hook(hook_feature)
通过register_forward_hook 得到中间结果 看看能否得到想要的结果 如果这样依然可以得到 那么 所有输入都设置为 require_grad均为false 和torch.no_grad() 是否有区别
class Autoencoder(nn.Module):
def __init__(self):
super(Autoencoder, self).__init__()
self.encoder = nn.Sequential(
_ConvLayer(3, 128),
_ConvLayer(128, 256),
_ConvLayer(256, 512),
_ConvLayer(512, 1024),
Flatten(),
nn.Linear(1024 * 4 * 4,1024),
nn.Linear(1024,1024 * 4 * 4),
Reshape(),
_UpScale(1024, 512),
)
...
# 打印出这个net的encoder部分的参数是否可以requires_grad
for param in model.encoder.parameters():
print(param.requires_grad)
这个结果显然是:
True
True
True
True
True
True
True
True
True
True
True
True
True
True
也就是说,你设计的net里面的权重参数默认都是可以进行自动梯度求导的,我们平常的loss.backward()中反向求导中的所要更新的值也就是net中的权重参数值。
class X(object):
def __init__(self, a, b, range):
self.a = a
self.b = b
self.range = range
def __call__(self, a, b):
self.a = a
self.b = b
print('__call__ with ({}, {})'.format(self.a, self.b))
def __del__(self, a, b, range):
del self.a
del self.b
del self.range
X(1,2,3)(4,5)
此时相当于执行了 __init__ 将a赋值为1,b赋值为2,range赋值为3 之后调用__call__ 将a值修改为4,b值修改为5 并输出 __call__ with (4,5)
动态图
题外话:由于0.4.0中支持0维张量(torch.tensor(3) 直接生成 tensor(3) 而0.4.0之前会生成 tensor([3]) ),可以将tensor完全看做numpy,0维向量其实对应于numpy中np.array(0),返回值为 array(0) ,对于 tensor([1,2]) 对应于array([1,2]) 无论是对于numpy还是tensor 都可以使用 item() 将0维数据返回为Python Number
题外话2:对于 loss.backward() 如果loss是一个标量 其包含一个元素此时无需对loss.backward()指定任何参数 其等价于 loss.backward(torch.FloatTensor([1.0])) Autograd
pytorch采用动态计算图,每一个节点代表一个变量,在0.4.0之后废弃了Variable,Tensor直接代替原来Variable的功能,因此Tensor现在就代表一个节点(用来保存前向传播的激活值以及反向传播的梯度),而Function。Tensor中保留了之前 Variable中required_grad 以及 grad_fn ,对于leaf Variable 和生成变量的required_grad和grad_fn是不相同的 leaf variable 的require_grad可以指定,若有一个为true 则与其相关的生成变量都为true,
with torch.no_grad():
for input,target in test_loader:
......
model.eval()