实验背景:
使用pytorch训练模型过程中,我们可以通过修改模型结构尝试改进模型效果。
修改模型后,如添加了特殊的操作(op)或特殊的层结构,再训练模型时经常会遇到错误信息如下:
RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation: [torch.cuda.FloatTensor [64, 64]], which is output 0 of ExpBackward, is at version 44; expected version 0 instead. Hint: enable anomaly detection to find the operation that failed to compute its gradient, with torch.autograd.set_detect_anomaly(True).
我的情况是在添加了指数计算操作exp()后,出现报错提示该op出错(is output 0 of ExpBackward),并提示该操作的梯度被其他op修改(inplace),建议设置with torch.autograd.set_detect_anomaly(True)。
解决方案:
1. 修改op的参数 inplace=False
在搜索引擎上找到很多类似的问题,解法根据提示信息,将指定的op的inplace参数设置为False。示例代码如下:
exp = torch.exp(inplace=False)
这个解法我尝试之后没有解决问题,所以找到第2个解法。
2. detach() 分离变量
这个思路和inplace参数类似,将要求导的变量在操作op和op之间分离。示例代码如下:
# before
a = torch.mm(x1, x2)
a = torch.exp(a)
# after
a = torch.mm(x1, x2)
a = torch.exp(a.detach())
其中变量a是x1和x2的乘积结果,然后对它进行指数操作,在对exp传参变量a进行分离后,得到一个复制变量对其进行指数操作。
问题解决~
为什么detach()分离操作可以解决这个问题?下面具体看下detach()的定义:
在pytorch的官方文档里找到说明:torch.Tensor.detach
摘抄部分说明如下:
Returns a new Tensor, detached from the current graph.
The result will never require gradient.
说明detach这个操作会返回一个新的Tensor,并且该tensor不要求任何梯度的计算。所以呼应报错信息,detach确实可以解决梯度已被更新的冲突。
但我们要思考该操作是否会对整体模型的精度造成影响?
在我的case里,之所以会出现这个问题是我重复使用同一个变量a,同时记录了矩阵乘mm操作的输出,以及exp操作的输出。这是导致出现梯度冲突的根本原因。
那么我们将a tensor做了分离处理后,相当于设置了一个临时变量转存了a的值,然后再往下计算。在计算梯度的过程中不计算临时变量的梯度,但对于exp和mm操作的输出照常计算。因而该操作在当前讨论的情况下不会导致精度受损。
欢迎大家多多指正讨论!