有没有可能使用Tensor索引对PyTorchTensor进行子集划分,而不会导致向后的第二次错误?

igetnqfo  于 2023-06-06  发布在  其他
关注(0)|答案(1)|浏览(161)

我正在尝试编写一个基于计算嵌入的Pytorch程序,在训练过程中使用嵌入的各种子集。我想使用Tensor索引对嵌入Tensor进行子集化,但是当我这样做时,我会得到典型的向后二次误差。但是,使用切片而不是索引来执行完全相同的操作可以运行得很好。在我的实际代码中,我已经确认了切片版本可以按预期工作并正确训练等,但新功能不能只用切片来完成。
我在Windows上运行python 3.10.9,pytorch 2.0.0和cuda 11.8。
下面是一个最小的例子,重现了我遇到的错误:

import torch
device = 'cpu'    # same error occurs when using device='cuda:0'
embeddings = torch.tensor(torch.randn([1024, 128], device=device, dtype=torch.float32), requires_grad=True)
target = torch.rand([1024, 128], device=device)
optimizer = torch.optim.Adadelta([embeddings], lr=1.0)

# alternate approach using nn.Embedding doesn't work either
embeddings_2 = torch.nn.Embedding(1024, 128, device=device)

# OPTION 1: This works
# cur_embeddings = embeddings[:512, :]

# OPTION 2: This is what I want to do, but it doesn't work
cur_embeddings = embeddings[torch.arange(512, device=device), :]

# OPTION 3: The following option does not work either
# cur_embeddings = embeddings_2(torch.arange(512, device=device))

cur_target = target[:512, :]
for idx in range(2):
    optimizer.zero_grad()
    loss = torch.nn.MSELoss()(cur_embeddings, cur_target)
    loss.backward()
    optimizer.step()

在所呈现的3个选项中,选项1工作,但选项2产生错误,即使其功能相同。如何使选项2的工作方式与选项1完全相同(同时保持对任意行进行子集划分的自由)?
我还在这里展示了使用nn.Embedding(我并不真的想使用它,但如果它能工作的话,我可以使用它)仍然会产生错误。

c6ubokkw

c6ubokkw1#

**解决方案:**将这一行放在训练循环中:

cur_embeddings = embeddings[torch.arange(512, device=device), :]

**说明:**您的计算图如下

embeddings ---> cur_embeddings ---> loss
            |                   |
    torch.arange(512)       cur_target

要反向传播到embeddings,pytorch必须记住如何索引embeddings以获得cur_embeddings,也就是说,pytorch需要保存torch.arange(512)以进行反向传播。然而,通过将这个索引操作放在循环之外,torch.arange(512)在第一次反向操作之后被释放,所以在第二次反向操作之前,pytorch已经“忘记”了索引是如何完成的。
您的选项1可以工作,因为索引被硬编码为embeddings[:512]

相关问题