Tensor Algorithms for Deep Learning
Los tensores son la piedra angular de cualquier arquitectura de deep learning. En este artículo exploramos su definición, sus operaciones clave, cómo se implementan en los principales frameworks de Python y presentamos ejemplos paso‑a‑paso que puedes ejecutar hoy mismo.
1. ¿Qué es un tensor?
Un tensor es una estructura de datos multidimensional que generaliza vectores (1‑D) y matrices (2‑D). En términos simples:
- Escalar: tensor de orden 0 (ej.
3.14) - Vector: tensor de orden 1 (ej.
[1, 2, 3]) - Matriz: tensor de orden 2 (ej.
[[1,2],[3,4]]) - Tensor de orden n: cualquier arreglo con
nejes.
En deep learning, los tensores representan datos de entrada, pesos de la red y activaciones intermedias, y pueden residir tanto en CPU como en GPU.
2. Operaciones fundamentales sobre tensores
Las operaciones que forman la base de los algoritmos de entrenamiento incluyen:
- Redimensionamiento (reshape)
- Transposición (transpose)
- Broadcasting (extensión automática de dimensiones)
- Multiplicación matricial / producto punto
- Reducciones: suma, media, máximo, etc.
- Activaciones no lineales: ReLU, Sigmoid, Softmax
- Gradientes automáticos (autodiff) para back‑propagation.
3. Frameworks de Python que manejan tensores
NumPy
Biblioteca de propósito general para cálculo numérico. No está diseñada para GPU ni autodiff, pero es ideal para comprender la mecánica de los tensores.
- CPU‑only
- Operaciones vectorizadas de alto rendimiento
- Base de muchos otros frameworks
PyTorch & TensorFlow
Frameworks de deep learning que implementan tensores con soporte nativo para GPU y diferenciación automática.
- GPU acceleration (CUDA, ROCm)
- Autodiff dinámico (PyTorch) vs. estático (TensorFlow 1.x) – TensorFlow 2.x adopta eager execution similar a PyTorch.
- Amplio ecosistema: modelos pre‑entrenados, despliegue en producción, TPU support (TF).
4. Creación y manipulación de tensores – Ejemplos en Python
4.1. NumPy
import numpy as np
# Tensor de orden 3 (batch, canales, altura, anchura)
X = np.random.randn(32, 3, 224, 224).astype(np.float32)
print('Shape:', X.shape)
# Redimensionar sin copiar datos
X_flat = X.reshape(32, -1)
print('Flat shape:', X_flat.shape)
# Producto punto batched
W = np.random.randn(3, 3)
Y = np.tensordot(X, W, axes=([1], [0])) # result shape: (32, 224, 224, 3)
4.2. PyTorch
import torch
# Tensor en GPU (si está disponible)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
X = torch.randn(32, 3, 224, 224, device=device, dtype=torch.float32)
print('Device:', X.device)
# Operación de convolución 2D (ejemplo de capa)
conv = torch.nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, stride=1, padding=1).to(device)
Y = conv(X) # shape (32, 16, 224, 224)
print('Output shape:', Y.shape)
# Back‑propagation simple
loss = Y.mean()
loss.backward()
print('Gradients shape (conv.weight):', conv.weight.grad.shape)
4.3. TensorFlow 2.x (eager execution)
import tensorflow as tf
# Tensor en GPU si está disponible
X = tf.random.normal([32, 224, 224, 3]) # TF usa NHWC por defecto
print('Shape:', X.shape)
# Capa convolucional Keras
conv = tf.keras.layers.Conv2D(filters=16, kernel_size=3, padding='same')
Y = conv(X) # shape (32, 224, 224, 16)
print('Output shape:', Y.shape)
# Gradiente con GradientTape
with tf.GradientTape() as tape:
tape.watch(conv.trainable_variables)
loss = tf.reduce_mean(Y)
grads = tape.gradient(loss, conv.trainable_variables)
print('Gradient shapes:', [g.shape for g in grads])
5. Comparativa de rendimiento (CPU vs GPU)
CPU (NumPy)
- Latencia más alta en operaciones matriciales grandes.
- Ideal para prototipos y pre‑procesado.
- Escalabilidad limitada a núcleos físicos.
GPU (PyTorch/TensorFlow)
- Velocidad 10‑100× mayor en convoluciones y multiplicaciones de matrices.
- Soporte nativo para batch processing.
- Requiere gestión de memoria (torch.cuda.empty_cache()).
6. Buenas prácticas y trucos de optimización
- Pre‑allocación de tensores: evita crear nuevos objetos dentro de loops críticos.
- Mixed precision (FP16): reduce consumo de memoria y acelera cómputos en GPUs modernas (NVIDIA Ampere, RTX).
- Pin memory en PyTorch para acelerar transferencias host‑GPU.
- tf.function o
@torch.jit.scriptpara compilar grafos y eliminar overhead de Python. - Determinismo: fijar semillas (
torch.manual_seed,np.random.seed) y usartorch.backends.cudnn.deterministic = Truecuando sea necesario reproducir resultados.
7. Solución de problemas (troubleshooting)
Al trabajar con tensores, los errores más comunes son:
- Shape mismatch: Verifica siempre la dimensión esperada usando
.shapeo.size(). - Device errors: Asegúrate de que todos los tensores involucrados estén en el mismo dispositivo (CPU vs GPU).
- Memory overflow: Reduce el batch size o habilita
torch.cuda.empty_cache()entre iteraciones. - Gradients become NaN: Usa clipping (
torch.nn.utils.clip_grad_norm_) o reduce la tasa de aprendizaje.
8. Futuro de los tensores en IA
Con la llegada de Tensor Cores, TPUs y GPU de arquitectura AMD CDNA, los frameworks están adoptando APIs de bajo nivel como CUDA Graphs y XLA para eliminar cuellos de botella de lanzamiento de kernels. Además, los nuevos estándares ONNX Runtime y MLIR prometen portabilidad entre hardware sin perder rendimiento.
9. Conclusión
Dominar los algoritmos basados en tensores es esencial para cualquier ingeniero de deep learning. Desde la simple manipulación con NumPy hasta el entrenamiento de modelos masivos con PyTorch o TensorFlow, los conceptos de forma, broadcasting y autodiff son universales. Aplica las mejores prácticas de rendimiento y mantente al día con las innovaciones de hardware para exprimir al máximo tus modelos.
Tensor Algorithms for Deep Learning: Conceptos, Operaciones y Ejemplos Prácticos en Python