Espacios vectoriales

Espacios vectoriales con Python

Los ocho axiomas que definen un espacio vectorial, los subespacios y su criterio de verificación, el span, y la distinción entre las tres orientaciones de un array unidimensional.

Las lecciones anteriores operaron sobre vectores y matrices dando por supuesto qué son. Este módulo establece la definición. No es un formalismo prescindible: es lo que explica por qué las mismas técnicas se aplican a listas de números, a polinomios y a las representaciones internas de un modelo de lenguaje.

La definición

Un espacio vectorial sobre R\mathbb{R} es un conjunto VV con dos operaciones, una suma V×VVV \times V \to V y un producto por escalar R×VV\mathbb{R} \times V \to V, que satisfacen ocho condiciones. Para todos u,v,wV\vec{u},\vec{v},\vec{w} \in V y λ,μR\lambda,\mu \in \mathbb{R}:

u+v=v+uconmutativa(u+v)+w=u+(v+w)asociativa0:v+0=velemento neutro(v):v+(v)=0elemento opuestoλ(u+v)=λu+λvdistributiva sobre vectores(λ+μ)v=λv+μvdistributiva sobre escalaresλ(μv)=(λμ)vasociativa del escalar1v=vescalar neutro\begin{aligned} &\vec{u} + \vec{v} = \vec{v} + \vec{u} && \text{conmutativa}\\ &(\vec{u} + \vec{v}) + \vec{w} = \vec{u} + (\vec{v} + \vec{w}) && \text{asociativa}\\ &\exists\, \vec{0} : \vec{v} + \vec{0} = \vec{v} && \text{elemento neutro}\\ &\exists\, (-\vec{v}) : \vec{v} + (-\vec{v}) = \vec{0} && \text{elemento opuesto}\\ &\lambda(\vec{u} + \vec{v}) = \lambda\vec{u} + \lambda\vec{v} && \text{distributiva sobre vectores}\\ &(\lambda + \mu)\vec{v} = \lambda\vec{v} + \mu\vec{v} && \text{distributiva sobre escalares}\\ &\lambda(\mu\vec{v}) = (\lambda\mu)\vec{v} && \text{asociativa del escalar}\\ &1 \cdot \vec{v} = \vec{v} && \text{escalar neutro} \end{aligned}

Los elementos de VV se llaman vectores, con independencia de su naturaleza. La definición no menciona flechas, listas ni coordenadas.

En V=RnV = \mathbb{R}^n, realizado en NumPy como un ndarray unidimensional, las ocho se heredan de las propiedades de los números reales y son comprobables directamente:

import numpy as np u = np.array([1., 2., 3.]) v = np.array([4., 5., 6.]) np.allclose(u + v, v + u) # conmutativa np.allclose(3 * (u + v), 3 * u + 3 * v) # distributiva np.allclose(u + np.zeros(3), u) # elemento neutro

Cerradura y vectorización

Las dos primeras condiciones de la definición, antes de los axiomas, son que la suma y el producto por escalar devuelvan elementos de VV. Esa propiedad se denomina cerradura, y en NumPy se manifiesta en la forma del resultado:

(u + v).shape == (3 * u).shape == u.shape # True

La consecuencia es de implementación, no solo de notación. Como el resultado pertenece al mismo espacio y tiene el mismo tipo y la misma forma, la operación puede compilarse en un único bucle tipado sobre un bloque contiguo de memoria, sin decidir nada en cada elemento. Eso es lo que se denomina vectorización, y es la razón de que u + v sea entre uno y dos órdenes de magnitud más rápido que el bucle equivalente en Python.

La cerradura algebraica y la eficiencia de la biblioteca no son hechos independientes: la segunda es posible porque se cumple la primera.

Combinaciones lineales y span

La operación central del álgebra lineal combina ambas: escalar varios vectores y sumarlos.

w=λ1v1++λkvk\vec{w} = \lambda_1\vec{v}_1 + \cdots + \lambda_k\vec{v}_k

Disponiendo los vectores como columnas de una matriz, la combinación es un producto matriz-vector:

V = np.column_stack([u, v]) # (3, 2) c = np.array([2., -1.]) V @ c # array([-2., -1., 0.]) = 2u − v

La identificación merece enunciarse con claridad: el producto VcV\vec{c} es la combinación lineal de las columnas de VV con coeficientes c\vec{c}. Es la lectura que convierte Ax=bA\vec{x} = \vec{b} en la pregunta de qué combinación de las columnas de AA produce b\vec{b}.

El conjunto de todas las combinaciones lineales posibles se denomina span:

span{v1,,vk}={iλivi  :  λiR}\operatorname{span}\{\vec{v}_1,\dots,\vec{v}_k\} = \Big\{\, \textstyle\sum_i \lambda_i\vec{v}_i \;:\; \lambda_i \in \mathbb{R} \,\Big\}

