InplaceABN Backward Error

本文讲述了作者在修改包含InplaceABN模块的网络时遇到的运行时错误,详细解析了InplaceABN的工作原理,重点在于理解为何连续的inplace操作导致梯度计算失败。通过实际案例和GitHub issue线索,揭示了问题定位和解决方案,为类似问题的排查提供指导。

近日在对一个包含InplaceABN模块的网络进行魔改的时候,遇到了如下报错:

RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation: [torch.cuda.FloatTensor [64, 256, 7, 7]], which is output 0 of InPlaceABNBackward, is at version 3; expected version 1 instead. Hint: the backtrace further above shows the operation that failed to compute its gradient. The variable in question was changed in there or anywhere later. Good luck!

之前应用InplaceABN的时候,并没有研读过paper和代码,所以在解决这个问题的时候,花费了数小时,像无头苍蝇一样试错,虽然知道是连续的inplace操作引发的问题,但是没有定位到具体引发问题是在哪个block的哪块代码,居然一直在错误地方尝试clone()来解决。次日常看github的issue,才将问题原因真正搞清楚。

1. InplaceABN提供的block

ABN is standard BN + activation (no memory savings).
InPlaceABN is BN+activation done inplace (with memory savings).
InPlaceABNSyncis BN+activ

### 使用方法 `backward()` 方法用于在 PyTorch 中进行自动求导,计算张量的梯度。以下是几种不同场景下的使用示例: #### 标量张量的情况 当最终的张量是一个标量时,`backward()` 方法不需要传入参数。 ```python import torch # 创建一个需要梯度的张量 m = torch.tensor([1.0, 2.0, 3.0], requires_grad=True) # 对张量进行运算 n = m * m # 计算梯度 n.sum().backward() # 输出梯度 print(m.grad) ``` #### 非标量张量的情况 当最终的张量是非标量时,`backward()` 方法需要传入一个与该张量形状相同的梯度张量,用于指定链式法则中的权重。 ```python import torch x = torch.tensor([0, 1, 2, 3], dtype=torch.float32, requires_grad=True) y = torch.tensor([2.0, 2.0, 2.0, 2.0]) # 梯度张量 z = 2 * torch.dot(x, x) * y z.backward(y) # 传入梯度张量 print(x.grad) ``` ### 原理 `backward()` 方法的核心原理是基于自动微分技术中的反向传播算法。在 PyTorch 中,当创建一个需要梯度的张量(通过设置 `requires_grad=True`)并对其进行一系列操作时,PyTorch 会构建一个计算图(Computational Graph)来记录这些操作的顺序和依赖关系。 当调用 `backward()` 方法时,PyTorch 会从最终的张量开始,沿着计算图的反向路径,根据链式法则计算每个需要梯度的张量的梯度。具体来说,对于一个函数 \(y = f(x)\),如果 \(y\) 是一个标量,那么可以直接计算 \(\frac{\partial y}{\partial x}\);如果 \(y\) 是一个向量,那么需要传入一个与 \(y\) 形状相同的向量 \(v\),计算 \(\frac{\partial y}{\partial x} \cdot v\),其中 \(\cdot\) 表示向量的点积。 ### 常见问题及解决方案 #### 梯度累加问题 在默认情况下,PyTorch 会累加梯度。如果需要在每次迭代中重新计算梯度,需要在每次调用 `backward()` 之前手动清零梯度。 ```python import torch x = torch.tensor([0, 1, 2, 3], dtype=torch.float32, requires_grad=True) y = 2 * torch.dot(x, x) y.backward() print(x.grad) # 清零梯度 x.grad.zero_() y = x.sum() y.backward() print(x.grad) ``` #### 多次反向传播问题 如果需要多次调用 `backward()` 方法,需要在第一次调用时设置 `retain_graph=True`,以保留计算图。 ```python import torch import torch.nn as nn class Function(torch.nn.Module): def __init__(self): super(Function, self).__init__() self.Linear = torch.nn.Linear(1, 1) def forward(self, input): output = self.Linear(input) return output func = Function() x = torch.tensor([1.0]) x.requires_grad = True y = func(x) ** 2 y.backward(retain_graph=True) y.backward() ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

EnjoyCodingAndGame

愿我的知识,成为您的财富!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值