【问题标题】:Best way to cut a pytorch tensor into overlapping chunks?将pytorch张量切割成重叠块的最佳方法?
【发布时间】:2021-02-14 17:49:26
【问题描述】:

如果我有:

eg6 = torch.tensor([
    [ 1.,  7., 13., 19.],
    [ 2.,  8., 14., 20.],
    [ 3.,  9., 15., 21.],
    [ 4., 10., 16., 22.],
    [ 5., 11., 17., 23.],
    [ 6., 12., 18., 24.]])
batch1 = eg6
batch2 = -eg6
x = torch.cat((batch1,batch2)).view(2,6,4)

然后我想把它分成重叠的块,就像一个滑动窗口函数,并让这些块可以批处理。例如,只看第一个维度,我想要1,2,33,4,55,6(或5,6,0)。

似乎unfold() 可以满足我的要求。由于某种原因,它转置了最后两个维度,但这很容易修复。将其从形状[2,3,3,4] 更改为[6,3,4] 需要内存副本,但我认为这是不可避免的?

SZ=3
x2 = x.unfold(1,SZ,2).transpose(2,3).reshape(-1,SZ,4)

x 的形状为[2,7,4] 时,这非常有效。但是只有 6 行,它会丢弃最后一行。

是否有可以告诉unfold() 使用所有数据的版本,最好是使用填充字符?

或者我需要在调用unfold() 之前填充x 吗?最好的方法是什么?我想知道“pad”是否是错误的词,因为我只找到想要在两端放置填充字符的函数,并考虑到卷积。


旁白:看the source of unfold,奇怪的转置似乎是故意而明确的?!出于这个原因,以及不受欢迎的斩波行为,它让我认为我的问题的正确答案可能是编写一个新的低级函数。但这对我来说太费力了,至少在今天……(我认为还需要编写用于向后传递的第二个函数。)

【问题讨论】:

    标签: pytorch


    【解决方案1】:

    这里执行的操作类似于一维卷积的行为。使用kernel_size=SZstride=2。正如您所注意到的,如果您没有提供足够的填充(您的措辞是正确的),则不会使用最后一个元素。

    一般方法(对于任何SZ 和任何输入形状x.size(1))是确定是否需要填充,如果需要,需要多少。

    • 输出的大小由out = floor((x.size(1) - SZ)/2 + 1)给出。

    • 未使用元素的数量是x.size(1) - out*(SZ-1) - 1

    • 如果未使用的元素个数不为零,则需要添加(out+1)*(SZ-1) + 1 - x.size(1)的padding


    这个例子不需要填充:

    >>> x = torch.stack((torch.tensor([
                [ 1.,  7., 13., 19.],
                [ 2.,  8., 14., 20.],
                [ 3.,  9., 15., 21.],
                [ 4., 10., 16., 22.],
                [ 5., 11., 17., 23.]]),)*2)
    
    >>> x.shape
    torch.Size([2, 5, 4])
    
    >>> out = floor((x.size(1) - SZ)/2 + 1)
    2
    
    >>> unused = x.size(1) - out*(SZ-1) - 1
    0
    

    虽然这个会:

    >>> x = torch.stack((torch.tensor([
              [ 1.,  7., 13., 19.],
              [ 2.,  8., 14., 20.],
              [ 3.,  9., 15., 21.],
              [ 4., 10., 16., 22.],
              [ 5., 11., 17., 23.],
              [ 6., 12., 18., 24.]]),)*2)
    
    >>> x.shape
    torch.Size([2, 6, 4])
    
    >>> out = floor((x.size(1) - SZ)/2 + 1)
    2
    
    >>> unused = x.size(1) - out*(SZ-1) - 1
    1
    
    >>> p = (out+1)*(SZ-1) + 1 - x.size(1)
    1
    

    现在,要实际添加填充,您只需使用 torch.cat。虽然我是内置的,nn.functional.pad,但会工作...

    >>> torch.cat((x, torch.zeros(x.size(0), p, x.size(2))), dim=1)
    tensor([[[ 1.,  7., 13., 19.],
             [ 2.,  8., 14., 20.],
             [ 3.,  9., 15., 21.],
             [ 4., 10., 16., 22.],
             [ 5., 11., 17., 23.],
             [ 6., 12., 18., 24.],
             [ 0.,  0.,  0.,  0.]],
    
            [[ 1.,  7., 13., 19.],
             [ 2.,  8., 14., 20.],
             [ 3.,  9., 15., 21.],
             [ 4., 10., 16., 22.],
             [ 5., 11., 17., 23.],
             [ 6., 12., 18., 24.],
             [ 0.,  0.,  0.,  0.]]])
    

    【讨论】:

    • 使用F.pad(x, (0,0,0,p)) 确实有效,并且比cat 更易于阅读(我猜它至少也一样有效??)。我使用了p = (SZ - overlap) - 1,它给出了需要多少填充的上限:因为unfold() 将多余的部分扔掉,这并不重要。
    • 好,肯定使用F.pad,那么最好使用内置插件而不是解决方法。
    猜你喜欢
    • 2017-05-24
    • 2014-04-11
    • 2021-03-10
    • 2021-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-14
    • 1970-01-01
    相关资源
    最近更新 更多