Convierta Numpy Array a Tensor y Tensor a Numpy Array con PyTorch

En esta breve guía, aprenda cómo convertir una matriz Numpy en un tensor PyTorch y cómo convertir un tensor PyTorch en una matriz Numpy. ¡Trate con los tensores de CPU y GPU y evite las excepciones de conversión!

Los tensores son objetos multidimensionales y el bloque de representación de datos esencial de los marcos de aprendizaje profundo, como Tensorflow y PyTorch.

Un escalar tiene cero dimensiones, un vector tiene una dimensión, una matriz tiene dos dimensiones y los tensores tienen tres o más. En la práctica, a menudo nos referimos a escalares y vectores y matrices como tensores también por conveniencia.

{.icon aria-hidden=“true”}

Nota: Un tensor también puede ser cualquier matriz n-dimensional, al igual que una matriz Numpy. Muchos marcos tienen soporte para trabajar con matrices Numpy, y muchos de ellos están construidos sobre Numpy, por lo que la integración es natural y eficiente.

Sin embargo, un torch.Tensor tiene más capacidades integradas que las matrices Numpy, y estas capacidades están orientadas a las aplicaciones de aprendizaje profundo (como la aceleración de GPU), por lo que tiene sentido preferir las instancias de torch.Tensor a las Numpy normales. matrices cuando se trabaja con PyTorch. Además, torch.Tensors tiene una API muy parecida a Numpy, ¡lo que lo hace intuitivo para la mayoría con experiencia previa!

En esta guía, aprenda cómo convertir entre Numpy Array y PyTorch Tensors.

Convertir matriz Numpy en tensor PyTorch

Para convertir una matriz Numpy en un tensor PyTorch, tenemos dos enfoques distintos que podemos tomar: usando la función from_numpy(), o simplemente proporcionando la matriz Numpy al constructor torch.Tensor() o usando la función tensor():

1
2
3
4
5
6
7
8
9
import torch
import numpy as np

np_array = np.array([5, 7, 1, 2, 4, 4])

# Convert Numpy array to torch.Tensor
tensor_a = torch.from_numpy(np_array)
tensor_b = torch.Tensor(np_array)
tensor_c = torch.tensor(np_array)

Entonces, ¿cuál es la diferencia? ¡Las funciones from_numpy() y tensor() son conscientes de dtype! Dado que hemos creado una matriz Numpy de enteros, el dtype de los elementos subyacentes será, naturalmente, int32:

1
2
print(np_array.dtype)
# dtype('int32')

Si tuviéramos que imprimir nuestros dos tensores:

1
print(f'tensor_a: {tensor_a}\ntensor_b: {tensor_b}\ntensor_c: {tensor_c}')

tensor_a y tensor_c conservan el tipo de datos utilizado dentro de np_array, convertidos en la variante de PyTorch (torch.int32), mientras que tensor_b asigna automáticamente los valores a floats:

1
2
3
tensor_a: tensor([5, 7, 1, 2, 4, 4], dtype=torch.int32)
tensor_b: tensor([5., 7., 1., 2., 4., 4.])
tensor_c: tensor([5, 7, 1, 2, 4, 4], dtype=torch.int32)

Esto también se puede observar comprobando sus campos dtype:

1
2
3
print(tensor_a.dtype) # torch.int32
print(tensor_b.dtype) # torch.float32
print(tensor_c.dtype) # torch.int32

Numpy Array a PyTorch Tensor con dtype

Estos enfoques también difieren en si puede establecer explícitamente el dtype deseado al crear el tensor. from_numpy() y Tensor() no aceptan un argumento dtype, mientras que tensor() sí:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Retains Numpy dtype
tensor_a = torch.from_numpy(np_array)
# Creates tensor with float32 dtype
tensor_b = torch.Tensor(np_array)
# Retains Numpy dtype OR creates tensor with specified dtype
tensor_c = torch.tensor(np_array, dtype=torch.int32)

print(tensor_a.dtype) # torch.int32
print(tensor_b.dtype) # torch.float32
print(tensor_c.dtype) # torch.int32

Naturalmente, puede emitir cualquiera de ellos muy fácilmente, usando exactamente la misma sintaxis, lo que le permite establecer el dtype después de la creación también, por lo que la aceptación de un argumento dtype no es una limitación, pero más de una conveniencia:

