- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我有一个特殊用例,我必须将推理和反向传播分开:我必须推理所有图像和切片输出分成批处理,然后逐批反向传播。我不需要更新我的网络权重。
我修改了 cifar10_tutorial 的片段进入以下模拟我的问题:j
是一个变量来表示由我自己的逻辑返回的索引,我想要一些变量的梯度。
for epoch in range(2): # loop over the dataset multiple times
for i, data in enumerate(trainloader, 0):
# get the inputs
inputs, labels = data
inputs.requires_grad = True
# zero the parameter gradients
optimizer.zero_grad()
# forward + backward + optimize
outputs = net(inputs)
for j in range(4): # j is given by external logic in my own case
loss = criterion(outputs[j, :].unsqueeze(0), labels[j].unsqueeze(0))
loss.backward()
print(inputs.grad.data[j, :]) # what I really want
我收到以下错误:
RuntimeError: Trying to backward through the graph a second time, but the buffers have already been freed. Specify retain_graph=True when calling backward the first time.
我的问题是:
根据我的理解,问题的出现是因为第一个反向传播整个outputs
和outputs[1,:].unsqueeze(0)
已发布,因此第二次反向传播失败。我说得对吗?
在我的例子中,如果我设置 retain_graph=True
,代码会根据这个 post 运行得越来越慢吗? ?
有没有更好的方法来实现我的目标?
最佳答案
是的,你是对的。当您已经第一次(第一次迭代)通过输出
反向传播时,缓冲区将被释放,并且它将在接下来的时间(循环的下一次迭代)失败,因为此计算所需的数据已被删除。
是的,图形变得越来越大,因此它可能会变慢,具体取决于 GPU (或 CPU) 使用情况和您的网络。我曾经用过一次,但速度要慢得多,但这在很大程度上取决于您的网络架构。但是使用 retain_graph=True
肯定会比不使用时需要更多的内存。
根据您的outputs
和labels
形状,您应该能够计算所有outputs
和 的损失一次标签
:
criterion(outputs, labels)
你必须跳过 j
-loop 然后,这也会使你的代码更快。也许您需要 reshape (resp.view
)您的数据,但这应该可以正常工作。
如果您出于某种原因不能这样做,您可以手动计算张量的损失并在循环后调用 backward
。这应该也能正常工作,但比上面的解决方案慢。
所以你的代码看起来像这样:
# init loss tensor
loss = torch.tensor(0.0) # move to GPU if you're using one
for j in range(4):
# summing up your loss for every j
loss += criterion(outputs[j, :].unsqueeze(0), labels[j].unsqueeze(0))
# ...
# calling backward on the summed loss - getting gradients
loss.backward()
# as you call backward now only once on the outputs
# you shouldn't get any error and you don't have to use retain_graph=True
编辑:
损失的累积和后面调用是完全等价的,这里有一个有和没有累积损失的小例子:
首先创建一些数据data
:
# w in this case will represent a very simple model
# I leave out the CE and just use w to map the output to a scalar value
w = torch.nn.Linear(4, 1)
data = [torch.rand(1, 4) for j in range(4)]
data
看起来像:
[tensor([[0.4593, 0.3410, 0.1009, 0.9787]]),
tensor([[0.1128, 0.0678, 0.9341, 0.3584]]),
tensor([[0.7076, 0.9282, 0.0573, 0.6657]]),
tensor([[0.0960, 0.1055, 0.6877, 0.0406]])]
让我们首先像您那样做,分别为每次迭代 j
调用反向调用:
# code for directly applying backward
# zero the weights layer w
w.zero_grad()
for j, inp in enumerate(data):
# activate grad flag
inp.requires_grad = True
# remove / zero previous gradients for inputs
inp.grad = None
# apply model (only consists of one layer in our case)
loss = w(inp)
# calling backward on every output separately
loss.backward()
# print out grad
print('Input:', inp)
print('Grad:', inp.grad)
print()
print('w.weight.grad:', w.weight.grad)
这是每个输入的打印输出以及模型的相应梯度和梯度。 w
层在我们的简化案例中:
Input: tensor([[0.4593, 0.3410, 0.1009, 0.9787]], requires_grad=True)
Grad: tensor([[-0.0999, 0.2665, -0.1506, 0.4214]])
Input: tensor([[0.1128, 0.0678, 0.9341, 0.3584]], requires_grad=True)
Grad: tensor([[-0.0999, 0.2665, -0.1506, 0.4214]])
Input: tensor([[0.7076, 0.9282, 0.0573, 0.6657]], requires_grad=True)
Grad: tensor([[-0.0999, 0.2665, -0.1506, 0.4214]])
Input: tensor([[0.0960, 0.1055, 0.6877, 0.0406]], requires_grad=True)
Grad: tensor([[-0.0999, 0.2665, -0.1506, 0.4214]])
w.weight.grad: tensor([[1.3757, 1.4424, 1.7801, 2.0434]])
现在,我们不再为每次迭代 j 调用一次 backward,而是累加这些值并在总和上调用 backward
并比较结果:
# init tensor for accumulation
loss = torch.tensor(0.0)
# zero layer gradients
w.zero_grad()
for j, inp in enumerate(data):
# activate grad flag
inp.requires_grad = True
# remove / zero previous gradients for inputs
inp.grad = None
# apply model (only consists of one layer in our case)
# accumulating values instead of calling backward
loss += w(inp).squeeze()
# calling backward on the sum
loss.backward()
# printing out gradients
for j, inp in enumerate(data):
print('Input:', inp)
print('Grad:', inp.grad)
print()
print('w.grad:', w.weight.grad)
让我们看看结果:
Input: tensor([[0.4593, 0.3410, 0.1009, 0.9787]], requires_grad=True)
Grad: tensor([[-0.0999, 0.2665, -0.1506, 0.4214]])
Input: tensor([[0.1128, 0.0678, 0.9341, 0.3584]], requires_grad=True)
Grad: tensor([[-0.0999, 0.2665, -0.1506, 0.4214]])
Input: tensor([[0.7076, 0.9282, 0.0573, 0.6657]], requires_grad=True)
Grad: tensor([[-0.0999, 0.2665, -0.1506, 0.4214]])
Input: tensor([[0.0960, 0.1055, 0.6877, 0.0406]], requires_grad=True)
Grad: tensor([[-0.0999, 0.2665, -0.1506, 0.4214]])
w.grad: tensor([[1.3757, 1.4424, 1.7801, 2.0434]])
当比较结果时,我们可以看到两者是相同的。
这是一个非常简单的例子,但是我们可以看到对每个张量调用backward()
并对张量求和然后调用backward()
是就输入和权重的结果梯度而言是等效的。
当您按照3. 中的描述同时对所有j 使用CE 时,您可以使用标志reduction= 'sum'
将与上述相同的行为归档为对 CE 值求和,默认为 'mean',这可能会导致略有不同的结果。
关于python - Pytorch - 推断所有图像并逐批反向传播,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53843711/
如果有人能解释这个注释的作用以及我们何时使用它: @Transactional(propagation=Propagation.REQUIRED) 谢谢 最佳答案 如果您需要在 Spring Docs
我有一个页面,它有一个 keydown 事件监听器,用于监听 Escape 键,以便返回。我还有一个简单的模态类,它也监听 Escape 键以关闭它。主页监听器检查模式是否打开,如果打开,则不执行任何
我想在模型中设置默认变量名称 T (=xx) - 将该模型拖到新模型中并在其中定义变量 xx。我收到错误消息:使用未声明的变量 xx。 这是子模型 model test parameter Rea
在 android 2.x 浏览器中查看此示例..它是在我的应用程序中复制场景的示例.. http://johnchacko.net/samples/tap.html 它是关于监听“tap”并从监听器
如您所见,我正在尝试将 GatewayConnectionFailedException 传播到我的 UI。我希望此代码捕获除异常之外的所有内容,我希望表示层捕获该异常以通知用户数据库是问题所在,以便
我目前正在尝试让可执行文件与它需要的所有依赖项正确链接。 这是依赖项的示例结构: exe -> libA -> libB exe和 libA有自己的存储库。 exe拉入libA像这样的东西: add_
有什么方法可以调用带有单个参数的 Scala 函数,给定一个数组 (类似于 JavaScript Spreads在 ECMAScript 6) 中? ys = [10.0, 2.72, -3.14]
我有一个小型静态库,它需要 boost 头文件,并且需要包含目录中的“include”目录。 ... add_library(alib STATIC ...) target_include_direc
我有一些 promise 可以返回对象。 现在我想将它们合并/扩展为一个新对象,因此我使用 Lodash's extend . var whenEverythingIsDone = Promise.a
这是我认为人们通常希望在 Scala 中做的事情,但如果我能在任何地方找到一个例子,我就该死了。 这段代码由于类型删除而无法编译,但它演示了我正在努力完成的事情: def parse[T](json:
这是我认为人们通常希望在 Scala 中做的事情,但如果我能在任何地方找到一个例子,我就该死了。 这段代码由于类型删除而无法编译,但它演示了我正在努力完成的事情: def parse[T](json:
我们有大量 MOSS 2007 站点需要添加大量的 javascript。我编辑、 checkin 、发布并批准了对 default.master 的更改,更改反射(reflect)在根网站上,但没有
请看一下下面的 fiddle :http://jsfiddle.net/K9NjY/ 我在这段代码上花了 3-4 个小时,并将其缩小到最短的版本,但现在我陷入了困境。 问题:1. 点击“divOne”
我读到如果在流程中抛出异常,框架要做的第一件事就是检查消息头中的错误 channel 属性。总是这样吗? 在我的特殊情况下,我将自定义错误 channel 分配给消息 header ,但该消息似乎已向
创建一个小的 C++ 大型精度类,一切似乎都运行良好,但是添加,如果我将 0xffffffff 和 0x04 加在一起,我会得到 0xffff0003,而我应该得到 0x0100000003。这是有问
我正在尝试重新创建 Dan Abramov 类(class)中的 Redux 示例。传播{...store.getState()}在应用程序级别不起作用,Redux 正在更改状态并且 React 不会
考虑一个需要很长时间的事务。在此期间,我想对 TableSmall 执行一些小更新。 ,它应该立即执行,并且主事务的回滚不应该回滚那些小的更新。 我当前的问题是这些小更新将锁定 TableSmall\
我需要对现有函数进行修改,具有一些 const 输入参数: int f(const owntype *r1, const owntype *r2) 为了做到这一点,我想调用一个使用相同类型但没有 co
我有一个带有 ViewModel 的 WPF UserControl: 这个 UserControl 有一个 De
我试图在收到这样的短信时不传播 public class SMSReceiver extends BroadcastReceiver { @Override public void onRec
我是一名优秀的程序员,十分优秀!