Introducción a tensores
Contents
Introducción a tensores#
Introducción#
En esta lección se introducen los conceptos de vectores, matrices y tensores. Los tensores son la estructura de datos más utilizada en el aprendizaje profundo. Desde el punto de vista matemático, un tensor generaliza los conceptos de escalares, vectores y matrices.
Solamente haremos la introducción al concepto de tensores desde el punto de vista de las estructuras de datos requeridas en el aprendizaje profundo.
Los tensores constituyen el pilar fundamental de las estructuras de datos utilizadas en la inteligencia artificial (IA). Motores de aprendizaje profundo como Torch o TensorFlow se basan en Numpy, la librería por excelencia de Python para el manejo de tensores. Recientemente TensorFlow anunció el desarrollo propio de su librería de tensores en un intento de liberarse de Numpy.
Desde el punto vista del aprendizaje de máquinas, los tensores con arreglos multidimensionales que generalizan las matrices a múltiples y a los cuales se les agregan algunas características adicionales como la diferenciación automática, que desarrollaremos en otra lección.
Dimensión y tamaño de un tensor#
Los tensores pueden ser estructuras desde muy simples hasta muy complejos. Para poder describirlos, usamos los términos dimensión y tamaño. La siguiente imagen ilustra diferentes tipos de tensores.
La dimensión de un tensor
se define como el número de índices requeridos para indicar la posición de un elemento en el tensor. En la imagen, es escalar es un tensor de dimensión cero, porque no se requiere ningún índice para determinar la posición de su único elemento. Cada índice requerido para indicar la posición de un elemento determina una dimensión del tensor. Así en ejemplo, de arriba el escalar tiene dimensión cero, el vector dimensión 1, la matriz es de dimensión 2 y el último objeto a la derecha es un tensor de de dimensión 4.
El tamaño de un tensor
corresponde al número de elementos que se tienen al largo de cada dimensión. En la imagen el vector tiene tamaño 2 , indicado (2,) por acuerdo de notación. La matriz tiene tamaño (2,3) y el tensor de la derecha tiene tamaño (1,4,1,2).
Para entender porque este último tensor tiene dimensión cuatro, observe lo siguiente. Hay un contenedor externo. Al representar el tensor en esta forma gráfica se da a entender que esta es la primera capa de varias posibles con la misma estructura. Al interior hay 4 compartimientos de tamaño (1,2), completando así el tamaño (1,4,1,2). ()
La siguiente ilustra una abstracción de juguete de un video con tres cuadros(frames) de color de forma (shape) \((2, 2)\) cada uno. Este seguro de entender la lógica de organización del tensor.
Fuente: Alvaro Montenegro
La próxima imagen muestra la organización de diferentes tipos de tensores.
Fuente: A. Montenegro
Nota acalaratoria#
El concepto de tensor en inteligencia artificial difiere del conocido en matemáticas y física. Un tensor desde el punto de vista matemático es a menudo, representado por una matriz de componentes que describen funciones relevantes para las coordenadas de un espacio. En pocas palabras, un tensor es una matriz de números que se transforman de acuerdo con ciertas reglas bajo un cambio de coordenadas. Un tensor se puede definir como un solo punto, una colección de puntos aislados, o se puede definir como un continuo de puntos en los que los elementos del tensor son funciones de posición, y el tensor forma lo que se conoce como campo tensor. Esto significa que en lugar de estar representado por un solo punto, el tensor se define en múltiples puntos juntos en un espacio.
Fuente: Wikipedia.
El lector interesado en el concepto matemático , puede consultar en Wikipedia.
Adicionalmente la dimensión de un tensor es un concepto algebraico y no geométrico. En geometría cada componente en un vector representa una dimensión. Entonces el vector \((3,4)\) es un vector bidimensional desde el punto de vista geométrico. Gráficamente lo representamos en un plano como se muestra en la siguiente imagen. Desde el punto de vista tensorial, el mismo objeto es unidimensional, debido a que solamente se necesita un índice para identificar cada uno de sus elementos. Esta la definición que nos interesa aquí. El siguiente código dibuja algunos vectores en \(\mathbb{R}^2\). Los matemáticos dicen que estos objetos geométricos tienen dimensión geométrica 2. Por favor revisa cada línea del código para estar seguro que lo entiende.
Por favor revisa cada línea del siguiente código para estar asegurarse que lo entiende. Cualquier duda consulte la documentación de Python. ax.quiver
crea el gráfico de los vectores, a partir de las coordenadas en \(X, Y, U, V\).
import numpy as np
import matplotlib.pyplot as plt
soa = np.array([[0, 0, 4, 1], [0, 0, 1, 5], [0, 0, 3, 2]])
X, Y, U, V = zip(*soa)
plt.figure()
plt.title('Vectores en el espacio Euclideano $R^2$ ')
ax = plt.gca()
ax.quiver(X, Y, U, V, angles='xy', scale_units='xy', scale=1, color='blue')
ax.set_xlim([-1, 6])
ax.set_ylim([-1, 6])
plt.draw()
plt.show()
Escalares (Tensores de dimensión cero)#
Un tensor de dimensión cero representa una cantidad escalar. Es decir, un tensor de dimensión cero solamente contendrá un número y por tanto tiene tamaño 1.
Vectores (Tensores unidimensionales)#
En esta sección revisamos el concepto de vector. Desde el punto de vista del aprendizaje profundo entendemos un vector como un contenedor de n datos, cada uno de los cuales se identifica genéricamente mediante un índice. Por ejemplo supongamos que \(w\) es un vector de tamaño tres. Este vector se representa genéricamente como:
En estadística es usual escribir los vectores en columna. En este caso \(w\) se escribe como:
El tipo de valores que puede contener un vector debe ser de la misma clase por convención. Por ejemplo, si \(w\) es un vector de números reales, entonces \(z=(3.2\;, 1.5 \;, -7.2 \;, 0.0)\) es un vector real de tamaño cuatro. Matemáticamente se dice que el vector \(z\) tiene dimensión cuatro. En otras palabras, la dimensión matemática de un vector es su tamaño.
El contenido y tipo de datos de un vector depende del contexto en que se está utilizando. Supongamos que se trata de construir una máquina de aprendizaje profundo que identifique dígitos escritos a mano en tonalidades de gris. Lo que se acostumbra a hacer es digitalizar las imágenes correspondientes, con lo cual se obtiene una matriz de números de tamaño digamos \(255 \times 255\). Esta matriz puede facilmente transformarse a un vector, o mejor diríamos un tensor de dimensión 1, con \(N = 255^2\) componentes.
Ejemplo en NumPy#
En NumPy el vector \(w =(1,2,3)\) se puede crear así:
import numpy as np
# crea el vector (array)
w = np.array([1,2,3])
# lo imprime
print(w)
# Muestra el tamaño (shape) del vector
print(w.shape)
[1 2 3]
(3,)
Discusión: ¿Vector o Tensor?#
Como se dijo arriba, En geometría la dimensión hace referencia al número de componentes con el que se representa un objeto en un espacio. Por lo general usamos el espacio Euclidiano, digamos \(\mathbb{R}^2\) o \(\mathbb{R}^3\).
Los tensores son objetos de tipo algebraico. La dimensión de un tensor se define como el número de índices requerido para representar todos a los elementos del tensor.
Entonces:
El vector \(w = (w_1,w_2, w_3)\) tiene dimensión geométrica 3.
El tensor \(w = (w_1,w_2, w_3)\) tiene dimensión 1 y tamaño (shape = 3).
Asegúrese de comprender la diferencia.
El tipo de valores que puede contener un vector debe ser de la misma clase por convención. Por ejemplo, si \(w\) es un vector de números reales, entonces \(z=(3.2\;, 1.5 \;, -7.2 \;, 0.0)\) es un vector real de tamaño cuatro. Matemáticamente se dice que el vector \(z\) tiene dimensión cuatro. En otras palabras, la dimensión matemática de un vector es su tamaño.
El contenido y tipo de datos de un vector depende del contexto en que se está utilizando. Supongamos que se trata de construir una máquina de aprendizaje profundo que identifique dígitos escritos a mano en tonalidades de gris. Lo que se acostumbra a hacer es digitalizar las imágenes correspondientes, con lo cual se obtiene una matriz de números de tamaño digamos \(255 \times 255\). Esta matriz puede fácilmente transformarse a un vector, o mejor diríamos un tensor de dimensión 1, con \(N = 255^2\) componentes.
Aritmética básica de tensores unidimensionales#
Mientras no se diga lo contrario, asumiremos que los tensores que usaremos tienen el mismo tamaño. Por facilidad, en las definiciones usaremos tensores unidimensionales de tamaño \(n=3\). En realidad el tamaño de los tensores unidimensionales pueden ser cualquier número entero positivo \(n\) y las definiciones se generalizan de forma obvia.
Supongamos que \(a= (a_1,a_2,a_3)\) y \(b=(b_1,b_2,b_3)\) son dos vectores. La suma entre \(a\) y \(b\) es un vector \(c\) definido por:
Por ejemplo, con Numpy escribimos:
a = np.array([1,2,3])
b = np.array([7,8,9])
c = a + b
print(c)
[ 8 10 12]
Similarmente la diferencia de vectors \(a-b\) es definida por:
Con Numpy operamos así:
a = np.array([1,2,3])
b = np.array([7,8,9])
c = a - b
print(c)
[-6 -6 -6]
El producto de Hadamard, o producto elemento por elemento entre dos vectores se denota \(a \odot b\) y se define como:
En Python el producto de Hadamard se implementa simplemente usando el operador de multiplicación ( * ). Veamos:
a = np.array([1,2,3])
b = np.array([7,8,9])
c = a * b
print(c)
[ 7 16 27]
La división entre vectores no es una operación formalmente definida. En ocasiones sin embargo se requiere dividir los elementos de un vector entre los elementos de otro, elemento a elemento. Esta operación se implementa en Python simplemente usando el operador división (/).
a = np.array([1,2,3])
b = np.array([7,8,9])
c = a / b
print(c)
[0.14285714 0.25 0.33333333]
Matrices (Tensores bidimensionales)#
Una matriz es una organización (tensor) bidimensional de objetos del mismo tipo. Por ejemplo una matriz \(M\) de tamaño \(2\times 3\) puede ser:
Las matrices son muy utilizadas en prácticamente todas las áreas de la ciencia y la tecnología.
En el caso del aprendizaje profundo, y más generalmente en Estadística las matrices se usan para representar conjuntos de datos. En los casos de regresión, las filas usualmente representan individuos y las columnas variables.
En adelante llamaremos a las matrices tensores bidimensionales (o de dos dimensiones). Entonces una matriz que tiene \(m\) filas y \(n\) columnas, es un tensor bidimensional de tamaño (shape) \(=(m,n)\).
El tensor \(M\) se representa en NumPy de la siguiente forma:
import numpy as np
# Crea el tensor
M = np.array([[1,2,3],
[4,5,6]])
# Imprime el Tensor
print(M)
# Muestra la forma (shape)
M.shape
[[1 2 3]
[4 5 6]]
(2, 3)
Creación de algunos tensores bidimensionales#
Tensor vacío#
La función empty() crea un arreglo de la forma especificada.
v = np.empty([2,3])
print(v)
print(v.shape)
[[0.40208333 0.33611111 0.40208333]
[3.35611111 5.05208333 0.33611111]]
(2, 3)
La salida puede parecer un poco extraña. En realidad lo que hace Numpy es reservar el espacio de memoria necesario para mantener un arreglo de tamaño \((2, 3)\), asignarle el nombre v en la tabla de objetos definidos dinámicamente y nada más. Entonces la información que se encontraba es ese lugar no se modifica. De hecho si volvemos a correr en seguida la celda anterior de código, la salida no debe cambiar.
Observe que v es un tensor que tiene forma (shape) \(2\times2\). En NumPy un tensor puede estar compuesto por uno o más tensores. La dimensión del tensor v es 2. Se requiere un objeto de doble entrada para representar un tensor bidimensional. Dese cuenta que al arreglo es mostrado como una lista con dos elementos, cada uno de los cuales es un arreglo de tamaño 3. Internamente, una arreglos no se almacena como una lista.
Tensor de ceros#
La función zeros() crea un arreglo de la forma especificada relleno de ceros. Por ejemplo
w = np.zeros([3,2])
print(w)
[[0. 0.]
[0. 0.]
[0. 0.]]
Arreglo de unos#
La función ones() crea un arreglo de la forma especificada relleno de unos.
w = np.ones([2,2])
print(w)
[[1. 1.]
[1. 1.]]
Apilamiento de arreglos (stack)#
Vertical: vstack
#
Con la función vstack(). Apila a lo largo del eje 0.
v = np.ones([2,3])
w = np.array([2,2,2])
z = np.vstack((v,w))
print('v = {}\n'.format(v))
print('w = {}\n'.format(w))
print('z = {}\n'.format(z))
print('tamaño final = {}'.format(z.shape))
v = [[1. 1. 1.]
[1. 1. 1.]]
w = [2 2 2]
z = [[1. 1. 1.]
[1. 1. 1.]
[2. 2. 2.]]
tamaño final = (3, 3)
Horizontal: hstack
#
Con la función hstack(). Apila a lo largo del eje 1.
v = np.ones([2,3])
w = np.array([[5],[5]])
z = np.hstack((v,w))
print('v = {}\n'.format(v))
print('w = {}\n'.format(w))
print('z = {}\n'.format(z))
print('tamaño final = {}'.format(z.shape))
v = [[1. 1. 1.]
[1. 1. 1.]]
w = [[5]
[5]]
z = [[1. 1. 1. 5.]
[1. 1. 1. 5.]]
tamaño final = (2, 4)
Apilamiento a lo largo de cualquier eje. stack
#
Con la función stack(). Apila a lo largo cualquier eje. La función stack() puede usarse para los dos casos anteriores cambiando el eje a lo largo del cual se hace la apilación. Revise el siguiente ejemplo en el cual apilamos una lista de arreglos a lo largo de cada uno de los ejes.
Generamos una lista de 3 arreglos, cada uno de forma \((3, 4)\)
import numpy as np
arrays = [np.random.randn(3, 4) for _ in range(3)]
print('Longitud de arrays =', len(arrays))
print('arrays[0] = \n', arrays[0])
print('arrays[1] = \n', arrays[1])
print('arrays[1] = \n', arrays[2])
Longitud de arrays = 3
arrays[0] =
[[ 0.8448297 -1.01559939 0.57498071 0.64038646]
[-1.56685489 -0.10273499 0.40506054 -1.83520697]
[-0.50410026 -0.44892294 -0.42463926 0.72057719]]
arrays[1] =
[[-1.25340753 0.04637402 -0.03301552 0.3628957 ]
[ 0.65879705 -0.45332522 -0.79593583 -0.10302451]
[-0.71806263 0.24412283 -1.183287 -0.90287282]]
arrays[1] =
[[-1.14402716 -1.01879902 0.20990921 0.06749962]
[ 0.35731967 0.76010511 0.87766142 -0.38463828]
[-0.91435576 -0.03921191 1.86436018 -1.62978027]]
np.stack(arrays, axis=0)
array([[[ 0.8448297 , -1.01559939, 0.57498071, 0.64038646],
[-1.56685489, -0.10273499, 0.40506054, -1.83520697],
[-0.50410026, -0.44892294, -0.42463926, 0.72057719]],
[[-1.25340753, 0.04637402, -0.03301552, 0.3628957 ],
[ 0.65879705, -0.45332522, -0.79593583, -0.10302451],
[-0.71806263, 0.24412283, -1.183287 , -0.90287282]],
[[-1.14402716, -1.01879902, 0.20990921, 0.06749962],
[ 0.35731967, 0.76010511, 0.87766142, -0.38463828],
[-0.91435576, -0.03921191, 1.86436018, -1.62978027]]])
A lo largo del eje 1.
Input In [15]
A lo largo del eje 1.
^
SyntaxError: invalid syntax
Creación de tensores multidimensionales#
En Python una lista de listas puede crearse como se muestra en el siguiente fragmento de código. En el código se observa como acceder a la primera lista y como acceder al segundo elemento de la segunda lista. Asegúrese de entender la lógica involucrada.
a = np.array([[1,2,3],[4,5,6]])
print('a=',a)
print()
print('a[0]=',a[0])
print()
print('a[1][1]=',a[1][1])
a= [[1 2 3]
[4 5 6]]
a[0]= [1 2 3]
a[1][1]= 5
Un ejemplo con arreglos tridimensionales#
Para ayudar a entender la indexación y rebanado de arreglos, observe el siguiente ejemplo. Asegúrese de entender completamente la lógica. Vamos a crear un tensor tridimensional de tamaño \((4, 3, 1)\).
a = np.array([
[[1,2],
[3,4],
[5,6]],
[[7,8],
[9,10],
[11,12]],
[[13,14],
[15,16],
[17,18]],
[[19,20],
[21,22],
[23,24]]
])
print('a.shape=', a.shape)
print('\na =',a)
a.shape= (4, 3, 2)
a = [[[ 1 2]
[ 3 4]
[ 5 6]]
[[ 7 8]
[ 9 10]
[11 12]]
[[13 14]
[15 16]
[17 18]]
[[19 20]
[21 22]
[23 24]]]
Ejercicios#
Investigue sobre los siguientes temas:
Indexación de tensores NumPy.
Rebanado (slicing) tensores.
Reorganización (Reshape) de tensores.
Ayuda
Revise la lección de Numpy del libro de Fundamentos de Programación
Ejercicios adicionales#
Cree una lista 5 de arreglos aleatorios de forma \((2,4,3,5)\).
Apile los arreglos por cada uno de los cuatro ejes.
Interprete los resultados.