【问题标题】:Pytorch reshape tensor dimensionPytorch 重塑张量维度
【发布时间】:2017-09-05 19:40:31
【问题描述】:

例如,我有一个维度为 (5) 的一维向量。我想将其重塑为二维矩阵 (1,5)。

这是我使用 numpy 的方法

>>> import numpy as np
>>> a = np.array([1,2,3,4,5])
>>> a.shape
(5,)
>>> a = np.reshape(a, (1,5))
>>> a.shape
(1, 5)
>>> a
array([[1, 2, 3, 4, 5]])
>>> 

但是如何使用 Pytorch 张量(和变量)来做到这一点。我不想切换回 numpy 并再次切换到 Torch 变量,因为它会丢失反向传播信息。

这是我在 Pytorch 中所拥有的

>>> import torch
>>> from torch.autograd import Variable
>>> a = torch.Tensor([1,2,3,4,5])
>>> a

 1
 2
 3
 4
 5
[torch.FloatTensor of size 5]

>>> a.size()
(5L,)
>>> a_var = variable(a)
>>> a_var = Variable(a)
>>> a_var.size()
(5L,)
.....do some calculation in forward function
>>> a_var.size()
(5L,)

现在我希望它的大小为 (1, 5)。 如何在不丢失 grad 信息的情况下调整变量中 pytorch 张量的尺寸或重塑其尺寸。 (因为我会在后退之前输入另一个模型)