Aplicado a las columnas de una matriz, recibe el nombre de espacio columna, y es exactamente el conjunto de vectores b\vec{b} para los que Ax=bA\vec{x} = \vec{b} tiene solución.

Subespacios

Un subconjunto UVU \subseteq V es un subespacio si es a su vez un espacio vectorial con las mismas operaciones. No hace falta verificar los ocho axiomas: se heredan de VV. Basta comprobar tres condiciones:

  1. 0U\vec{0} \in U
  2. u,vUu+vU\vec{u},\vec{v} \in U \Rightarrow \vec{u} + \vec{v} \in U
  3. uU, λRλuU\vec{u} \in U,\ \lambda \in \mathbb{R} \Rightarrow \lambda\vec{u} \in U

La figura aplica el criterio a varios subconjuntos de R2\mathbb{R}^2:

  • contiene al 0
  • cerrado bajo la suma
  • cerrado bajo el producto por escalar

es un subespacio

Cada fallo ilustra una condición distinta. La recta desplazada no contiene al origen. El primer cuadrante lo contiene y es cerrado bajo la suma, pero multiplicar por 1-1 lo abandona. La parábola contiene al origen y falla en las otras dos: (1,1)(1,1) y (2,4)(2,4) pertenecen a ella y su suma (3,5)(3,5) no, y duplicar (1,1)(1,1) produce (2,2)(2,2), que tampoco.

Los subespacios de R2\mathbb{R}^2 son, en consecuencia, únicamente {0}\{\vec{0}\}, las rectas que pasan por el origen y R2\mathbb{R}^2 completo. Un subespacio contiene siempre al origen, y esa es la diferencia con el conjunto solución de Ax=bA\vec{x} = \vec{b} descrito en la lección sobre solución particular y general, que es afín salvo que b=0\vec{b} = \vec{0}.

Los subespacios ya aparecidos en el curso son instancias de esta definición: el núcleo de una matriz, su espacio columna y el espacio fila.

Las tres orientaciones de un array

En NumPy, un vector unidimensional de forma (n,) carece de orientación: no es ni fila ni columna. Para el álgebra matricial se le da forma explícita:

u.shape # (3,) sin orientación u.reshape(-1, 1).shape # (3, 1) columna u.reshape(1, -1).shape # (1, 3) fila

La distinción no es cosmética. Bajo las reglas de broadcasting descritas en la lección sobre matrices, combinar dos orientaciones distintas produce una matriz en lugar de un vector:

v + v

246

resultado (3) · elemento a elemento

operando izquierdo
operando derecho

Sumar una columna (3, 1) con una fila (1, 3) alinea las formas por la derecha, encuentra un 11 en cada posición y extiende ambas: el resultado es de forma (3, 3) y contiene todas las sumas cruzadas. La operación es legal y no produce ningún aviso, de modo que el error se manifiesta más adelante, en una dimensión que no encaja o en una pérdida silenciosamente mal calculada.

La comprobación de .shape antes de operar es el hábito que lo evita.

Espacios que no son ℝⁿ

La generalidad de la definición no es un detalle técnico. Cualquier conjunto cuyas operaciones satisfagan los ocho axiomas hereda toda la teoría posterior: independencia lineal, base, dimensión, transformaciones lineales.

EspacioElementosDimensión
Rm×n\mathbb{R}^{m\times n}matrices realesmnmn
Pk\mathcal{P}_kpolinomios de grado k\le kk+1k+1
C[a,b]\mathcal{C}[a,b]funciones continuas en [a,b][a,b]infinita
{x:Ax=0}\{\vec{x} : A\vec{x} = \vec{0}\}el núcleo de AAnrank(A)n - \operatorname{rank}(A)

En aprendizaje automático el espacio relevante es casi siempre Rd\mathbb{R}^d, pero la elección de dd y de la aplicación que lleva los datos hasta allí es donde se concentra el diseño. Un texto se representa como un punto de R768\mathbb{R}^{768}, una imagen como uno de Rd\mathbb{R}^{d} tras aplanar sus píxeles, un usuario como un vector de factores latentes.

Que esas representaciones vivan en un espacio vectorial es lo que autoriza las operaciones habituales sobre ellas. Promediar embeddings para representar un documento tiene sentido porque la suma y el producto por escalar están definidos y el resultado sigue perteneciendo al espacio. La lección sobre el producto por un escalar mostró la otra cara: la métrica que se emplee sobre ese espacio decide qué significa que dos representaciones sean próximas.


Ejercicio. Determinar cuáles de los siguientes subconjuntos de R3\mathbb{R}^3 son subespacios, aplicando las tres condiciones: los vectores con x1+x2+x3=0x_1 + x_2 + x_3 = 0; los vectores con x1+x2+x3=1x_1 + x_2 + x_3 = 1; los vectores con x1x2=0x_1 x_2 = 0. Verificar después la primera respuesta comprobando que null_space(np.ones((1, 3))) tiene la dimensión esperada.