Algoritmos con Tensores y Dimensiones Multidimensionales
Los tensores son la piedra angular de la computación científica y el aprendizaje profundo. En este artículo descubrirás qué son, cómo se representan en Python y cómo diseñar algoritmos eficientes que operen sobre datos multidimensionales.
¿Qué es un tensor?
Un tensor es una estructura de datos que generaliza vectores y matrices a n dimensiones. En términos simples:
0‑D: escalar.1‑D: vector.2‑D: matriz.3‑Dy superiores: cubos, hipercubos, etc.
Su representación incluye:
- Forma (
shape): número de elementos en cada eje. - Tipo de dato (
dtype):float32,int64, etc. - Dispositivo (
device): CPU o GPU.
Algoritmos habituales con tensores
Los algoritmos que manipulan tensores suelen involucrar operaciones lineales y no lineales. Algunos ejemplos clásicos:
- Multiplicación de matrices (producto punto).
- Descomposición en valores singulares (SVD).
- Convolución n‑dimensional.
- Reducciones:
sum,mean,maxa lo largo de ejes. - Transformaciones de forma (
reshape,transpose,permute).
Cuando los tensores están en GPU, estas operaciones se ejecutan con cientos de veces más velocidad que en CPU.
Tensores multidimensionales en Python
Los tres ecosistemas más usados son NumPy, PyTorch y TensorFlow. Cada uno ofrece APIs optimizadas y soporte para aceleración por hardware.
Ejemplo 1: Creación y manipulación con NumPy
import numpy as np
# Tensor 3‑D (2×3×4)
A = np.arange(24, dtype=np.float32).reshape(2, 3, 4)
print('A.shape:', A.shape)
# Operación de reducción: promedio por eje 0
B = A.mean(axis=0)
print('B.shape (3×4):', B.shape)
# Convolución 2‑D usando scipy (para ilustrar)
from scipy.signal import convolve2d
kernel = np.array([[1, 0, -1], [1, 0, -1], [1, 0, -1]], dtype=np.float32)
C = convolve2d(A[0], kernel, mode='same')
print('Convolución result shape:', C.shape)
Ejemplo 2: Operaciones con PyTorch
import torch
# Tensor 4‑D (batch, canales, alto, ancho)
x = torch.randn(8, 3, 224, 224, device='cuda')
print('x.shape:', x.shape)
# Convolución 2‑D nativa de PyTorch
conv = torch.nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, padding=1).cuda()
output = conv(x)
print('output.shape:', output.shape) # (8, 16, 224, 224)
# Cambio de tipo de dato para ahorro de memoria
x_fp16 = x.half()
print('x_fp16 dtype:', x_fp16.dtype)
Ejemplo 3: Red neuronal simple con TensorFlow
import tensorflow as tf
# Tensor de entrada (batch, 28, 28, 1)
inputs = tf.random.normal([32, 28, 28, 1])
model = tf.keras.Sequential([
tf.keras.layers.Conv2D(32, 3, activation='relu'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(10, activation='softmax')
])
outputs = model(inputs)
print('outputs.shape:', outputs.shape) # (32, 10)
Comparativa rápida entre NumPy, PyTorch y TensorFlow
Ventajas
- NumPy: simplicidad, amplio ecosistema científico, CPU‑only pero con
numpy.linalgacelerado por BLAS. - PyTorch: modelo dinámico (eager execution), excelente soporte de GPU, comunidad de investigación.
- TensorFlow: despliegue a producción (TensorFlow Serving, TFLite), graph mode para optimizaciones, integración con Keras.
Desventajas
- NumPy: falta de soporte nativo GPU, limitado para deep learning.
- PyTorch: curva de aprendizaje para exportar modelos (ONNX, TorchScript) y servir en producción.
- TensorFlow: API más verbosa, depuración a veces compleja por graph mode.
Buenas prácticas y optimización
- Tipo de dato adecuado: Usa
float16obfloat16en GPU cuando la precisión lo permite. - Batching: Procesa datos en lotes para maximizar la utilización de la GPU.
- Pre‑alocación: Evita crear tensores dentro de bucles; reutiliza buffers con
torch.empty_likeotf.Variable. - Pin memory (PyTorch):
DataLoader(..., pin_memory=True)acelera la transferencia CPU→GPU. - Mixed‑precision training: Usa
torch.cuda.ampotf.keras.mixed_precisionpara reducir el consumo de memoria y acelerar el entrenamiento.
Resolución de problemas habituales
1. Incompatibilidad de dtype
# Error típico: Tensor dtype mismatch
x = torch.randn(5, dtype=torch.float32)
y = torch.ones(5, dtype=torch.int64)
# Solución
y = y.to(x.dtype) # o x = x.to(y.dtype)
2. Desalineación de formas (shape)
# Broadcasting error
A = torch.randn(4, 1)
B = torch.randn(3)
# Solución: expandir dimensiones
B = B.unsqueeze(0) # shape (1,3)
result = A + B # ahora broadcast a (4,3)
3. Memoria GPU agotada
- Utiliza
torch.cuda.empty_cache()entre iteraciones. - Reduce el tamaño del batch o emplea
gradient checkpointing.
Seguridad y validación de datos
Cuando los tensores provienen de fuentes externas (p.ej., archivos CSV, APIs), es fundamental validar:
- Rangos numéricos (evitar NaN/Inf).
- Dimensiones esperadas.
- Tipo de dato (convertir a
float32antes de enviar a GPU).
Ejemplo de sanitización con NumPy:
def safe_tensor(arr):
arr = np.asarray(arr, dtype=np.float32)
if np.isnan(arr).any() or np.isinf(arr).any():
raise ValueError('Tensor contiene valores no numéricos')
return arr
Escalabilidad y despliegue en producción
Para llevar algoritmos tensoriales a entornos de alta disponibilidad, considera:
- Contenedores Docker: empaqueta la aplicación con
torchserveotensorflow/serving. - Kubernetes: usa
GPU‑operatorpara gestionar recursos de GPU. - ONNX: exporta modelos PyTorch a un formato interoperable y sirve con
onnxruntimeoptimizado para CPU/GPU. - Observabilidad: integra Prometheus + Grafana para métricas de latencia y uso de GPU.
Ejemplo de Dockerfile minimalista para PyTorch:
FROM pytorch/pytorch:2.3.0-cuda12.1-cudnn8-runtime
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "serve.py"]
Conclusión
Dominar los tensores y sus algoritmos permite abordar problemas que van desde el procesamiento de señales hasta el entrenamiento de modelos de IA a gran escala. Elegir la librería adecuada, aplicar buenas prácticas de rendimiento y asegurar una correcta validación de datos son pasos críticos para construir soluciones robustas y escalables.
Algoritmos con Tensores y Dimensiones Multidimensionales: Conceptos y Ejemplos en Python