Pytorch - 为什么预分配内存会导致 "trying to backward through the graph a second time"
Pytorch - why does preallocating memory cause "trying to backward through the graph a second time"
假设我有一个以典型方式训练的简单单隐藏层网络:
for x,y in trainData:
optimizer.zero_grad()
out = self(x)
loss = self.lossfn(out, y)
loss.backward()
optimizer.step()
这按预期工作,但如果我改为预分配和更新输出数组,我会收到错误消息:
out = torch.empty_like(trainData.tensors[1])
for i,(x,y) in enumerate(trainData):
optimizer.zero_grad()
out[i] = self(x)
loss = self.lossfn(out[i], y)
loss.backward()
optimizer.step()
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.
这里发生了什么,在第二个版本中 Pytorch 试图再次向后遍历图表?为什么这在第一个版本中不是问题? (请注意,即使我不这样做也会出现此错误zero_grad()
)
该错误表明程序正在尝试通过一组操作进行第二次反向传播。第一次通过一组操作进行反向传播时,pytorch 会删除计算图以释放内存。因此,您第二次尝试反向传播失败,因为该图已被删除。
同样的详细解释。
简答
使用loss.backward(retain_graph=True)
。这不会删除计算图。
详细解答
第一个版本中,在每次循环迭代中,每out = self(x)
为运行,就会生成一个新的计算图。
Every loop's graph
out = self(x) -> loss = self.lossfn(out, y)
在第二个版本中,由于out
是在循环外声明的,所以每一个循环中的计算图在外都有一个父节点。
- out[i] = self(x) -> loss = self.lossfn(out[i], y)
out[i] - | - out[i] = self(x) -> loss = self.lossfn(out[i], y)
- out[i] = self(x) -> loss = self.lossfn(out[i], y)
因此,这是发生的事情的时间表。
- 第一次迭代运行s
- 计算图被删除,包括父节点
- 第二次迭代尝试反向传播但失败,因为它没有找到父节点
假设我有一个以典型方式训练的简单单隐藏层网络:
for x,y in trainData:
optimizer.zero_grad()
out = self(x)
loss = self.lossfn(out, y)
loss.backward()
optimizer.step()
这按预期工作,但如果我改为预分配和更新输出数组,我会收到错误消息:
out = torch.empty_like(trainData.tensors[1])
for i,(x,y) in enumerate(trainData):
optimizer.zero_grad()
out[i] = self(x)
loss = self.lossfn(out[i], y)
loss.backward()
optimizer.step()
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.
这里发生了什么,在第二个版本中 Pytorch 试图再次向后遍历图表?为什么这在第一个版本中不是问题? (请注意,即使我不这样做也会出现此错误zero_grad()
)
该错误表明程序正在尝试通过一组操作进行第二次反向传播。第一次通过一组操作进行反向传播时,pytorch 会删除计算图以释放内存。因此,您第二次尝试反向传播失败,因为该图已被删除。
简答
使用loss.backward(retain_graph=True)
。这不会删除计算图。
详细解答
第一个版本中,在每次循环迭代中,每out = self(x)
为运行,就会生成一个新的计算图。
Every loop's graph
out = self(x) -> loss = self.lossfn(out, y)
在第二个版本中,由于out
是在循环外声明的,所以每一个循环中的计算图在外都有一个父节点。
- out[i] = self(x) -> loss = self.lossfn(out[i], y)
out[i] - | - out[i] = self(x) -> loss = self.lossfn(out[i], y)
- out[i] = self(x) -> loss = self.lossfn(out[i], y)
因此,这是发生的事情的时间表。
- 第一次迭代运行s
- 计算图被删除,包括父节点
- 第二次迭代尝试反向传播但失败,因为它没有找到父节点