1
2
3
4
5
6
7
tensor_a = tensor_a.float()
tensor_b = tensor_b.float()
tensor_c = tensor_c.float()

print(tensor_a.dtype) # torch.float32
print(tensor_b.dtype) # torch.float32
print(tensor_c.dtype) # torch.float32

Convertir PyTorch Tensor a Numpy Array

Convertir un PyTorch Tensor en una matriz Numpy es sencillo, ya que los tensores se construyen en última instancia sobre las matrices Numpy, y todo lo que tenemos que hacer es "exponer" la estructura de datos subyacente.

Dado que PyTorch puede optimizar los cálculos realizados en los datos basados ​​en su hardware, existen algunas advertencias:

1
2
3
4
5
tensor = torch.tensor([1, 2, 3, 4, 5])

np_a = tensor.numpy()
np_b = tensor.detach().numpy()
np_c = tensor.detach().cpu().numpy()

Entonces, ¿por qué usar detach() y cpu() antes de exponer la estructura de datos subyacente con numpy(), y cuándo debería desconectar y transferir a una CPU?

CPU PyTorch Tensor -> CPU Numpy Array

Si su tensor está en la CPU, donde también estará la nueva matriz Numpy, está bien exponer la estructura de datos:

1
2
np_a = tensor.numpy()
# array([1, 2, 3, 4, 5], dtype=int64)

Esto funciona muy bien, y tienes una matriz Numpy limpia.

CPU PyTorch Tensor with Gradients -> CPU Numpy Array

Sin embargo, si su tensor requiere que calcule gradientes para él también (es decir, el argumento requires_grad está establecido en True), este enfoque ya no funcionará. Tendrás que separar la matriz subyacente del tensor y, al separar, eliminarás los gradientes:

1
2
3
4
5
6
tensor = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float32, requires_grad=True)

np_a = tensor.numpy()
# RuntimeError: Can't call numpy() on Tensor that requires grad. Use tensor.detach().numpy() instead.
np_b = tensor.detach().numpy()
# array([1., 2., 3., 4., 5.], dtype=float32)

GPU PyTorch Tensor -> CPU Numpy Array

Finalmente, si ha creado su tensor en la GPU, vale la pena recordar que las matrices Numpy normales no admiten la aceleración de la GPU. ¡Residen en la CPU! Tendrás que transferir el tensor a una CPU, y luego separar/exponer la estructura de datos.

{.icon aria-hidden=“true”}

Nota: Esto se puede hacer a través de las funciones to('cpu') o cpu() - son funcionalmente equivalentes.

Esto debe hacerse de forma explícita, porque si se hiciera automáticamente, la conversión entre tensores de CPU y CUDA en arreglos sería diferente bajo el capó, lo que podría generar errores inesperados en el futuro.

PyTorch es bastante explícito, por lo que este tipo de conversión automática se evitó a propósito:

1
2
3
4
5
6
7
# Create tensor on the GPU
tensor = torch.tensor([1, 2, 3, 4, 5], dtype=torch.float32, requires_grad=True).cuda()

np_b = tensor.detach().numpy()
# TypeError: can't convert cuda:0 device type tensor to numpy. Use Tensor.cpu() to copy the tensor to host memory first.
np_c = tensor.detach().cpu().numpy()
# array([1., 2., 3., 4., 5.], dtype=float32)

{.icon aria-hidden=“true”}

Nota: Se recomienda enfáticamente llamar a detach() antes de cpu(), para eliminar los gradientes antes de transferir a la CPU. Los gradientes no importarán de todos modos después de la llamada detach(), por lo que copiarlos en cualquier punto es totalmente redundante e ineficiente. Es mejor "cortar el peso muerto" lo antes posible.

En términos generales, este enfoque es el más seguro, ya que no importa con qué tipo de tensor esté trabajando, no fallará. Si tiene un tensor de CPU e intenta enviarlo a la CPU, no sucede nada. Si tiene un tensor sin gradientes e intenta separarlo, no pasa nada. En el otro extremo del palo, se lanzan excepciones.

Conclusión

En esta guía, echamos un vistazo a lo que son los tensores PyTorch, antes de sumergirnos en cómo convertir una matriz Numpy en un tensor PyTorch. Finalmente, hemos explorado cómo los tensores de PyTorch pueden exponer la matriz Numpy subyacente, y en qué casos tendría que realizar transferencias y recortes adicionales. es.