PyTorch的Tensor与Variable的区别, detach的作用, with no_grad()

今天复现论文“3D Hand Shape and Pose from Images in the Wild”,写代码用到的一些关于PyTorch的基础知识。

1. cuda()与cpu()

用法: tensorA.cuda() # tensorA 是一个tensor类型的变量
作用:把tensorA从CPU移动到GPU,方便后续在GPU中计算

用法: modelA.cuda() # 把modelA是一个神经网络(nn.Module)
作用:Moves all model parameters and buffers to the GPU, 便于后续在GPU中训练模型

cpu()的作用相反,从GPU移到CPU

2.Tensor与Variable

Tensor只是一个类似Numpy array`的数据格式,它可以进行多种运行,但无法构建计算图(也就无法方向传播计算梯度)

Variable作为Tensor的封装,对Variable使用.backward()方法,可以得到该Variable之前计算图所有Variable的梯度。

在创建一个Variable时,有两个bool型参数可供选择,一个是requires_grad(默认为False),一个是Volatile。

requires_grad=False不对该Var进行计算梯度(自然也不会计算前面的节点的梯度),一般在finetune是可以用来固定某些层的参数,减少计算。只要有一个叶节点是True,其后续的节点都是True,当所有子节点都为False, 后续节点才为False。

variable的volatile属性默认为False,如果某一个variable的volatile属性被设为True,那么所有依赖它的节点volatile属性都为True。volatile属性为True的节点不会求导,volatile的优先级比requires_grad高。

注:
1.该属性已经在0.4版本中被移除了,并提示你可以使用with torch.no_grad()代替该功能
2.volatile为
False不一定不求导

3.volatile为True时是所有的后继结点不求导; 而req_grad为True时是所有后继结点求导。

3.detach和_detach

detach returns a new Variable whose req_grad = False
注意,detach是返回了一个新的Variable,原先的Variable还在计算图里,因此还能对原先的Variable方向传播求梯度。
作用:新的Variable可以作为新的计算图的叶子结点,不用求前面节点的梯度。

_detach(这一部分直接copy的,原文)
官网给的解释是:将 Variable 从创建它的 graph 中分离,把它作为叶子节点。

从源码中也可以看出这一点

将 Variable 的grad_fn 设置为 None,这样,BP 的时候,到这个 Variable 就找不到 它的 grad_fn,所以就不会再往后BP了。
将 requires_grad 设置为 False。这个感觉大可不必,但是既然源码中这么写了,如果有需要梯度的话可以再手动 将 requires_grad 设置为 true

# detach_ 的源码
def detach_(self):
    """Detaches the Variable from the graph that created it, making it a
    leaf.
    """
    self._grad_fn = None
    self.requires_grad = False

4. detach在GAN中的作用

在判别器时输入为:torch.cat((images, outputs.detach()), dim=1)

#output是生成器的输出

以下几点需要注意:

  • 如果没有 outputs.detach(),虽然会回传到生成器梯度,但是优化器分开进行,其实不会出错(每次对哪些参数进行优化与反向传播无关,方向传播只负责求出梯度并缓存;优化器根据缓存的梯度对参数优化)。但是outputs.detach()可以加快速度,因为不需要反传所有的梯度。

  • gen_fake, gen_fake_feat = self.discriminator(gen_input_fake),训练生成器时没有detach(),因为阻断了梯度回传,不能回传梯度到生成器,这样就训练不了。但是优化范围有opt_gen指定,并不会优化D的参数。

5. torch.cuda.synchronize()

在torch中,GPU和CPU是异步的,很可能GPU还在训练,CPU的进程已经结束了,因此要正确的统计训练时间要用synchronized()同步GPU和CPU。在CPU进程中调用sunchronize()后,CPU会等待GPU进程执行完毕再继续执行接下来的代码。

6.with no_grad() 此部分转载

volatile=True的优先级高于requires_grad,即当volatile = True时,无论requires_grad是Ture还是False,反向传播时都不会自动求导。在测试阶段,volatile可以实现一定速度的提升,并节省一半的显存,因为其不需要保存梯度。(volatile默认为False,这时反向传播是否自动求导,取决于requires_grad)

with torch.no_grad
上文提到volatile已经被废弃,替代其功能的就是with torch.no_grad。作用与volatile相似,即使一个tensor(命名为x)的requires_grad = True,由x得到的新tensor(命名为w-标量)requires_grad也为False,且grad_fn也为None,即不会对w求导。

7. to_device(non_blocking=True)

首先辨明同步/异步和单线程/多线程的概念。

同步/异步强调的是阻塞与否。关键在于进程在执行请求时,是等待请求返回结果还是直接执行下面的操作。
异步不等于多进程。比如js语言是单线程异步,始终只有一个js线程在执行,但是js线程触发一个http请求时无需等到请求结果返回,就可以去继续只需下面的任务。
异步可以采用多线程技术或则交给另外的进程来处理。

附详细链接: https://blog.csdn.net/bryant_liu24/article/details/55506029

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值