问题

在用pytorch生成对抗网络的时候,出现错误Runtime Error: one of the variables needed for gradient computation has been modified by an inplace operation,特记录排坑记录。

环境

windows10 2004

python 3.7.4

pytorch 1.7.0 + cpu

解决过程

  • 尝试一

这段错误代码看上去不难理解,意思为:计算梯度所需的某变量已被一就地操作修改。什么是就地操作呢,举个例子如x += 1就是典型的就地操作,可将其改为y = x + 1。但很遗憾,这样并没有解决我的问题,这种方法的介绍如下。

在网上搜了很多相关博客,大多原因如下:

由于0.4.0把Varible和Tensor融合为一个Tensor,inplace操作,之前对Varible能用,但现在对Tensor,就会出错了。

所以解决方案很简单:将所有inplace操作转换为非inplace操作。如将x += 1换为y = x + 1

仍然有一个问题,即如何找到inplace操作,这里提供一个小trick:分阶段调用y.backward(),若报错,则说明这之前有问题;反之则说明错误在该行之后。

  • 尝试二

在我的代码里根本就没有找到任何inplace操作,因此上面这种方法行不通。自己盯着代码,debug,啥也看不出来,好久......

忽然有了新idea。我的训练阶段的代码如下:

for epoch in range(1, epochs + 1):
for idx, (lr, hr) in enumerate(traindata_loader):
lrs = lr.to(device)
hrs = hr.to(device) # update the discriminator
netD.zero_grad()
logits_fake = netD(netG(lrs).detach())
logits_real = netD(hrs)
# Label smoothing
real = (torch.rand(logits_real.size()) * 0.25 + 0.85).clone().detach().to(device)
fake = (torch.rand(logits_fake.size()) * 0.15).clone().detach().to(device)
d_loss = bce(logits_real, real) + bce(logits_fake, fake)
d_loss.backward(retain_graph=True)
optimizerD.step() # update the generator
netG.zero_grad()
# !!!问题出错行
g_loss = contentLoss(netG(lrs), hrs) + adversarialLoss(logits_fake)
g_loss.backward()
optimizerG.step()

判别器loss的backward是正常的,生成器loss的backward有问题。观察到g_loss由两项组成,所以很自然的想法就是删掉其中一项看是否正常。结果为:只保留第一项程序正常运行;g_loss中包含第二项程序就出错。

因此去看了adversarialLoss的代码:

class AdversarialLoss(nn.Module):
def __init__(self):
super(AdversarialLoss, self).__init__()
self.bec_loss = nn.BCELoss() def forward(self, logits_fake):
# Adversarial Loss
# !!! 问题在这,logits_fake加上detach后就可以正常运行
adversarial_loss = self.bec_loss(logits_fake, torch.ones_like(logits_fake))
return 0.001 * adversarial_loss

看不出来任何问题,只能挨个试。这里只有两个变量:logits_faketorch.ones_like(logits_fake)。后者为常量,所以试着固定logits_fake,不让其参与训练,程序竟能运行了!

class AdversarialLoss(nn.Module):
def __init__(self):
super(AdversarialLoss, self).__init__()
self.bec_loss = nn.BCELoss() def forward(self, logits_fake):
# Adversarial Loss
# !!! 问题在这,logits_fake加上detach后就可以正常运行
adversarial_loss = self.bec_loss(logits_fake.detach(), torch.ones_like(logits_fake))
return 0.001 * adversarial_loss

由此知道了被修改的变量是logits_fake。尽管程序可以运行了,但这样做不一定合理。类AdversarialLoss中没有对logits_fake进行修改,所以返回刚才的训练程序中。

for epoch in range(1, epochs + 1):
for idx, (lr, hr) in enumerate(traindata_loader):
lrs = lr.to(device)
hrs = hr.to(device) # update the discriminator
netD.zero_grad()
logits_fake = netD(netG(lrs).detach())
logits_real = netD(hrs)
# Label smoothing
real = (torch.rand(logits_real.size()) * 0.25 + 0.85).clone().detach().to(device)
fake = (torch.rand(logits_fake.size()) * 0.15).clone().detach().to(device)
d_loss = bce(logits_real, real) + bce(logits_fake, fake)
d_loss.backward(retain_graph=True)
# 这里进行的更新操作
optimizerD.step() # update the generator
netG.zero_grad()
# !!!问题出错行
g_loss = contentLoss(netG(lrs), hrs) + adversarialLoss(logits_fake)
g_loss.backward()
optimizerG.step()

注意到Discriminator在出错行之前进行了更新操作,因此真相呼之欲出————optimizerD.step()logits_fake进行了修改。直接将其挪到倒数第二行即可,修改后代码为:

for epoch in range(1, epochs + 1):
for idx, (lr, hr) in enumerate(traindata_loader):
lrs = lr.to(device)
hrs = hr.to(device) # update the discriminator
netD.zero_grad()
logits_fake = netD(netG(lrs).detach())
logits_real = netD(hrs)
# Label smoothing
real = (torch.rand(logits_real.size()) * 0.25 + 0.85).clone().detach().to(device)
fake = (torch.rand(logits_fake.size()) * 0.15).clone().detach().to(device)
d_loss = bce(logits_real, real) + bce(logits_fake, fake)
d_loss.backward(retain_graph=True) # update the generator
netG.zero_grad()
g_loss = contentLoss(netG(lrs), hrs) + adversarialLoss(logits_fake)
g_loss.backward()
optimizerD.step()
optimizerG.step()

程序终于正常运行了,耶( •̀ ω •́ )y!

