Algoritmo de Descomposición SVD: teoría, implementación y ejemplos en Python
Introducción
La descomposición en valores singulares (Singular Value Decomposition, SVD) es una herramienta fundamental en álgebra lineal que permite factorizar cualquier matriz real o compleja \(A\) en el producto de tres matrices:
A = U Σ VT
Donde:
U– matriz ortogonal de dimensiones \(m \times m\) cuyas columnas son los vectores singulares izquierdos.Σ– matriz diagonal \(m \times n\) con los valores singulares (no negativos) ordenados de mayor a menor.V– matriz ortogonal de dimensiones \(n \times n\) cuyas columnas son los vectores singulares derechos (transpuesta en la fórmula).
Esta factorización es única (exceptuando signos) y sirve como base para PCA, compresión de imágenes, recomendación de sistemas, filtrado de ruido y más.
Fundamento matemático
El cálculo de la SVD se basa en la relación entre la matriz original y sus matrices de covarianza:
- \(A^T A = V Σ^2 V^T\) – los valores singulares al cuadrado son los eigenvalores de \(A^T A\).
- \(A A^T = U Σ^2 U^T\) – los mismos valores singulares aparecen en la descomposición de \(A A^T\).
Por tanto, la SVD puede obtenerse mediante dos eigen‑decompositions, aunque los algoritmos modernos (Lanczos bidiagonalization, QR) son mucho más estables y eficientes para matrices grandes.
Aplicaciones típicas
- Reducción de dimensionalidad (PCA y Truncated SVD).
- Compresión de imágenes: representación de una imagen mediante los k valores singulares más importantes.
- Sistemas de recomendación: factorizar la matriz de usuarios‑items.
- Resolución de sistemas lineales mal condicionados mediante pseudo‑inversa.
Implementación en Python
Python ofrece varias librerías para calcular la SVD de forma eficiente:
numpy.linalg.svd– interfaz simple, utiliza LAPACK bajo el capó.scipy.linalg.svd– permite especificar modos de cálculo (full, reduced, econ).scipy.sparse.linalg.svds– algoritmo de Lanczos para matrices dispersas y truncated SVD.
A continuación, un ejemplo paso a paso con numpy:
import numpy as np
# Matriz de ejemplo (4x3)
A = np.array([[1, 0, 0],
[0, 2, 0],
[0, 0, 3],
[0, 0, 0]], dtype=float)
# Descomposición completa
U, s, VT = np.linalg.svd(A, full_matrices=True)
# Convertir el vector de valores singulares a matriz diagonal
Sigma = np.zeros((U.shape[1], VT.shape[0]))
np.fill_diagonal(Sigma, s)
# Reconstrucción para validar
A_rec = U @ Sigma @ VT
print('A reconstruida:\n', np.round(A_rec, 6))
Salida esperada:
A reconstruida:
[[1. 0. 0.]
[0. 2. 0.]
[0. 0. 3.]
[0. 0. 0.]]
Comparativa: SVD vs Eigen‑decomposition vs PCA
SVD
- Aplica a cualquier matriz (no necesita ser cuadrada).
- Siempre devuelve valores singulares no negativos y ortogonales.
- Mayor estabilidad numérica para matrices mal condicionadas.
- Computacionalmente costosa O(min(mn², m²n)).
Eigen‑decomposition
- Requiere matrices cuadradas y, preferiblemente, simétricas.
- Los eigenvalores pueden ser negativos o complejos.
- Más rápida O(n³) para matrices pequeñas, pero inestable en casos no simétricos.
- Base para SVD a través de AᵀA o AAᵀ.
PCA (basada en SVD)
- Utiliza la SVD de la matriz centrada para obtener componentes principales.
- Escala bien a datos de alta dimensión cuando se usa truncated SVD.
- Requiere pre‑procesamiento (centrado, a veces escalado).
Truncated SVD (Sparse PCA)
- Calcula solo los k mayores valores singulares.
- Ideal para matrices dispersas (texto, recomendación).
- Complejidad O(k·nnz) donde nnz es el número de elementos no cero.
Rendimiento y escalabilidad
Para matrices muy grandes (≥10⁶ × 10⁶) la SVD completa es inviable. Las estrategias habituales son:
- Truncated SVD con
scipy.sparse.linalg.svds(algoritmo Lanczos). - Uso de randomized SVD (
sklearn.utils.extmath.randomized_svd) que reduce la complejidad a O(mn log k). - Distribución del cálculo con Dask o Spark MLlib para entornos de cluster.
Ejemplo de SVD aleatorizado:
from sklearn.utils.extmath import randomized_svd
U, s, VT = randomized_svd(A, n_components=5, n_iter=5, random_state=42)
Este método es particularmente útil en procesamiento de lenguaje natural (TF‑IDF) y análisis de redes.
Buenas prácticas y consideraciones de seguridad
- Tipo de dato: use
float64para precisión numérica; evitefloat32cuando los valores singulares varíen en varios órdenes de magnitud. - Normalización: escale la matriz (por ejemplo, dividir por su norma) para evitar overflow en matrices muy grandes.
- Gestión de memoria: al trabajar con matrices dispersas, convierta a formato
csr_matrixantes de llamar asvdspara minimizar la huella de memoria. - Seguridad: nunca cargue datos sin validar su origen; la SVD puede exponer patrones sensibles (por ejemplo, en datos de pacientes) si los valores singulares se comparten sin anonimizar.
Solución de problemas comunes
| Problema | Causa | Solución recomendada |
|---|---|---|
Convergencia lenta en svds | Valor de k demasiado cercano al rango efectivo | Aumentar n_iter o reducir k. Verificar la distribución de valores singulares. |
| Resultado con valores NaN | Datos con valores faltantes o infinidades | Imputar o eliminar filas/columnas, o usar np.nan_to_num antes de la descomposición. |
| Desbordamiento de memoria | Uso de SVD completa en matriz densa grande | Pasar a formato disperso y usar svds o randomized SVD. |
Conclusión
La descomposición SVD es una herramienta versátil que, bien entendida y aplicada, permite resolver problemas de reducción de dimensionalidad, compresión, y cálculo de pseudo‑inversas con alta estabilidad numérica. Con las librerías modernas de Python (NumPy, SciPy, scikit‑learn) y técnicas de truncamiento aleatorio, es posible escalar la SVD a conjuntos de datos masivos sin sacrificar precisión.
Dominar su teoría y sus implementaciones prácticas es esencial para cualquier profesional de datos, ingeniero de Machine Learning o desarrollador que trabaje con grandes matrices.
Algoritmo de Descomposición SVD: Teoría, Implementación y Ejemplos en Python