gulzar
El ejemplo REINFORCE y actor-crítico de OpenAI para el aprendizaje por refuerzo tiene el siguiente código:
policy_loss = torch.cat(policy_loss).sum()
loss = torch.stack(policy_losses).sum() + torch.stack(value_losses).sum()
uno esta usando torch.cat
los otros usos torch.stack
para casos de uso similares.
Según tengo entendido, el documento no ofrece una distinción clara entre ellos.
Me encantaría saber las diferencias entre las funciones.
Jatentaki
Concatena una secuencia de tensores a lo largo de un Nueva dimensión.
Concatena la secuencia dada de tensores seq en la dimensión dada.
Así que si A
y B
son de forma (3, 4):
torch.cat([A, B], dim=0)
será de forma (6, 4)torch.stack([A, B], dim=0)
será de forma (2, 3, 4)
-
Por lo tanto, torch.stack([A,B],dim = 0) es equivalente a torch.cat([A.unsqueeze(0),b.unsqueeze(0)],dim = 0) . Entonces, si se encuentra realizando muchas operaciones de descomprimir() antes de combinar tensores, es probable que pueda simplificar su código usando stack().
– DerekG
6 de abril de 2020 a las 16:46
-
Solo para complementar, en los ejemplos de OpenAI en la pregunta,
torch.stack
ytorch.cat
se puede usar indistintamente en cualquier línea de código ya quetorch.stack(tensors).sum() == torch.cat(tensors).sum()
.– usuario118967
6 de agosto de 2020 a las 1:31
iacob
t1 = torch.tensor([[1, 2],
[3, 4]])
t2 = torch.tensor([[5, 6],
[7, 8]])
torch.stack |
torch.cat |
---|---|
‘Pilas’ una secuencia de tensores a lo largo de una nueva dimensión: | ‘Estafagatogenera una secuencia de tensores a lo largo de una dimensión existente: |
Estas funciones son análogas a numpy.stack
y numpy.concatenate
.
charlie parker
La respuesta original carece de un buen ejemplo que sea autónomo, así que aquí va:
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')
producción:
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"
como referencia aquí están las definiciones:
cat: Concatena la secuencia dada de tensores seq en la dimensión dada. La consecuencia es que una dimensión específica cambia de tamaño, por ejemplo, dim=0, entonces está agregando elementos a la fila, lo que aumenta la dimensionalidad del espacio de la columna.
stack: Concatena una secuencia de tensores a lo largo de una nueva dimensión. Me gusta pensar en esto como la operación de “agregar” de la antorcha, ya que puede indexar/obtener su tensor original al “abrirlo” desde el frente. Sin argumentos, agrega tensores al frente del tensor.
Relacionado:
- aquí está el enlace del foro de pytorch con discusiones sobre esto: https://discuss.pytorch.org/t/best-way-to-convert-a-list-to-a-tensor/59949/8 Aunque deseo eso
tensor.torch
convierte una lista anidada de tensores en un gran tensor con muchas dimensiones que respetan la profundidad de la lista anidada.
Actualización: con lista anidada del mismo tamaño
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
aquí hay algunas pruebas unitarias (no escribí más pruebas, pero funcionó con mi código real, así que confío en que está bien. No dude en ayudarme agregando más pruebas si lo desea):
def test_tensorify():
t = [1, 2, 3]
print(tensorify
tt = [t, t, t]
print(tensorify(tt))
ttt = [tt, tt, tt]
print(tensorify(ttt))
if __name__ == '__main__':
test_tensorify()
print('Done\a')
Si alguien está investigando los aspectos de rendimiento de esto, he hecho un pequeño experimento. En mi caso, necesitaba convertir una lista de tensores escalares en un solo tensor.
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)
Mi conclusión es que incluso si quieres tener la dimensión adicional de torch.stack
usando torch.cat
y entonces reshape
es mejor.
Nota: esta publicación está tomada del foro de PyTorch (soy el autor de la publicación original)
si está interesado en listas anidadas de longitud variable para tensor, aquí hay enlaces que parecen tener una solución: stackoverflow.com/questions/55050717/… y discutir.pytorch.org/t/…
–Charlie Parker
9 de febrero de 2021 a las 20:04