总结

原因:在计算生成器网络梯度之前先对判别器进行更新,修改了某些值,导致Generator网络的梯度计算失败。

解决方法:将Discriminator的更新步骤放到Generator的梯度计算步骤后面。

RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation的更多相关文章

  1. RuntimeError: one of the variables needed for gradient computation has been modified by an inplace

    vgg里面的 ReLU默认的参数inplace=True 当我们调用vgg结构的时候注意 要将inplace改成 False 不然会报错 RuntimeError: one of the variab ...

  2. one of the variables needed for gradient computation has been modified by an inplace operation: [torch.cuda.FloatTensor [3, 1280, 28, 28]], which is output 0 of LeakyReluBackward1, is at version 2;

    RuntimeError: one of the variables needed for gradient computation has been modified by an inplace o ...

  3. TensorFlow 学习(八)—— 梯度计算(gradient computation)

    maxpooling 的 max 函数关于某变量的偏导也是分段的,关于它就是 1,不关于它就是 0: BP 是反向传播求关于参数的偏导,SGD 则是梯度更新,是优化算法: 1. 一个实例 relu = ...

  4. pytorch .detach() .detach_() 和 .data用于切断反向传播

    参考:https://pytorch-cn.readthedocs.io/zh/latest/package_references/torch-autograd/#detachsource 当我们再训 ...

  5. PyTorch学习笔记及问题处理

    1.torch.nn.state_dict(): 返回一个字典,保存着module的所有状态(state). parameters和persistent_buffers都会包含在字典中,字典的key就 ...

  6. pytorch的自动求导机制 - 计算图的建立

    一.计算图简介 在pytorch的官网上,可以看到一个简单的计算图示意图, 如下. import torchfrom torch.autograd import Variable x = Variab ...

  7. [源码解析]PyTorch如何实现前向传播(2) --- 基础类(下)

    [源码解析]PyTorch如何实现前向传播(2) --- 基础类(下) 目录 [源码解析]PyTorch如何实现前向传播(2) --- 基础类(下) 0x00 摘要 0x01 前文回顾 0x02 Te ...

  8. Coursera Deep Learning 2 Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization - week1, Assignment(Gradient Checking)

    声明:所有内容来自coursera,作为个人学习笔记记录在这里. Gradient Checking Welcome to the final assignment for this week! In ...

  9. 课程二(Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization),第一周(Practical aspects of Deep Learning) —— 4.Programming assignments:Gradient Checking

    Gradient Checking Welcome to this week's third programming assignment! You will be implementing grad ...

随机推荐

  1. Vue.js 学习笔记之二:数据驱动开发

    在 Vue.js 框架中,与 HTML 页面元素的交互方式没有像原生 JavaScript 接口那么直接,它是通过先在 HTML 元素标签中嵌入一系列类似于普通标签属性的 Vue 指令属性来绑定数据, ...

  2. Python self用法详解

    在定义类的过程中,无论是显式创建类的构造方法,还是向类中添加实例方法,都要求将 self 参数作为方法的第一个参数.例如,定义一个 Person 类: class Person: def __init ...

  3. 多线程之ReentrantLock篇(五)

    昨天有说过后面讲ReentrantLock,今天我们这篇幅就全局的讲解下,我们在Lock出来前,解决并发问题没得选只能用Synchronized. 一.ReentrantLock PK synchro ...

  4. 树形DP 学习笔记

    树形DP学习笔记 ps: 本文内容与蓝书一致 树的重心 概念: 一颗树中的一个节点其最大子树的节点树最小 解法:对与每个节点求他儿子的\(size\) ,上方子树的节点个数为\(n-size_u\) ...

  5. 《C++primerplus》第12章“队列模拟”程序

    这个程序刚开始学有很多难点,个人认为主要有以下三项: 1.链表的概念 2.如何表示顾客随机到达的过程 3.程序执行时两类之间的关系,即执行逻辑 关于第一点,书上的图解释得比较清楚了,把"空指 ...

  6. P4568 [JLOI2011]飞行路线 / P2939 [USACO09FEB]Revamping Trails G

    题目描述 Link Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司.该航空公司一共在 \(n\) 个城市设有业务,设这些城市分别标记为 \(0\) 到 \(n-1\),一共 ...

  7. P2590 树的统计

    一道树剖的模板题 首先,由于本人比较懒,就把单点修改写成了区间修改,其实也没有有多大区别(关键是我不会写单点修改QAQ) 不得不说,树剖的码量比较大,调了一上午才勉强调完. 这道题要求我们支持 单点修 ...

  8. 【4】进大厂必须掌握的面试题-Java面试-jdbc

    1.什么是JDBC驱动程序? JDBC驱动程序是使Java应用程序与数据库进行交互的软件组件.JDBC驱动程序有4种类型: JDBC-ODBC桥驱动程序 本机API驱动程序(部分为Java驱动程序) ...

  9. 风车签名 - 让管理APP变成一件简单的事儿

    这是一款在Mac平台下安全可控的iOS签名管理软件,旨在对签名后的APP能够完全控制,包括APP的开启或禁用.设置到期时间锁.注入第三方动态库文件.设置安装限量.修改APP名称和自定义Bundle I ...

  10. Java死锁编码及定位分析的demo

    死锁 死锁是什么 大学课程中的四个要素: (1)互斥(2)不可抢占(3)循环等待(4)请求保持 也就是下图所描述 产生死锁的主要原因 (1)系统资源不足(2)进程运行推进的顺序不合适(3)资源分配不当 ...