1. detach
  2. 对于训练数据训练过程中不会出现out of memory的情况 但是在测试的时候随着测试的进行 GPU的memory会出现越来越多的占用 最终出现out of memory的情况
  3. torch.no_grad

对于测试时out of memory问题

在训练过程中由于loss.backward() 会将计算图的隐藏变量梯度清除,从而释放空间 而在测试的时候没有这一机制,因此有可能随着测试的进行 中间变量越来越多,从而导致out of memory的发生
注:
在测试时首先要将model.eval() ,否则会影响测试时batchNorm层和Dropout层的结果 具体影响有待添加
解决方案:

  1. 在pytorch 0.4.0之前 input= Variable(input, volatile=True) 设置volatile为True ,只要是一个输入为volatile,则输出也是volatile的,它能够保证不存在中间状态
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
  1. 但是在pytorch 0.4.0之后取消了volatile的机制 被替换成torch.no_grad(), torch.set_grad_enable(grad_mode)等函数
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
  1. 这里有一点疑问就是对于输入网络结构的数据 最开始的 require_grad 是否是True 如果全部设置为require_grad 为false 是否还会存储中间的各种变量 这个可以通过hock实验一下

对于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() 是否有区别

  1. 对于CNN而言
    Pytorch中autograd
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中的权重参数值。
自定义Function
  1. call 在python中类的实例可以当做函数对待 ,可以将他们作为输入传递到其他函数中并执行,例如:
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)
  1. 对于torch.autograd.Function 想要添加新的Function 需要继承torch.autograd.Function 并重写 init forward 和backward 函数
pytorch 动态连接图

动态图

题外话:由于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,

测试时pytroch 处理
with torch.no_grad():
 	for input,target in test_loader:
 		......
model.eval()