【问题讨论】:

    标签: python numpy deep-learning pytorch tensor


    【解决方案1】:

    使用torch.unsqueeze(input, dim, out=None)

    >>> import torch
    >>> a = torch.Tensor([1,2,3,4,5])
    >>> a
    
     1
     2
     3
     4
     5
    [torch.FloatTensor of size 5]
    
    >>> a = a.unsqueeze(0)
    >>> a
    
     1  2  3  4  5
    [torch.FloatTensor of size 1x5]
    

    【讨论】:

      【解决方案2】:

      你可能会使用

      a.view(1,5)
      Out: 
      
       1  2  3  4  5
      [torch.FloatTensor of size 1x5]
      

      【讨论】:

      • 请注意,这不会修改原始张量a。它只是创建一个视图。
      【解决方案3】:

      有多种方法可以重塑 PyTorch 张量。您可以将这些方法应用于任何维度的张量。

      让我们从二维2 x 3 张量开始:

      x = torch.Tensor(2, 3)
      print(x.shape)
      # torch.Size([2, 3])
      

      为了给这个问题增加一些鲁棒性,让我们重塑 2 x 3 张量,在前面添加一个新维度,在中间添加另一个维度,生成一个 1 x 2 x 1 x 3 张量。

      方法一:用None添加维度

      在任何你想要的地方使用 NumPy 风格的 insertion of None (aka np.newaxis) to add dimensions。见here

      print(x.shape)
      # torch.Size([2, 3])
      
      y = x[None, :, None, :] # Add new dimensions at positions 0 and 2.
      print(y.shape)
      # torch.Size([1, 2, 1, 3])
      

      方法2:解压

      使用torch.Tensor.unsqueeze(i)(又名torch.unsqueeze(tensor, i) 或就地版本unsqueeze_())在第i 个维度添加一个新维度。返回的张量与原始张量共享相同的数据。在这个例子中,我们可以使用unqueeze() 两次来添加两个新维度。

      print(x.shape)
      # torch.Size([2, 3])
      
      # Use unsqueeze twice.
      y = x.unsqueeze(0) # Add new dimension at position 0
      print(y.shape)
      # torch.Size([1, 2, 3])
      
      y = y.unsqueeze(2) # Add new dimension at position 2
      print(y.shape)
      # torch.Size([1, 2, 1, 3])
      

      在 PyTorch 的实践中,adding an extra dimension for the batch 可能很重要,所以你可能经常看到unsqueeze(0)

      方法三:查看

      使用torch.Tensor.view(*shape) 指定所有维度。返回的张量与原始张量共享相同的数据。

      print(x.shape)
      # torch.Size([2, 3])
      
      y = x.view(1, 2, 1, 3)
      print(y.shape)
      # torch.Size([1, 2, 1, 3])
      

      方法四:重塑

      使用torch.Tensor.reshape(*shape)(又名torch.reshape(tensor, shapetuple))指定所有维度。如果原始数据是连续的并且具有相同的步幅,则返回的张量将是输入的视图(共享相同的数据),否则将是副本。此函数类似于 NumPy reshape() 函数,因为它允许您定义所有维度并可以返回视图或副本。

      print(x.shape)
      # torch.Size([2, 3])
      
      y = x.reshape(1, 2, 1, 3)
      print(y.shape)
      # torch.Size([1, 2, 1, 3])
      

      此外,来自 O'Reilly 2019 年出版的书 Programming PyTorch for Deep Learning,作者写道:

      现在您可能想知道view()reshape() 之间有什么区别。答案是view() 作为原始张量上的视图运行,因此如果基础数据发生更改,视图也会更改(反之亦然)。但是,如果所需的视图不连续,view() 可能会抛出错误;也就是说,如果从头开始创建所需形状的新张量,它不会共享相同的内存块。如果发生这种情况,您必须先致电tensor.contiguous(),然后才能使用view()。然而,reshape() 在幕后完成了所有这些工作,所以总的来说,我建议使用reshape() 而不是view()

      方法五:调整大小_

      使用就地函数torch.Tensor.resize_(*sizes) 修改原始张量。文档指出:

      警告。这是一种低级方法。存储被重新解释为 C 连续,忽略当前步幅(除非目标大小等于当前大小,在这种情况下张量保持不变)。在大多数情况下,您将希望使用检查连续性的view() 或在需要时复制数据的reshape()。要使用自定义步幅就地更改大小,请参阅set_()

      print(x.shape)
      # torch.Size([2, 3])
      
      x.resize_(1, 2, 1, 3)
      print(x.shape)
      # torch.Size([1, 2, 1, 3])
      

      我的观察

      如果您只想添加一个维度(例如,为批次添加第 0 个维度),请使用unsqueeze(0)。如果您想完全改变维度,请使用reshape()

      另见:

      What's the difference between reshape and view in pytorch?

      What is the difference between view() and unsqueeze()?

      In PyTorch 0.4, is it recommended to use reshape than view when it is possible?

      【讨论】:

        【解决方案4】:

        对于张量形状的就地修改,你应该使用 tensor.resize_():

        In [23]: a = torch.Tensor([1, 2, 3, 4, 5])
        
        In [24]: a.shape
        Out[24]: torch.Size([5])
        
        
        # tensor.resize_((`new_shape`))    
        In [25]: a.resize_((1,5))
        Out[25]: 
        
         1  2  3  4  5
        [torch.FloatTensor of size 1x5]
        
        In [26]: a.shape
        Out[26]: torch.Size([1, 5])
        

        在 PyTorch 中,如果操作末尾有下划​​线(如 tensor.resize_()),则该操作会对原始张量进行 in-place 修改。


        此外,您可以在 Torch 张量中简单地使用 np.newaxis 来增加维度。这是一个例子:

        In [34]: list_ = range(5)
        In [35]: a = torch.Tensor(list_)
        In [36]: a.shape
        Out[36]: torch.Size([5])
        
        In [37]: new_a = a[np.newaxis, :]
        In [38]: new_a.shape
        Out[38]: torch.Size([1, 5])
        

        【讨论】:

          【解决方案5】:

          或者你可以使用它,'-1' 意味着你不必指定元素的数量。

          In [3]: a.view(1,-1)
          Out[3]:
          
           1  2  3  4  5
          [torch.FloatTensor of size 1x5]
          

          【讨论】:

            【解决方案6】:

            这个问题已经得到了彻底的回答,但我想为经验不足的 python 开发人员补充一点,您可能会发现* 运算符与view() 结合使用会很有帮助。

            例如,如果您有一个特定的张量大小并且希望不同的数据张量符合,您可以尝试:

            img = Variable(tensor.randn(20,30,3)) # tensor with goal shape
            flat_size = 20*30*3
            X = Variable(tensor.randn(50, flat_size)) # data tensor
            
            X = X.view(-1, *img.size()) # sweet maneuver
            print(X.size()) # size is (50, 20, 30, 3)
            

            这也适用于 numpy shape

            img = np.random.randn(20,30,3)
            flat_size = 20*30*3
            X = Variable(tensor.randn(50, flat_size))
            X = X.view(-1, *img.shape)
            print(X.size()) # size is (50, 20, 30, 3)
            

            【讨论】:

              【解决方案7】:

              torch.reshape() 被用来欺骗numpy reshape 方法。

              它位于view()torch.resize_() 之后,位于dir(torch) 包内。

              import torch
              x=torch.arange(24)
              print(x, x.shape)
              x_view = x.view(1,2,3,4) # works on is_contiguous() tensor
              print(x_view.shape)
              x_reshaped = x.reshape(1,2,3,4) # works on any tensor
              print(x_reshaped.shape)
              x_reshaped2 = torch.reshape(x_reshaped, (-1,)) # part of torch package, while view() and resize_() are not
              print(x_reshaped2.shape)
              

              输出:

              tensor([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17,
                      18, 19, 20, 21, 22, 23]) torch.Size([24])
              torch.Size([1, 2, 3, 4])
              torch.Size([1, 2, 3, 4])
              torch.Size([24])
              

              但您知道它也可以替代squeeze()unsqueeze()

              x = torch.tensor([1, 2, 3, 4])
              print(x.shape)
              x1 = torch.unsqueeze(x, 0)
              print(x1.shape)
              x2 = torch.unsqueeze(x1, 1)
              print(x2.shape)
              x3=x.reshape(1,1,4)
              print(x3.shape)
              x4=x.reshape(4)
              print(x4.shape)
              x5=x3.squeeze()
              print(x5.shape)
              
              

              输出:

              torch.Size([4])
              torch.Size([1, 4])
              torch.Size([1, 1, 4])
              torch.Size([1, 1, 4])
              torch.Size([4])
              torch.Size([4])
              

              【讨论】:

                【解决方案8】:
                import torch
                >>>a = torch.Tensor([1,2,3,4,5])
                >>>a.size()
                torch.Size([5])
                #use view to reshape
                
                >>>b = a.view(1,a.shape[0])
                >>>b
                tensor([[1., 2., 3., 4., 5.]])
                >>>b.size()
                torch.Size([1, 5])
                >>>b.type()
                'torch.FloatTensor'
                

                【讨论】:

                  【解决方案9】:

                  据我所知,重塑张量的最佳方法是使用einops。它通过提供简单而优雅的功能解决了各种重塑问题。在你的情况下,代码可以写成

                  from einops import rearrange
                  ans = rearrange(tensor,'h -> 1 h')
                  

                  我强烈建议您尝试一下。

                  顺便说一句,您可以将它与 pytorch/tensorflow/numpy 和许多其他库一起使用。

                  【讨论】:

                    【解决方案10】:

                    假设以下代码:

                    import torch
                    import numpy as np
                    a = torch.tensor([1, 2, 3, 4, 5])
                    

                    以下三个调用具有完全相同的效果:

                    res_1 = a.unsqueeze(0)
                    res_2 = a.view(1, 5)
                    res_3 = a[np.newaxis,:]
                    res_1.shape == res_2.shape == res_3.shape == (1,5)  # Returns true
                    

                    请注意,对于任何生成的张量,如果您修改其中的数据,您也在修改 a 中的数据,因为它们没有数据的副本,而是引用 a 中的原始数据。

                    res_1[0,0] = 2
                    a[0] == res_1[0,0] == 2  # Returns true
                    

                    另一种方法是使用resize_就地操作:

                    a.shape == res_1.shape  # Returns false
                    a.reshape_((1, 5))
                    a.shape == res_1.shape # Returns true
                    

                    小心使用resize_autograd 的其他就地操作。请参阅以下讨论:https://pytorch.org/docs/stable/notes/autograd.html#in-place-operations-with-autograd

                    【讨论】:

                    • 您声明另一种方法是使用 resize_ in place 操作,但您的代码使用reshape_
                    【解决方案11】:
                    import torch
                    t = torch.ones((2, 3, 4))
                    t.size()
                    
                    >>torch.Size([2, 3, 4])
                    
                    a = t.view(-1,t.size()[1]*t.size()[2])
                    a.size()
                    
                    >>torch.Size([2, 12])
                    

                    【讨论】:

                      猜你喜欢
                      • 2022-07-26
                      • 2018-10-27
                      • 2019-07-22
                      • 1970-01-01
                      • 2021-10-23
                      • 1970-01-01
                      • 2023-01-19
                      • 2018-07-19
                      • 2020-02-09
                      相关资源
                      最近更新 更多