pytorch训练模型时,修改代码后报RuntimeError: Trying to backward through the graph a second time

文章讲述了在PyTorch中遇到的关于梯度反向传播的错误,重点分析了retain_graph参数的使用、no_grad上下文管理器的影响,并提供了如何通过修改代码避免内存问题的解决方案。
摘要由CSDN通过智能技术生成

一、错误问题

前两天帮忙看一个错误,是pytorch训练算法,这个错误我也是第一次发现,报错信息如下:

RuntimeError: Trying to backward through the graph a second time (or directly access saved tensors after they have already been freed). Saved intermediate values of the graph are freed when you call .backward() or autograd.grad(). Specify retain_graph=True if you need to backward through the graph a second time or if you need to access saved tensors after calling backward.

运行错误:第二次尝试向后遍历图形(或在已释放的张量后直接访问已保存的张量)。调用时,图形的已保存中间值将被释放。向后()或自动标记。梯度()。如果需要再次向后浏览图形,或者如果需要在向后调用后访问保存的张量,请指定retain_graph=True。

 错误的原因是:俗话就是,修改代码后,在使用torch训练中反向传播梯度的时候,中间的梯度值不存在。

二、方法总结

我在网上也查了相关的问题,各个博主也给了出现这个错误的理由以及要解决这个错误的方法,

总结了一下,

①:是在调用bachward()或者autograd.grad()函数时,指定retain_graph = True,意思就是把梯度值保存了;

比如下面代码:

vnet_optimizer.zero_grad()
resnet_optimizer.zero_grad()
#修改前
#v_loss.backward()
#r_loss.backward()
#修改后
v_loss.backward(retain_graph = True)
r_loss.backward(retain_graph = True)
vnet_optimizer.step()
resnet_optimizer.step()

没有效果,训练使用的torch版本是1.10的,我看里面的代码,torch的_torsor.py文件里的backward()函数确实有该参数,但是我加上了,一点用都没有,还是报一样的错误,有点奇怪了。

②:第一种方法不行,我又查了一些博主提供的方式,他们说直接在backward上retain_graph = True,其实还是在原来的梯度上操作,要先clone一下。

比如下面的代码:

vnet_optimizer.zero_grad()
resnet_optimizer.zero_grad()
#修改前
#v_loss.backward()
#r_loss.backward()
#修改后
#先对loss进行clone,然后使用detach进行分离
#说一下detach()这个方法的作用: 这个方法允许你从计算图中分离出一个Tensor,这样它就不
#再参与任何计算图,并且创建了一个新的Tensor,该新Tensor与原Tensor共享数据,但不依赖于
#过去的计算值,这个方法特别适用于需要在缓存释放、模型评估和简化计算图等场景中使用的情况。
v_loss_0=v_loss.clone().detach()
r_loss_0 = r_loss.clone().detach()
v_loss_0.requires_grad=True
r_loss_0.requires_grad=True
v_loss_0.backward()
r_loss_0.backward()
vnet_optimizer.step()
resnet_optimizer.step()

这样修改后,的确是可以解决问题了,但是一个修改后很可能造成内存不够,因为分离图后,下一个step的数据图相当于重新构建了,那内存哪能受的了,我还没有运行两三次就崩溃了,还是不行。

注释:

以上两种方法都是我自己总结的,也只是在我修改的代码中出现这个问题,仅提供参考,不同的代码会有不同的错误原因,如有不同的地方,请指点,谢谢。

三、自我查找原因

我试了上面两种方法都不行,但是错误原因都是一样的,也就是在训练的时候,某些地方代码有指定张量requires_grad为True,有些张量是未保存,所有才报这样的错误,如果不知道怎么查,可以用下面的方法试一试:

就是在单步调试的时候,先看一下loss后面有没有带有grad_fn=<****Backward0>,比如相加grad_fn=<AddBackward0>等等,如果没有这个后缀表示,那肯定前面某个地方有错,那就继续往自己前面的代码上找,但是也不能随便加一个张量的值,那样也是无用功,我只是说如果不知道怎么查,可以试一试。

我抱着上面的错误想法,一直单步往前面的代码上看,发现修改的代码有一块有意思,如下:

with torch.no_grad():
   batch_size = img_a.shape[0]
   v_outputs = model_vnet(torch.cat([unimg_a, img_a], dim=0))
   v_outputs_u,v_outputs_l =v_outputs[:batch_size], v_outputs[batch_size:batch_size * 2
   r_outputs = model_resnet(torch.cat([unimg_a, img_a], dim=0))  
   r_outputs_u, r_outputs_l = r_outputs[:batch_size], r_outputs[batch_size:batch_size * 2]
   plab_v = get_cut_mask(v_outputs_u, nms=1)
   plab_r = get_cut_mask(r_outputs_u, nms=1)
   img_mask, loss_mask = context_mask(img_a, args.mask_ratio)

v_loss_seg = F.cross_entropy(v_outputs_l, lab_a)
v_outputs_soft = F.softmax(v_outputs_l, dim=1)
v_loss_seg_dice = losses.dice_loss(v_outputs_soft[:labeled_bs, 1, :, :, :], lab_a == 1)

有意思的就是,在训练的时候使用了torch.no_grad(),这个意思就是:下面的这些代码段不会计算微分,也就是不参与后面的梯度计算,但是在后面又参与了loss的运算,那肯定是有问题的(上面的v_outputs_u,v_outputs_l,只显示了部分代码)。OK,找到错误的原因了,那修改就简单了,可以把with torch.no_grad()去掉,也可以把需要进行计算的拿出来,我就采用第一个,修改后如下:

batch_size = img_a.shape[0]
v_outputs = model_vnet(torch.cat([unimg_a, img_a], dim=0))
v_outputs_u,v_outputs_l =v_outputs[:batch_size], v_outputs[batch_size:batch_size * 2
r_outputs = model_resnet(torch.cat([unimg_a, img_a], dim=0))  
r_outputs_u, r_outputs_l = r_outputs[:batch_size], r_outputs[batch_size:batch_size * 2]
plab_v = get_cut_mask(v_outputs_u, nms=1)
plab_r = get_cut_mask(r_outputs_u, nms=1)
img_mask, loss_mask = context_mask(img_a, args.mask_ratio)
v_loss_seg = F.cross_entropy(v_outputs_l, lab_a)
v_outputs_soft = F.softmax(v_outputs_l, dim=1)
v_loss_seg_dice = losses.dice_loss(v_outputs_soft[:labeled_bs, 1, :, :, :], lab_a == 1)

虽然解决了问题,整个代码也运行成功了,但是这样做有个坏处就是可能把测试集也会学习了一片,所以最好根据自己代码使用的部分进行修改吧。

以上就是提供一下参考,有不对的地方,麻烦指点,谢谢。

  • 16
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值