首页 > 解决方案 > Pytorch - 为什么预分配内存会导致“尝试第二次向后遍历图形”

问题描述

假设我有一个简单的单层网络,我正在以典型的方式进行训练:

    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:试图第二次向后遍历图形,但缓冲区已被释放。第一次向后调用时指定retain_graph=True。

在第二个版本中 Pytorch 再次尝试向后遍历图表时发生了什么?为什么这在第一个版本中不是问题?(请注意,即使我不这样做也会发生此错误zero_grad()

标签: pytorchbackpropagationtensorautogradautodiff

解决方案


该错误意味着程序正在尝试通过一组操作第二次反向传播。第一次通过一组操作进行反向传播时,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)

因此,这是发生的时间线。

  1. 第一次迭代运行
  2. 计算图被删除,包括父节点
  3. 第二次迭代尝试反向传播但失败,因为它没有找到父节点

推荐阅读