Los tensores son objetos matemáticos que, dentro del aprendizaje automático, se utilizan para representar datos numéricos en arreglos. Los tensores se caracterizan por dos elementos:
Dentro de los tensores, los elementos más comúnes son los siguientes:
Rango | Nombre | Notación |
---|---|---|
0 | Escalar | $\lambda$ |
1 | Vector | $x_i$ |
2 | Matriz | $A_{i,j}$ |
3 | Tensor | $T_{i,j,k}$ |
Numpy nos permite manejar tensores de diferente rango. En este caso, los trata como arreglos (arrays) numéricos. Por lo que para crear un tensor usamos la función np.array(). A partir de esta función podemos generar diferentes tipos de tensores.
Un vector es un tensor de rango 1, es decir, tiene un sólo índice:
$$x = x_i$$Cada entrada $x_i$ del vector contiene información sobre algún dato, por ejemplo un atributo.
import numpy as np
#Tensor de rango 1
x = np.array([2,3,3])
print(x)
[2 3 3]
Los vectores, tanto en 2 como en 3 dimensiones, son visualizables como flechas en espacios de 2 o 3 dimensiones.
import matplotlib.pyplot as plt
fig = plt.figure(figsize=(3,3))
ax = fig.add_subplot(111, projection='3d')
ax.quiver(0, 0, 0, x[0], x[1], x[2])
ax.set_xlim([0, 3])
ax.set_ylim([0, 3])
ax.set_zlim([0, 3])
plt.xlabel('$x_0$')
plt.ylabel('$x_1$')
ax.set_zlabel('$x_2$')
#ax.view_init(30,45)
plt.show()
Una matriz cuenta con dos índices siendo un tensor de rango 2:
$$A = A_{i,j}$$Se puede representar como tabla, el primer índice hace referencia a los renglones y el segundo a las columnas:
#Tensor de rango 2 (matriz)
A = np.array([[0, 5, 1],
[3, 0, 2],
[0, 2, 0]])
print(A)
[[0 5 1] [3 0 2] [0 2 0]]
Se pueden crear tensores con un rango n mayor a 2; en este caso se manejarán n índices. Por ejemplo un tensor de rango 3 maneja 3 índices. Como se puede observar, el tensor se visualiza como si constara de dos matrices.
#Tensor de rango 3
T = np.array([[[0, 5, 1],
[3, 0, 2],
[0, 2, 0]],
[[2, 1, 10],
[5, 4, 9],
[1, 0, 0.5]]])
print(T)
[[[ 0. 5. 1. ] [ 3. 0. 2. ] [ 0. 2. 0. ]] [[ 2. 1. 10. ] [ 5. 4. 9. ] [ 1. 0. 0.5]]]
Paqueterías que serán de suma importancia para el desarrollo de redes neuronales son PyTorch y Tensorflow. En tanto las redes neuronales trabajan con tensores, estas librerías permiten el manejo de diferentes tipos de tensores, de diferente rango. En general, estas librerias crean tensores con diferentes funciones:
import torch
import tensorflow as tf
x = torch.tensor([2,3,3])
x_tf = tf.constant([2,3,3])
print(x)
print(x_tf)
tensor([2, 3, 3]) tf.Tensor([2 3 3], shape=(3,), dtype=int32)
A = torch.tensor([[0, 5, 1],
[3, 0, 2],
[0, 2, 0]])
A_tf = tf.constant([[0, 5, 1],
[3, 0, 2],
[0, 2, 0]])
print(A)
print(A_tf)
tensor([[0, 5, 1], [3, 0, 2], [0, 2, 0]]) tf.Tensor( [[0 5 1] [3 0 2] [0 2 0]], shape=(3, 3), dtype=int32)
T = torch.tensor([[[0, 5, 1],
[3, 0, 2],
[0, 2, 0]],
[[2, 1, 10],
[5, 4, 9],
[1, 0, 0.5]]])
T_tf = tf.constant([[[0, 5, 1],
[3, 0, 2],
[0, 2, 0]],
[[2, 1, 10],
[5, 4, 9],
[1, 0, 0.5]]])
print(T)
print(T_tf)
tensor([[[ 0.0000, 5.0000, 1.0000], [ 3.0000, 0.0000, 2.0000], [ 0.0000, 2.0000, 0.0000]], [[ 2.0000, 1.0000, 10.0000], [ 5.0000, 4.0000, 9.0000], [ 1.0000, 0.0000, 0.5000]]]) tf.Tensor( [[[ 0. 5. 1. ] [ 3. 0. 2. ] [ 0. 2. 0. ]] [[ 2. 1. 10. ] [ 5. 4. 9. ] [ 1. 0. 0.5]]], shape=(2, 3, 3), dtype=float32)
Las dimensiones de los tensores es el número de datos númericos que contiene por cada índice. Por ejemplo, una matriz de $2 \times 3$ nos dice que tiene 2 renglones, es decir que el índice de los renglones son dos. Mientras que tiene 3 índices para las columnas.
Por medios de las paqueterías que estamos revisando, podemos ver las dimensiones que tienen los tensores que hemos creado. En cada paquetería se usan diferentes funciones:
print('Tensor {} tiene dimensiones: {}'.format(x, x.size()))
print('Tensor {} tiene dimensiones: {}'.format(A, A.size()))
print('Tensor {} tiene dimensiones: {}'.format(T, T.size()))
Tensor tensor([2, 3, 3]) tiene dimensiones: torch.Size([3]) Tensor tensor([[0, 5, 1], [3, 0, 2], [0, 2, 0]]) tiene dimensiones: torch.Size([3, 3]) Tensor tensor([[[ 0.0000, 5.0000, 1.0000], [ 3.0000, 0.0000, 2.0000], [ 0.0000, 2.0000, 0.0000]], [[ 2.0000, 1.0000, 10.0000], [ 5.0000, 4.0000, 9.0000], [ 1.0000, 0.0000, 0.5000]]]) tiene dimensiones: torch.Size([2, 3, 3])
Acceder a una entrada del tensor implica obtener el dato numérico que se encuentra en el correspondiente índice de ese tensor. Es decir, es obtener el valor $T_{i_1, i_2,...,i_n}$ del tensor.
Por ejemplo, en una matriz queremos ver que valor numérico se encuentra en $A_{1,2}$ es decir el la primera columna y el renglón 2. Para esto, generalmente se usa la notación:
$$T[i_1, i_2, ...,i_n]$$Con valores específicos para cada índice. Vemos unos ejemplos:
print(A[1,2])
print(T[1,1,2])
tensor(2) tensor(9.)