首页 > 解决方案 > 如何在pytorch中处理这种情况?

问题描述

我有以下两种相关的张量datamask. is的大小和datais1x2x24x2的大小。在中,意味着对应的数据是有效的,应该是梯度反向传播的。mask1x2x24maskTruedata

data:
tensor([[[[ 1.0663e+03,  5.5993e+02],
          [ 1.0612e+03,  7.2023e+02],
          [ 1.0831e+03,  7.2179e+02],
          [ 1.0945e+03,  5.6083e+02],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10]],

         [[ 6.9314e+02,  1.9700e+02],
          [ 6.3300e+02,  2.6924e+02],
          [ 6.3300e+02,  3.4165e+02],
          [ 7.7515e+02,  4.6000e+02],
          [ 8.2805e+02,  4.6000e+02],
          [ 9.0900e+02,  3.6276e+02],
          [ 9.0900e+02,  2.9035e+02],
          [ 7.9688e+02,  1.9700e+02],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10],
          [-1.0000e+10,  1.0000e+10]]]]) 
torch.Size([1, 2, 24, 2])

mask:
tensor([[[ True,  True,  True,  True, False, False, False, False, False, False,
          False, False, False, False, False, False, False, False, False, False,
          False, False, False, False],
         [ True,  True,  True,  True,  True,  True,  True,  True, False, False,
          False, False, False, False, False, False, False, False, False, False,
          False, False, False, False]]]) 
torch.Size([1, 2, 24])

基于dataand mask,我需要进一步处理。例如,我需要做这种for循环:

B, N = data.shape[0:2]
result = torch.zeros((B, N), dtype=torch.float32)
for b in range(B):
    for n in range(N):
        tmp_data = data[b,n]  # 24 x 2
        tmp_mask = mask[b,n]  # 24
        selected = tmp_data[tmp_mask]
        selected = torch.cat([selected, selected[0][None]], dim=0)
        total = selected[0:-1, 0] * selected[1:, 1] - selected[0:-1, 1] * selected[1:, 0]   # do cross product
        total = torch.sum(total, dim=0)
        result[b,n] = torch.abs(total) / 2

然后,result张量被送入后续处理,例如损失计算。我的问题是,有没有什么加速方法可以摆脱for-loop主要难点在于对​​于每个band , inn的个数不相等,有时是 3,有时是 5,但是每个样本中的最大个数是 8。TruemaskTrue所以,在 中for-loop,需要处理每个一个一个的案例。有什么矢量化的方法吗?

另一个困难在于梯度反向传播,我需要进一步调查以检查该例程是否可以正确地用于梯度反向传播。

我期待有一种优雅的方式来处理 pytorch 中的这种张量处理。提前致谢!

更新

和由上一个例程排序datamask例如:

sorted_inds = ...  # B x N x 24

data = torch.gather(data_original, dim=2, index=sorted_indices.unsqueeze(-1).repeat(1,1,1,2))  # B x N x 24 x 2
mask= torch.gather(mask_original, dim=2, index=sorted_indices)  # B x N x 24

# then the data and mask are obtained
# continue processing in this post
...

标签: pytorch

解决方案


您正在寻找使用张量操作对两个 for 循环进行矢量化。一般来说,这可以通过忽略前两个轴并在后两个轴上执行操作来实现。但是,在这种特殊情况下,我认为这是无法实现的。

原因在于(通过索引)发生的data掩蔽。此操作只会返回一个展平的张量:。mask

data[mask]
tensor([[1066.3000,  559.9300],
        [1061.2000,  720.2300],
        [1083.1000,  721.7900],
        [1094.5000,  560.8300],
        [ 693.1400,  197.0000],
        [ 633.0000,  269.2400],
        [ 633.0000,  341.6500],
        [ 775.1500,  460.0000],
        [ 828.0500,  460.0000],
        [ 909.0000,  362.7600],
        [ 909.0000,  290.3500],
        [ 796.8800,  197.0000]])

如您所见,保留了正确的值,但未保留所需的形状。这就是问题所在。这就是我们希望拥有的(为了继续使用矢量化形式)。这是不可能的,因为一个维度上的所有张量都必须具有相同的长度:该维度上的大小

tensor([[[1066.3000,  559.9300],
         [1061.2000,  720.2300],
         [1083.1000,  721.7900],
         [1094.5000,  560.8300]],
        [[ 693.1400,  197.0000],
         [ 633.0000,  269.2400],
         [ 633.0000,  341.6500],
         [ 775.1500,  460.0000],
         [ 828.0500,  460.0000],
         [ 909.0000,  362.7600],
         [ 909.0000,  290.3500],
         [ 796.8800,  197.0000]])

关于叉积。PyTorch 为此提供了一个函数:torch.cross,但它只能用于 3D 张量。在此之上,无法使用它。我认为你的实现很好。


一些建议,这些不是开创性的改进,但可以提供帮助:

  • 索引有一个技巧可以避免在单元素张量上做x.unsqueeze(0)(或像你做的那样):通过 slicing 。x[None]x[:1]

  • 参数dim通常默认为0. 因为.sum()您不需要它,因为您正在寻找总和而不是每个轴的总和。

  • 您可以展平前两个轴,执行一个循环并在最后重塑。

在这里,我冒昧地调整了您的代码:

B, N, *_ = data.shape
data_ = data.reshape(B*N, -1, 2)
mask_ = mask.reshape(B*N, -1)

result = []
for i in range(B*N):
    selected = data_[i][mask_[i]]
    looped = torch.cat([selected, selected[:1]])
    cross = looped[:-1, 0]*looped[1:, 1] - looped[:-1, 1]*looped[1:, 0]
    result.append(cross.sum().abs() / 2)

torch.tensor(result).reshape(B, N)

推荐阅读