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

本文探讨了PyTorch中因变量被就地修改导致的梯度计算错误,详细解析了问题根源,并提供了使用clone()方法的解决方案,避免了变量在计算过程中的直接修改,确保了梯度计算的正确性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

梯度运算所需的变量之一已被就地操作修改

变量分配是就地操作,在网络的实现过程中,存在上下文使用某个变量,某些功能要求变量不能更改。因此可以采用clone操作进行避免

prob_g=score[:,:vocab_size].clone()#1,vocab_size
prob_c=score[:,vocab_size:].clone()#1,seq_size

问题代码

	def forward():
        score=F.softmax(torch.cat((score_g,score_c),dim=1),dim=1)
        prob_g=score[:,:vocab_size]#1,vocab_size
        prob_c=score[:,vocab_size:]#1,seq_size
        
        
        if self.max_vocab_size>vocab_size:
            padding=torch.zeros(1,self.max_vocab_size-vocab_size)
            output1=torch.cat([prob_g,padding],1)
        else:
            output1=prob_g
        #max_vocab_size的含义不是十分确定,具体代表的是啥,以及为啥要进行补充
        
        
        seq_size=input_seq.shape[0]#input_seq在维度0上的size
        one_hot=torch.zeros(pre_prob.size(0),max_vocab_size).scatter_(1,input_seq.view(-1,1),1)#根据input_seq中的索引,按照维度1的方向对torch.zero进行填充1
        #将one_hot的seq_size改为max_length(pre_prob.size(0))
        output1 +=torch.mm(prob_c,one_hot)
        
        output1[output1==0]=1e-9
        output1[0][2]=1e-9
        
        output1=output1.log()
        
        return output1,hidden,attn_weights,prob_c
    for di in range(target_length):
        decoder_output,decoder_hidden,decoder_attention,prob_c=decoder(
            decoder_input,
            decoder_hidden,
            encoder_outputs,
            input_tensor_clone,
            pre_prob
        )
        pre_prob=prob_c.squeeze().long().detach()#求pre_prob中的梯度,不求prob_c中参数的梯度,不参与更新
        
        topv,topi=decoder_output.topk(1)
        decoder_input=topi.squeeze().detach()#去除topi的梯度更新,代表一个词
        output_rec.append(topi.item())#直接索引就代表一个单词,直接进行拼接
        loss=loss+criterion(decoder_output,target_tensor[di].view(1))

在decoder 的forward中返回值output1是由prob_g直接赋值,之后在loss的计算中使用了output1,由此出现了变量被就地修改的情况,使用clone()可以解决这个问题
修改后的代码如下:

	def forward():
        score_g=self.Wo(hidden).view(1,-1)#1,vocab_size
        
        print('%%%%')
        score_c=self.Wc(encoder_outputs)# seq_size,hidden_size
        score_c=torch.tanh(score_c)#score_c相当于hj*Wc,取非线性函数,得到的值
        score_c=torch.mm(score_c,hidden.view(-1,1)).view(1,-1)#hidden相当于st
        
        print('!!!!')
        score=F.softmax(torch.cat((score_g,score_c),dim=1),dim=1)
        prob_g=score[:,:vocab_size].clone()#1,vocab_size
        prob_c=score[:,vocab_size:].clone()#1,seq_size
        
        
        if self.max_vocab_size>vocab_size:
            padding=torch.zeros(1,self.max_vocab_size-vocab_size)
            output1=torch.cat([prob_g,padding],1)
        else:
            output1=prob_g
        #max_vocab_size的含义不是十分确定,具体代表的是啥,以及为啥要进行补充
        
        
        seq_size=input_seq.shape[0]#input_seq在维度0上的size
        one_hot=torch.zeros(pre_prob.size(0),max_vocab_size).scatter_(1,input_seq.view(-1,1),1)#根据input_seq中的索引,按照维度1的方向对torch.zero进行填充1
        #将one_hot的seq_size改为max_length(pre_prob.size(0))
        output1 +=torch.mm(prob_c,one_hot)
        
        output1[output1==0]=1e-9
        output1[0][2]=1e-9
        
        output1=output1.log()
        
        return output1,hidden,attn_weights,prob_c

原因

python的=赋值大概可以看做是将变量指向一个内存地址,在修改时,会直接修改指向的内存的数据。所以还会出现上述的情况
一个讨论上看到的讨论一个论坛上看到的讨论

测试代码

import numpy as np
a=np.arange(9).reshape((3,3))
print(a)
b=a[1:3,1:3]
print(b)
c=b
c[0][0]=1
print(c)
print(b)
print(a)

结果

在这里插入图片描述

相关链接

参考链接

### 解决 PyTorch 中由原地操作引发的 RuntimeError 当遇到 `RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation` 错误时,这通常意味着某些张量在反向传播过程中被修改了其数据,而这些修改发生在计算图中的节点之后。这种情况下,PyTorch 无法正确追踪梯度的变化。 为了处理这种情况,可以采取以下几个措施: #### 启用异常检测 启用自动求导模块中的异常检测功能可以帮助定位具体在哪一步发生了问题。通过设置 `torch.autograd.set_detect_anomaly(True)` 可以让程序在每次前向传递后检查是否存在不合法的操作,并抛出更详细的错误信息以便调试[^1]。 ```python import torch # 开启anomaly detection模式来帮助查找问题所在 torch.autograd.set_detect_anomaly(True) # 继续执行训练循环... ``` #### 避免使用原地运算符 许多 PyTorch 的函数都有对应的原地版本(带有下划线 `_`),比如 `.add_()` 或者 `.relu_()`. 这些方法会直接改变输入张量的内容而不是创建新的对象返回。为了避免破坏计算图结构,在构建模型或者编写自定义层时应尽量避免使用这类原地操作[^2]。 例如,如果原本有如下代码片段: ```python output = F.relu(output, inplace=True) ``` 应该改为非inplace的形式: ```python output = F.relu(output) ``` #### 使用 detach 方法分离不需要跟踪的历史记录 对于那些确实需要做原地更新但是又不想影响到整个计算图的情况,可以通过调用 `.detach()` 来切断当前张量与其历史之间的联系,从而允许对其进行安全的原地更改而不干扰后续的梯度计算过程[^3]。 ```python detached_output = output.detach() detached_output.add_(some_value) # 对 detached_output 执行原地加法不会影响原始 tensor 的 history ``` #### 修改网络架构设计 有时,特定类型的神经元激活函数可能会更容易触发此类错误,特别是像 ReLU 和 Tanh 这样的饱和型激活函数。考虑调整使用的激活函数种类或是重新审视整体网络的设计逻辑是否合理[^4]。 综上所述,针对此 Runtime Error 主要策略包括开启 anomaly detection 辅助排查、禁用所有可能引起冲突的原地操作以及适当运用 .detach() 技巧等手段相结合的方式来进行修复工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值