requires_grad
pytorch新建一个tensor,其默认requires_grad=False;但神经网络的权重参数(即net.parameters)默认requires_grad=True。
例子,比如x的requires_grad=True,y的requires_grad=False,而z=x+3y,则z的requires_grad也等于true,因为z由两条路径组成,其中x的那条是True。
计算图
计算图记录了各个参数之间的运算关系,根据这个计算图来计算各参数的梯度:根据参数之间的关系路径(即黑色箭头),以及相加相乘等操作(由grad_fn属性来记录),进行链式求导。
此外,pytorch的计算图是动态的,几乎每进行一次运算都会拓展原先的计算图。
loss.backward()
backward的意思是反向传播。执行backward()函数后,pytorch会根据在运算过程中(即前向传播过程中)搭建好的计算图,计算所有requires_grad=True的参数的梯度。以loss.backward()为例,通过loss.backward()进行反向传播计算时,整张计算图将对loss进行求导,所有requires_grad=True的参数都将参与梯度求导的运算,并将梯度累加到.grad属性中。
当反向传播完成,计算图默认会被清除,即进行前向传播时记录的计算过程会被释放掉,即计算图中的非叶子结点(图中的f、g、y)都会被释放掉。所以默认情况下,进行一次前向传播后最多只能用生成的计算图进行一次反向传播。可以用backward(retain_graph=True)让计算图不被立即释放,但这样容易显存溢出。
optimizer.step()
求完梯度之后,就可以用优化器optimizer对参数的值进行更新了,更新的效果依赖于梯度和学习率。
with torch.no_grad()
如果不需要进行参数更新,则要把该参数的requires_grad设成False,这样pytorch将关闭自动求导功能(上述所说的计算图、backward这个过程就是“自动求导过程”),不再追踪所有对于该参数的操作。
在with torch.no_grad()下计算出来的张量requires_grad都等于False,grad_fn属性都等于None。这意味着,所有计算操作都不会被记录在计算图中,从而减少了计算图的大小,也避免了不必要的内存消耗。
当然,也可以自己把所有无需求导的参数的requires_grad都改成False,效果跟with torch.no_grad()一样。
至于哪些参数可以避免求导,可根据计算图来看,从需要求导的参数到loss经过了哪些分支,这些分支上的所有参数都需要求导,其它分支上的参数都可以torch.no_grad。
detach()
datach的作用是将变量从计算图中剥离出来。
例子,stable diffusion的50步去噪过程,如果不加torch.no_grad,则由于网络的权重参数requires_grad都等于True,所以每经过一个step循环,计算图就不断拓展,没到50步就显存溢出了。此时除了使用torch.no_grad,或把网络所有参数requires_grad都改False,还可以对每步去噪得到的latent进行detach操作,即 latent = latent.clone().detach(),通过detach把latent从原来的计算图中剥离了,新的计算图计算的梯度就不会传到原来的计算图中,也就避免了显存溢出。