`stack()` 与 `cat()`

问题描述 投票:0回答:5

OpenAI 的强化学习 REINFORCE 和 actor-critic 示例具有以下代码:

加强

policy_loss = torch.cat(policy_loss).sum()

演员评论家

loss = torch.stack(policy_losses).sum() + torch.stack(value_losses).sum()

一个使用

torch.cat
,另一个使用
torch.stack
,用于类似的用例。

据我的理解,文档没有给出它们之间的任何明确区别。

我很高兴知道这些功能之间的差异。

python machine-learning deep-learning pytorch concatenation
5个回答
242
投票

stack

沿新维度连接张量序列。

cat

在给定维度连接给定的 seq 张量序列。

因此,如果

A

B
 的形状为 (3, 4):

  • torch.cat([A, B], dim=0)
     形状为 (6, 4)
  • torch.stack([A, B], dim=0)
     形状为 (2, 3, 4)

42
投票
t1 = torch.tensor([[1, 2], [3, 4]]) t2 = torch.tensor([[5, 6], [7, 8]])

'Con
torch.stack

torch.cat

'Stacks' 沿着新维度的张量序列:

enter image description here



catenates' 沿现有维度的张量序列:

enter image description here
这些功能类似于

numpy.stack

numpy.concatenate


4
投票
原始答案缺乏一个独立的好例子,所以这里是:

import torch # stack vs cat # cat "extends" a list in the given dimension e.g. adds more rows or columns x = torch.randn(2, 3) print(f'{x.size()}') # add more rows (thus increasing the dimensionality of the column space to 2 -> 6) xnew_from_cat = torch.cat((x, x, x), 0) print(f'{xnew_from_cat.size()}') # add more columns (thus increasing the dimensionality of the row space to 3 -> 9) xnew_from_cat = torch.cat((x, x, x), 1) print(f'{xnew_from_cat.size()}') print() # stack serves the same role as append in lists. i.e. it doesn't change the original # vector space but instead adds a new index to the new tensor, so you retain the ability # get the original tensor you added to the list by indexing in the new dimension xnew_from_stack = torch.stack((x, x, x, x), 0) print(f'{xnew_from_stack.size()}') xnew_from_stack = torch.stack((x, x, x, x), 1) print(f'{xnew_from_stack.size()}') xnew_from_stack = torch.stack((x, x, x, x), 2) print(f'{xnew_from_stack.size()}') # default appends at the from xnew_from_stack = torch.stack((x, x, x, x)) print(f'{xnew_from_stack.size()}') print('I like to think of xnew_from_stack as a \"tensor list\" that you can pop from the front')
输出:

torch.Size([2, 3]) torch.Size([6, 3]) torch.Size([2, 9]) torch.Size([4, 2, 3]) torch.Size([2, 4, 3]) torch.Size([2, 3, 4]) torch.Size([4, 2, 3]) I like to think of xnew_from_stack as a "tensor list"
以下定义可供参考:

cat:连接给定维度中给定的 seq 张量序列。结果是特定尺寸改变尺寸,例如dim=0 那么您将向行添加元素,这会增加列空间的维度。

stack:沿新维度连接张量序列。我喜欢将其视为火炬“追加”操作,因为您可以通过从前面“弹出”来索引/获取原始张量。如果没有参数,它将张量附加到张量的前面。


相关:

    这里是 pytorch 论坛的链接,其中包含对此的讨论:
  • https://discuss.pytorch.org/t/best-way-to-convert-a-list-to-a-tensor/59949/8我但希望 tensor.torch
     将张量的嵌套列表转换为具有多个维度且尊重嵌套列表深度的大张量。

更新:使用相同大小的嵌套列表

def tensorify(lst): """ List must be nested list of tensors (with no varying lengths within a dimension). Nested list of nested lengths [D1, D2, ... DN] -> tensor([D1, D2, ..., DN) :return: nested list D """ # base case, if the current list is not nested anymore, make it into tensor if type(lst[0]) != list: if type(lst) == torch.Tensor: return lst elif type(lst[0]) == torch.Tensor: return torch.stack(lst, dim=0) else: # if the elements of lst are floats or something like that return torch.tensor(lst) current_dimension_i = len(lst) for d_i in range(current_dimension_i): tensor = tensorify(lst[d_i]) lst[d_i] = tensor # end of loop lst[d_i] = tensor([D_i, ... D_0]) tensor_lst = torch.stack(lst, dim=0) return tensor_lst
这里有一些单元测试(我没有编写更多测试,但它适用于我的真实代码,所以我相信它没问题。如果需要,请随时帮助我添加更多测试):

