1

运行代码片段(PyTorch 1.7.1;Python 3.8)后,

import numpy as np
import torch

def batch_matrix(vector_pairs, factor=2):
    baselen = len(vector_pairs[0]) // factor
    split_batch = []

    for j in range(factor):
        for i in range(factor):
            start_j = j * baselen
            end_j = (j+1) * baselen if j != factor - 1 else None
            start_i = i * baselen
            end_i = (i+1) * baselen if i != factor - 1 else None

            mini_pairs = vector_pairs[start_j:end_j, start_i:end_i, :]
            split_batch.append(mini_pairs)
    return split_batch

def concat_matrix(vectors_):
    vectors = vectors_.clone()
    seq_len, dim_vec = vectors.shape
    project_x = vectors.repeat((1, 1, seq_len)).reshape(seq_len, seq_len, dim_vec)
    project_y = project_x.permute(1, 0, 2)
    matrix = torch.cat((project_x, project_y), dim=-1)
    matrix_ = matrix.clone()

    return matrix_

if __name__ == "__main__":
    vector_list = []
    for i in range(10):
        vector_list.append(torch.randn((5,), requires_grad=True))
    vectors = torch.stack(vector_list, dim=0)
    pmatrix = concat_matrix(vectors)

    factor = np.ceil(vectors.shape[0]/6).astype(int)
    batched_feats = batch_matrix(pmatrix, factor=factor)

    for i in batched_feats:
        i = i + 5
        print(i.shape)
        summed = torch.sum(i)
        summed.backward()

我得到如下输出和错误:

torch.Size([5, 5, 10])
torch.Size([5, 5, 10])
Traceback (most recent call last):
  File "/home/user/PycharmProjects/project/run.py", line 43, in <module>
    summed.backward()
  File "/home/user/anaconda3/envs/diff/lib/python3.8/site-packages/torch/tensor.py", line 221, in backward
    torch.autograd.backward(self, gradient, retain_graph, create_graph)
  File "/home/user/anaconda3/envs/diff/lib/python3.8/site-packages/torch/autograd/__init__.py", line 130, in backward
    Variable._execution_engine.run_backward(
RuntimeError: Trying to backward through the graph a second time, but the saved intermediate results have already been freed. Specify retain_graph=True when calling backward the first time.

我已阅读有关该问题的所有现有帖子,但自己无法解决。传入retain_graph=Truebackward() 修复了提供的代码段中的问题,但是,代码段只是大型网络的过度简化版本,其中retain_graph=True将错误更改为以下内容:

RuntimeError:梯度计算所需的变量之一已被inplace操作修改:[torch.FloatTensor [3000, 512]],即TBackward的输出0,版本3;而是预期的版本 2。提示:启用异常检测以查找未能计算其梯度的操作,使用 torch.autograd.set_detect_anomaly(True)。

我尝试设置torch.autograd.set_detect_anomaly(True)和确定故障点,但我尝试的所有方法都失败了并且错误仍然存​​在。

我怀疑如果我能理解当前情况下的错误原因,那么它将帮助我在实际代码库中解决这个错误。

因此,我想了解为什么它对 中backward()的前两个张量工作正常batched_feats,而对第三个张量却失败了?如果有人可以帮助我查看已释放的中间结果的重用,我将不胜感激。

非常感谢!

4

1 回答 1

0

在反向传播之后,叶节点的梯度存储在它们的Tensor.grad属性中。默认情况下释放非叶节点的梯度(即错误所指的中间结果),因为 PyTorch 假设您不需要它们。在您的示例中,您的叶节点是vector_list从创建的torch.randn()

backward()默认情况下,连续多次调用会通过求和累加梯度(这对循环神经网络很有用)。当现有的中间结果已被释放时,这是有问题的;叶节点的梯度没有;并且对 的调用backward()涉及一些与之前对 的调用相同的叶节点和中间结果backward()。这是您面临的问题;您的一些张量切片引用了相同的基础张量,并且您没有将调用之间的所有相关梯度归零backward(),但您式地将中间梯度归零。

如果您希望通过求和累积叶节点中的梯度,只需像这样向后调用summed.backward(retain_graph = True)

但是,如果您希望独立计算批次的梯度(而不是写入 中的叶节点vector_list),那么您可以在每次迭代开始时分离批次。这将防止梯度通过它们一直传播到它们的公共叶节点vector_list(即它们自己成为自己图中的叶节点)。分离张量会禁用渐变,因此您必须手动重新启用它们:

for i in batched_feats:
    i = i.detach()
    i.requires_grad = True
    j = i + 5
    print(j.shape)
    summed = torch.sum(j)
    summed.backward()
    print(i.grad) # Prints the gradients stored in i

这就是一些数据加载器的工作方式;他们从磁盘加载数据,将它们转换为张量,执行增强/其他预处理,然后分离它们,以便它们可以用作新计算图中的叶节点。如果应用程序开发人员想要计算数据张量的梯度,他们不必保存中间结果,因为数据张量已被分离并因此用作叶节点。

于 2021-09-27T02:50:39.390 回答