前言:
当前姐妹们对“loss反向传播”大致比较珍视,我们都想要分析一些“loss反向传播”的相关文章。那么小编在网上搜集了一些对于“loss反向传播””的相关内容,希望看官们能喜欢,大家快快来了解一下吧!在对一个损失进行反向传播时,在pytorch中调用out.backward()即可实现,给个小例子如:
import torchimport torch.nn as nnimport numpy as npclass net(nn.Module): def __init__(self): super().__init__() self.fc1 = nn.Linear(10,2) self.act = nn.ReLU() def forward(self,inputv): return self.act(self.fc1(inputv))n = net()opt = torch.optim.Adam(n.parameters(),lr=3e-4)inputv = torch.tensor(np.random.normal(size=(4,10))).float()output = n(inputv)target = torch.tensor(np.ones((4,2))).float()loss = nn.functional.mse_loss(output, target)loss.backward() # here we calculate the gradient w.r.t the leaf
对loss进行反向传播就可以求得:
这即是损失对于每个叶子节点的梯度。我们注意到,在.backward()这个API的文档中,有几个参数,如:
backward(gradient=None, retain_graph=None, create_graph=False)
这里我们关注的是retain_graph这个参数,这个参数如果为False或者None则在反向传播完后,就释放掉构建出来的graph,如果为True则不对graph进行释放[7][8]。
我们这里就有个问题,我们既然已经计算忘了梯度了,为什么还要保存graph呢?直接释放掉等待下一个迭代不就好了吗,不释放掉不会白白浪费内存吗?我们这里根据[7]中的讨论,简要介绍下为什么在某些情况下需要保留graph。如下图所示,我们用代码构造出此graph:
import torchfrom torch.autograd import Variablea = Variable(torch.rand(1, 4), requires_grad=True)b = a**2c = b*2d = c.mean()e = c.sum()1234567
如果我们第一次需要对末节点d进行求梯度,我们有:
d.backward()
问题是在执行完反向传播之后,因为没有显式地要求它保留graph,系统对graph内存进行释放,如果下一步需要对节点e进行求梯度,那么将会因为没有这个graph而报错。因此有例子:
d.backward(retain_graph=True) # finee.backward(retain_graph=True) # fined.backward() # also finee.backward() # error will occur!
利用这个性质在某些场景是有作用的,比如在对抗生成网络GAN中需要先对某个模块比如生成器进行训练,后对判别器进行训练,这个时候整个网络就会存在两个以上的loss,例子如:
G_loss = ...D_loss = ...opt.zero_grad() # 对所有梯度清0D_loss.backward(retain_graph=True) # 保存graph结构,后续还要用opt.step() # 更新梯度,只更新D的,因为只有D的不为0opt.zero_grad() # 对所有梯度清0G_loss.backward(retain_graph=False) # 不保存graph结构了,可以释放graph,# 下一个迭代中通过forward还可以build出来的opt.step() # 更新梯度,只更新G的,因为只有G的不为0
这个时候就可以对网络中多个loss进行分步的训练了。
标签: #loss反向传播