def test_tensorify(): t = [1, 2, 3] print(tensorify(t).size()) tt = [t, t, t] print(tensorify(tt)) ttt = [tt, tt, tt] print(tensorify(ttt)) if __name__ == '__main__': test_tensorify() print('Done\a')
    

1
投票
如果有人正在研究这方面的性能,我做了一个小实验。就我而言,我需要将标量张量列表转换为单个张量。

import torch torch.__version__ # 1.10.2 x = [torch.randn(1) for _ in range(10000)] torch.cat(x).shape, torch.stack(x).shape # torch.Size([10000]), torch.Size([10000, 1]) %timeit torch.cat(x) # 1.5 ms ± 476 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) %timeit torch.cat(x).reshape(-1,1) # 1.95 ms ± 534 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) %timeit torch.stack(x) # 5.36 ms ± 643 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
我的结论是,即使你想拥有

torch.stack

的附加维度,使用
torch.cat
然后使用
reshape
会更好。

注意:这篇文章取自PyTorch论坛(我是原帖的作者)


0
投票

stack() 可以连接一系列一个或多个 0D 或多个 D 张量,如下所示:

*备注:

  • stack()
     可以与 
    torch 一起使用,但不能与张量一起使用。
  • 带有
  • torch
     的第一个参数是 
    tensors
    (必需类型:
    tuple
    list
    tensor
    int
    float
    complex
    )。 *张量的大小必须相同。
    带有 
    bool
  • 的第二个参数是
  • torch
    (可选-默认:
    dim
    -类型:
    0
    )。
    int
  • 返回张量。
  • tensors+1D
cat()
连接一系列

import torch tensor1 = torch.tensor(2) tensor2 = torch.tensor(7) tensor3 = torch.tensor(4) torch.stack(tensors=(tensor1,)) # tensor([2]) torch.stack(tensors=(tensor1, tensor2)) # tensor([2, 7]) torch.stack(tensors=(tensor1, tensor2, tensor3)) # tensor([2, 7, 4]) tensor1 = torch.tensor([[2, 7, 4], [8, 3, 2]]) tensor2 = torch.tensor([[5, 0, 8], [3, 6, 1]]) tensor3 = torch.tensor([[9, 4, 7], [1, 0, 5]]) torch.stack(tensors=(tensor1, tensor2, tensor3)) torch.stack(tensors=(tensor1, tensor2, tensor3), dim=0) torch.stack(tensors=(tensor1, tensor2, tensor3), dim=-3) # tensor([[[2, 7, 4], [8, 3, 2]], # [[5, 0, 8], [3, 6, 1]], # [[9, 4, 7], [1, 0, 5]]]) torch.stack(tensors=(tensor1, tensor2, tensor3), dim=1) torch.stack(tensors=(tensor1, tensor2, tensor3), dim=-2) # tensor([[[2, 7, 4], [5, 0, 8], [9, 4, 7]], # [[8, 3, 2], [3, 6, 1], [1, 0, 5]]]) torch.stack(tensors=(tensor1, tensor2, tensor3), dim=2) torch.stack(tensors=(tensor1, tensor2, tensor3), dim=-1) # tensor([[[2, 5, 9], [7, 0, 4], [4, 8, 7]], # [[8, 3, 1], [3, 6, 0], [2, 1, 5]]]) 一个或多个 1D 或多个 D 张量,如下所示:

*备注:

seq
    可以与
  • cat()
     一起使用,但不能与张量一起使用。
    带有 
    torch
  • 的第一个参数是
  • torch
    (必需类型:
    tensors
    tuple
    list
    tensor
    int
    float
    )。 *基本上,张量的大小必须相同。
    带有 
    complex
     的第二个参数是 
    bool
  • (可选-默认:
  • torch
    -类型:
    dim
    )。
    concat()
    0
  • 的别名。
  • int
        
© www.soinside.com 2019 - 2024. All rights reserved.