今天复现论文“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