Tensores en Deep Learning
Los tensores son la estructura de datos fundamental de todo el deep learning. En esta sección aprenderás qué son, cómo operar con ellos, cómo los frameworks calculan gradientes automáticamente, y por qué GPU y TPU son esenciales.
¿Qué es un tensor?
La perspectiva matemática
En matemáticas, un tensor es un objeto algebraico que generaliza los conceptos de escalar, vector y matriz a dimensiones arbitrarias. Formalmente, un tensor de rango \(n\) es un array multidimensional de números que transforma de cierta forma bajo cambios de coordenadas.
Donde \(n\) es el rango (o número de ejes/dimensiones) del tensor, y cada \(d_i\) es el tamaño de la dimensión \(i\). Para acceder a un elemento necesitamos \(n\) índices: \(T_{i_1, i_2, \ldots, i_n}\).
Nota sobre terminología: En física y matemáticas puras, «tensor» tiene una definición más estricta (incluyendo propiedades de transformación covariante/contravariante). En deep learning, usamos el término de forma más relajada: un tensor es simplemente un array multidimensional de números con un tipo de dato homogéneo.
La perspectiva del científico de datos
Para un científico de datos o ingeniero de ML, lo importante es esto: absolutamente todo en deep learning son tensores:
- Las imágenes son tensores de 3 dimensiones (alto × ancho × canales)
- Un batch de imágenes es un tensor de 4 dimensiones
- Los pesos de cada capa de tu red neuronal son tensores
- Los gradientes que calculamos son tensores del mismo shape que los pesos
- El texto tokenizado se convierte en un tensor antes de entrar al modelo
- Las señales de audio, series temporales, vídeos... todo son tensores
Concepto clave: Un tensor tiene tres propiedades fundamentales:
- Shape (forma): las dimensiones del tensor, ej:
(32, 3, 224, 224) - Dtype (tipo): el tipo de dato numérico, ej:
float32,int64 - Device (dispositivo): dónde vive en memoria, ej:
cpu,cuda:0
Rangos de tensores: del escalar al vídeo
El rango de un tensor indica cuántos índices necesitamos para acceder a un elemento. Si un tensor tiene rango 3, necesitamos tres números —por ejemplo \((i, j, k)\)— para localizar cualquier valor dentro de él.
En la práctica del deep learning, el rango de un tensor está directamente ligado al tipo de dato que representamos. Los datos tabulares clásicos suelen ser matrices (rango 2), una imagen en color es un tensor de rango 3, y un batch de imágenes —la unidad de trabajo habitual de una red convolucional— es un tensor de rango 4. A medida que aumenta la complejidad del dato (vídeo, vídeo en batches, secuencias de secuencias…), el rango crece. Comprender esta correspondencia es fundamental: cuando un framework te devuelve un error de dimensiones, lo primero es pensar «¿qué tipo de dato es y cuántos ejes debería tener?».
Veamos cada caso con ejemplos del mundo real:
La progresión del escalar al tensor 5D no es caprichosa: refleja cómo crece la complejidad de la información que procesamos. Un escalar es un único dato numérico; un vector agrupa varios datos en una secuencia ordenada; una matriz los organiza en filas y columnas. A partir de rango 3, entramos en el terreno donde el deep learning realmente opera: tensores que codifican datos con estructura espacial, temporal o semántica. El shape de un tensor no es solo un detalle técnico —es la forma en que codificamos la estructura intrínseca de los datos.
Utiliza el explorador interactivo a continuación para ver cómo cambian el shape, el rango, la memoria necesaria y el uso típico según el tipo de dato. Observa especialmente cómo la cantidad de memoria crece exponencialmente: un batch de vídeos puede ocupar cientos de megabytes en un solo tensor.
En la práctica, rara vez trabajarás con tensores de rango superior a 5. Las arquitecturas
de deep learning suelen añadir como máximo una dimensión de batch al frente y, en algunos
casos, dimensiones adicionales para secuencias temporales o cabezas de atención. Si alguna vez
te encuentras con un tensor de rango 6 o superior, es probable que puedas reorganizarlo
mediante un reshape o un view — operaciones que veremos en la
siguiente sección.
Orden de dimensiones — ¡cuidado! PyTorch usa
(Batch, Canales, Alto, Ancho) → channels-first,
mientras que TensorFlow/Keras usa por defecto
(Batch, Alto, Ancho, Canales) → channels-last.
Mezclar estos órdenes es una fuente muy común de bugs.
Operaciones con tensores
Las operaciones entre tensores son el corazón de todo el deep learning. Cada capa de tu red neuronal ejecuta operaciones tensoriales: desde simples sumas y multiplicaciones elemento a elemento hasta productos matriciales que combinan millones de pesos con los datos de entrada. Dominar estas operaciones —y, sobre todo, entender cómo afectan al shape de los tensores— es la clave para poder construir, depurar y optimizar cualquier modelo.
Vamos a recorrer las operaciones más importantes, desde las más simples hasta las que requieren más cuidado con las dimensiones:
Operaciones elemento a elemento
Se aplican independientemente a cada elemento del tensor. Requieren que ambos tensores tengan el mismo shape (o que sean compatibles por broadcasting, que veremos después).
Incluyen: suma, resta, multiplicación (Hadamard \(\odot\)), división, potencia, y cualquier función aplicada elemento a elemento (ReLU, sigmoid, exp, log…).
Producto escalar (dot product)
El dot product (producto punto) de dos vectores es la operación más fundamental del álgebra lineal y, por extensión, de las redes neuronales. Es lo que calcula cada neurona:
Toma dos vectores del mismo tamaño y produce un escalar.
Geométricamente, mide cuánto se «alinean» dos vectores. En una red neuronal,
la operación de cada neurona es: output = dot(inputs, weights) + bias
—exactamente la operación que estudiamos en detalle en el módulo de
Perceptrón y MLP.
Producto matricial (matmul)
La multiplicación de matrices es la generalización del dot product a 2D. Es la operación que domina el coste computacional de toda red neuronal:
Regla clave: Si \(A\) tiene shape \((M, \textcolor{orange}{K})\) y \(B\) tiene shape \((\textcolor{orange}{K}, N)\), el resultado \(C\) tendrá shape \((M, N)\). Las dimensiones internas (\(K\)) deben coincidir y se «contraen» (desaparecen).
Relación: dot product ↔ matmul
Conexión importante: El dot product de dos vectores es un caso particular del matmul. Si convertimos el vector \(\mathbf{a}\) en una matriz fila \((1, n)\) y \(\mathbf{b}\) en una columna \((n, 1)\), entonces:
\(\mathbf{a} \cdot \mathbf{b} = \mathbf{a}^T \mathbf{b}\) → resultado \((1, 1)\) = escalar
Y una capa densa con 128 neuronas es un matmul: Y = X @ W + b donde
\(X\) es \((B, d_{in})\), \(W\) es \((d_{in}, 128)\), resultado \(Y\) es \((B, 128)\).
Transposición
Intercambia dos ejes de un tensor. Para una matriz, es intercambiar filas y columnas (\(A^T_{ij} = A_{ji}\)).
Para tensores de mayor rango, se especifica qué ejes intercambiar con permute() o transpose().
Reshape y view
Cambia la forma (shape) del tensor sin modificar los datos. Los elementos se mantienen en el mismo orden en memoria; solo cambia la «interpretación» de sus dimensiones.
Un tensor con datos [1, 2, 3, 4, 5, 6] (shape (6,)) se puede reinterpretar como:
reshape(2, 3)→ \(\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix}\)reshape(3, 2)→ \(\begin{bmatrix} 1 & 2 \\ 3 & 4 \\ 5 & 6 \end{bmatrix}\)reshape(6, 1)→ vector columnareshape(1, 6)→ vector filareshape(1, 2, 3)→ tensor 3D (un «batch» de una matriz 2×3)
Regla: el producto de las nuevas dimensiones debe ser igual al número total de elementos. \(2 \times 3 = 6 \checkmark\), \(2 \times 4 = 8 \neq 6\) ✗.
En PyTorch: x.view(2, 3) o x.reshape(2, 3).
En TensorFlow: tf.reshape(x, [2, 3]).
Concatenación y stacking
Concatenar une tensores a lo largo de un eje existente. Apilar (stack) los une creando un eje nuevo.
Concatenación (mismo rango, une a lo largo de un eje):
Si \(A\) tiene shape (3, 4) y \(B\) tiene shape (3, 4):
concat([A, B], axis=0)→ shape(6, 4)— une por filasconcat([A, B], axis=1)→ shape(3, 8)— une por columnas
Regla: todas las dimensiones excepto la del eje de concatenación deben coincidir.
Stacking (crea un nuevo eje):
Si \(A\) y \(B\) tienen shape (3, 4):
stack([A, B], axis=0)→ shape(2, 3, 4)— nuevo eje delantestack([A, B], axis=1)→ shape(3, 2, 4)— nuevo eje en medio
Regla: todos los tensores deben tener exactamente el mismo shape.
Padding
Añade valores (típicamente ceros) alrededor de un tensor. Es fundamental en CNNs para controlar el tamaño de salida de las convoluciones, y en Transformers para igualar la longitud de secuencias en un batch:
- Zero padding: rellena con ceros. El más común en CNNs y NLP.
- Reflect padding: rellena reflejando los valores del borde. Útil en imágenes para evitar artefactos.
- Replicate padding: repite el valor del borde. Usado en procesamiento de señales.
- Circular padding: trata el tensor como si fuera periódico.
En CNNs: Conv2d(padding=1) con un kernel 3×3 mantiene el tamaño espacial.
En NLP: pad_sequence(sequences, padding_value=0) iguala las longitudes de las frases de un batch.
Slicing e indexación
Extraer subconjuntos de un tensor. Funciona igual que el slicing de NumPy:
tensor[start:end, :, index]. Es una de las operaciones más usadas a diario.
Reducción
Operaciones que «colapsan» una o más dimensiones: sum, mean,
max, min, argmax. Siempre hay que especificar sobre
qué eje(s) se reduce.
Si \(A\) tiene shape (3, 4, 5):
A.sum(axis=0)→ shape(4, 5)— colapsó el eje 0A.sum(axis=1)→ shape(3, 5)— colapsó el eje 1A.sum(axis=(0,2))→ shape(4,)— colapsó ejes 0 y 2A.sum()→ escalar — colapsó todoA.sum(axis=1, keepdims=True)→ shape(3, 1, 5)— mantiene el eje como 1
keepdims=True es útil para luego hacer broadcasting sin problemas.
Reglas de compatibilidad de dimensiones
Una de las partes más confusas al empezar con tensores es entender cuándo dos tensores son compatibles para una operación. Estas son las reglas clave:
Regla para operaciones elemento a elemento
Dos tensores son compatibles para operaciones elemento a elemento si tienen exactamente el mismo shape... o si se puede aplicar broadcasting.
Broadcasting
Broadcasting es un mecanismo que permite operar con tensores de shapes diferentes sin copiar datos. Las reglas son:
- Se comparan los shapes de derecha a izquierda, dimensión por dimensión.
- Dos dimensiones son compatibles si son iguales, o si una de ellas es 1.
- Si un tensor tiene menos dimensiones, se le añaden 1s por la izquierda.
Un ejemplo clásico: si tienes un tensor de imágenes con shape (32, 3, 224, 224)
y quieres normalizar cada canal restando su media, solo necesitas un vector de medias con
shape (3, 1, 1). Broadcasting se encarga de «expandir» ese vector para que
se aplique a cada imagen del batch y a cada píxel, sin crear copias en memoria. Esta
eficiencia es lo que hace posible escribir código limpio y a la vez rápido.
Prueba la herramienta a continuación para experimentar con diferentes combinaciones de shapes y entender cuándo el broadcasting es posible y cuándo no:
Regla para matmul
| Operación | Shape A | Shape B | Regla | Shape resultado |
|---|---|---|---|---|
| Dot product | (n,) | (n,) | Mismo tamaño | Escalar |
| Mat-vec | (M, K) | (K,) | Cols A = tamaño B | (M,) |
| Matmul 2D | (M, K) | (K, N) | Cols A = Filas B | (M, N) |
| Batched matmul | (B, M, K) | (B, K, N) | Batch dims iguales + matmul rule | (B, M, N) |
Regla para concatenación
Todas las dimensiones deben coincidir excepto la del eje de concatenación. Ejemplo: para concatenar por eje 0, las dimensiones 1, 2, 3... deben ser idénticas.
Consejo práctico: cuando tu código da un error de shapes, imprime el .shape
de todos los tensores involucrados. El 90% de los bugs en deep learning son errores de dimensiones,
y se resuelven mirando los shapes.
Herramienta interactiva: prueba a crear tensores de diferentes rangos, aplicar operaciones y visualizar los resultados en el Explorador de Tensores. Experimenta con sumas, productos matriciales, broadcasting y concatenación para afianzar lo aprendido en esta sección.
Gradientes de tensores
¿Qué es un gradiente?
El gradiente de una función escalar \(\mathcal{L}\) respecto a un tensor de parámetros \(\theta\) es un tensor del mismo shape que \(\theta\) donde cada elemento indica cuánto cambiaría \(\mathcal{L}\) si cambiásemos ese parámetro en particular:
Intuición: el gradiente es un «mapa de sensibilidad». Para cada peso de tu red, te dice: «si aumentas este peso un poquito, ¿cuánto sube o baja el loss?». Si la derivada parcial es positiva, aumentar el peso aumenta el loss → debemos reducirlo. Si es negativa, lo contrario.
¿Por qué son necesarios en deep learning?
Todo el entrenamiento de redes neuronales se basa en el gradiente. El algoritmo es:
- Forward pass: calcula la predicción y el loss
- Backward pass: calcula \(\nabla_\theta \mathcal{L}\) para todos los parámetros
- Update: ajusta cada peso en la dirección que reduce el loss: \(\theta \leftarrow \theta - \eta \nabla_\theta \mathcal{L}\)
Este proceso de actualización iterativa de los pesos es lo que conocemos como descenso del gradiente, y lo estudiamos en profundidad en su propio módulo. Aquí nos centraremos en cómo los frameworks calculan esos gradientes de forma automática.
Un modelo como GPT-4 tiene cientos de miles de millones de parámetros. Calcular la derivada parcial de cada uno a mano sería imposible. Por eso necesitamos la diferenciación automática.
Diferenciación automática (Autograd)
Los frameworks de deep learning implementan diferenciación automática (autodiff): registran cada operación que se aplica a los tensores en un grafo computacional, y luego recorren este grafo «hacia atrás» (backpropagation) aplicando la regla de la cadena para calcular todos los gradientes automáticamente.
Esto se aplica recursivamente: si \(\mathcal{L}\) depende de \(z\), que depende de \(y\), que depende de \(x\):
Tensores con requires_grad
En PyTorch, para que un tensor participe en autograd, se marca con requires_grad=True.
Solo los tensores «hoja» (que creamos nosotros) almacenan gradientes; los intermedios se calculan al vuelo.
import torch
# Crear tensores con requires_grad
x = torch.tensor([2.0, 3.0], requires_grad=True)
w = torch.tensor([0.5, -1.0], requires_grad=True)
# Forward pass
y = (x * w).sum() # dot product = 2*0.5 + 3*(-1) = -2
loss = y ** 2 # loss = (-2)² = 4
# Backward pass — calcula TODOS los gradientes automáticamente
loss.backward()
print(x.grad) # tensor([-2., 4.]) → ∂loss/∂x
print(w.grad) # tensor([-8., -12.]) → ∂loss/∂w
# Verificación: ∂loss/∂x₁ = 2y · w₁ = 2(-2)(0.5) = -2 ✓
import tensorflow as tf
# En TensorFlow se usa GradientTape como contexto
x = tf.Variable([2.0, 3.0])
w = tf.Variable([0.5, -1.0])
with tf.GradientTape() as tape:
y = tf.reduce_sum(x * w) # dot product
loss = y ** 2
# Calcula gradientes respecto a x y w
grads = tape.gradient(loss, [x, w])
print(grads[0]) # tf.Tensor([-2. 4.], ...) → ∂loss/∂x
print(grads[1]) # tf.Tensor([-8. -12.], ...) → ∂loss/∂w
# GradientTape graba las operaciones; .gradient() recorre el grafo
Para profundizar:
- Tutorial oficial de Autograd en PyTorch
- Guía de diferenciación automática en TensorFlow
- Baydin et al., «Automatic Differentiation in Machine Learning: a Survey» (2018) — el paper de referencia sobre autodiff
Frameworks de deep learning
Hasta ahora hemos hablado de tensores y sus operaciones de forma abstracta. En la práctica, necesitas un framework de software que implemente estas operaciones de forma eficiente, que las ejecute en GPU, y que sea capaz de calcular gradientes automáticamente para entrenar modelos con millones de parámetros. En esta sección veremos por qué los frameworks son imprescindibles, qué aportan exactamente, y cómo se compara el enfoque «manual» con el que ofrecen herramientas como PyTorch o TensorFlow.
¿Por qué necesitamos frameworks?
En teoría, podrías implementar una red neuronal con Python puro y NumPy. Pero en la práctica, un framework te proporciona:
- Tensores optimizados con operaciones que corren en GPU/TPU, no solo en CPU
- Diferenciación automática (autograd): no tienes que derivar a mano ni escribir el backward pass
- Capas predefinidas: Conv2d, LSTM, MultiheadAttention, etc. — código probado y optimizado
- Optimizadores: SGD, Adam, AdamW... solo eliges y configuras
- Data loaders: carga y batching eficiente de datasets enormes
- Ecosistema: modelos pretrained, transfer learning, herramientas de debugging, visualización
Sin framework: implementar backpropagation para un Transformer manualmente
requeriría miles de líneas de derivadas parciales y sería extremadamente propenso a errores.
Con PyTorch, son literalmente 2 líneas: loss.backward() + optimizer.step().
Lo que hace un framework por ti
| Sin framework (NumPy) | Con framework |
|---|---|
| Calculas gradientes a mano | loss.backward() calcula todo automáticamente |
| Solo CPU | .to('cuda') y se ejecuta en GPU |
| Escribes cada capa desde cero | nn.Linear(784, 128) |
| Implementas tu propio SGD | optim.Adam(params, lr=3e-4) |
| Gestionas batches manualmente | DataLoader(dataset, batch_size=32) |
| Debug = sufrimiento | Hooks, profilers, TensorBoard, etc. |
Computación eficiente: GPU, TPU y más
¿Por qué la CPU no basta?
Las operaciones de deep learning son masivamente paralelas: una multiplicación de matrices \((1024 \times 1024) \times (1024 \times 1024)\) implica más de mil millones de operaciones de multiplicación-suma. Una CPU moderna tiene 8-32 cores; una GPU tiene miles.
Dato clave: Una NVIDIA A100 puede ejecutar hasta 312 TFLOPS (trillones de operaciones de coma flotante por segundo) en FP16. Una CPU de alta gama alcanza ~1-2 TFLOPS. Eso es más de 150× más rápido en las operaciones que dominan el deep learning.
Tipos de aceleradores
| Hardware | Fabricante | Cores | Uso principal | Framework principal |
|---|---|---|---|---|
| CPU | Intel, AMD | 8-128 | Preprocesamiento, modelos pequeños | Todos |
| GPU (CUDA) | NVIDIA | ~10,000+ | Training & inference general | PyTorch, TF |
| TPU | Systolic array | Training a escala masiva | JAX, TF | |
| Apple Silicon | Apple | GPU + Neural Engine | On-device ML, desarrollo local | PyTorch (MPS) |
| GPU (ROCm) | AMD | ~10,000+ | Alternativa a NVIDIA | PyTorch |
¿Cómo funciona la computación en GPU?
La idea clave es el paralelismo SIMT (Single Instruction, Multiple Threads):
la misma operación se ejecuta simultáneamente sobre miles de elementos del tensor.
Cuando haces C = A @ B en GPU:
- Los tensores \(A\) y \(B\) se transfieren a la memoria de la GPU (VRAM)
- La GPU lanza miles de threads, cada uno calculando una parte de \(C\)
- Se usan bibliotecas optimizadas como cuBLAS, cuDNN y Tensor Cores
- El resultado \(C\) queda en VRAM, listo para la siguiente operación
El paso 4 es crucial: una vez que los datos están en la GPU, las operaciones sucesivas no necesitan transferirlos de vuelta a la CPU. Esto es lo que hace que mantener tensores en el mismo device sea tan importante. Cada transferencia CPU↔GPU tiene una latencia significativa, y un error frecuente en principiantes es mover datos innecesariamente entre dispositivos en cada iteración del bucle de entrenamiento.
Los modelos modernos usan mixed precision: calculan el forward/backward en FP16 (media precisión, 16 bits) pero mantienen una copia de los pesos en FP32 para las actualizaciones.
Ventajas:
- ×2 velocidad en los Tensor Cores de NVIDIA
- ×2 menos memoria VRAM → puedes usar batch sizes más grandes
- La calidad del modelo es prácticamente idéntica
En PyTorch: torch.cuda.amp.autocast() + GradScaler()
En TensorFlow: tf.keras.mixed_precision.set_global_policy('mixed_float16')
Mover tensores entre dispositivos
# Comprobar si hay GPU
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# Crear tensor directamente en GPU
x = torch.randn(3, 4, device=device)
# Mover modelo y datos a GPU
model = model.to(device)
inputs = inputs.to(device)
# ¡OJO! Ambos tensores deben estar en el mismo device
# x_cpu + x_gpu → RuntimeError
# TF detecta y usa la GPU automáticamente
print(tf.config.list_physical_devices('GPU'))
# Forzar un device específico
with tf.device('/GPU:0'):
x = tf.random.normal([3, 4])
y = tf.matmul(x, tf.transpose(x))
# Para multi-GPU: tf.distribute.MirroredStrategy
PyTorch vs TensorFlow
Breve historia
La evolución de los frameworks de deep learning es una historia de compromiso entre expresividad (facilidad para experimentar) y eficiencia (velocidad de ejecución). Los primeros frameworks apostaron por grafos estáticos: el usuario definía la arquitectura completa antes de ejecutarla, lo que permitía optimizaciones agresivas pero hacía el debugging muy difícil. La aparición de PyTorch popularizó los grafos dinámicos, y desde entonces la tendencia general ha sido combinar la flexibilidad de la ejecución dinámica con la velocidad de la compilación.
@tf.function para compilar cuando necesitas velocidad.Comparación directa
A día de hoy, PyTorch y TensorFlow+Keras son los dos ecosistemas dominantes, y ambos
son opciones excelentes. Sin embargo, han convergido desde filosofías opuestas: PyTorch
nació como una herramienta de investigación centrada en la flexibilidad y el debugging,
mientras que TensorFlow se diseñó como una plataforma de producción end-to-end. Con el
tiempo, PyTorch ha añadido herramientas de deployment (torch.compile,
TorchServe, ExecuTorch) y TensorFlow ha adoptado la ejecución dinámica (eager mode).
El resultado es que hoy se parecen más que nunca, pero cada uno conserva sus fortalezas históricas.
Las siguientes tarjetas resumen los puntos fuertes de cada framework:
- Grafos dinámicos nativos — debug con pdb/print
- API Pythonica e intuitiva
- Domina investigación (~85% de papers en 2025)
- torch.compile para velocidad de producción
- Ecosistema: HuggingFace, Lightning, torchvision
- Soporte CUDA excepcional
- TorchScript / ONNX para deployment
- Keras: API de alto nivel, ideal para empezar
- TF Serving: deployment en producción maduro
- TFLite: modelos en móviles y edge
- TPU: soporte nativo (Google Cloud)
- TensorBoard: visualización integrada
- SavedModel: formato portable
- Ecosystem: TFX, TF Hub, TF.js
Más allá de las características individuales, la diferencia más significativa en 2025 es el ecosistema. La inmensa mayoría de los modelos punteros de Hugging Face —incluyendo LLMs como LLaMA, Mistral o Gemma— se publican primero (y a veces exclusivamente) en PyTorch. Por otro lado, TensorFlow sigue siendo muy fuerte en despliegue en producción, especialmente en el ecosistema de Google Cloud, y TFLite es la opción más madura para modelos en dispositivos móviles y edge.
La siguiente tabla resume las diferencias clave punto por punto:
| Aspecto | PyTorch | TensorFlow / Keras |
|---|---|---|
| Filosofía | «Python-first», investigación | «End-to-end platform» |
| Ejecución | Eager (dinámico) | Eager + @tf.function |
| Debugging | Python nativo (pdb, print) | Eager OK; @tf.function más difícil |
| Investigación | Dominante (~85% papers) | Minoritario pero activo |
| Industria | Creciendo rápido (Meta, OpenAI) | Fuerte (Google, muchas empresas) |
| Mobile/Edge | PyTorch Mobile, ExecuTorch | TFLite (más maduro) |
| Curva de aprendizaje | Media (más control manual) | Baja con Keras, alta con TF bajo nivel |
| GPU | CUDA (NVIDIA), ROCm (AMD), MPS (Apple) | CUDA, TPU (nativo), ROCm |
| Comunidad (2025) | ~82K stars GitHub | ~186K stars GitHub |
¿Cuál elegir?
- Investigación / estado del arte: PyTorch (la mayoría de papers, HuggingFace)
- Aprender rápido / prototipar: Keras (con TF backend)
- Producción en Google Cloud / TPU: TF o JAX
- Modelos en móviles: TFLite o PyTorch Mobile
- En general para un científico de datos: aprende PyTorch primero, Keras como complemento
De la teoría a la práctica: tutoriales paso a paso
Hasta aquí hemos cubierto la teoría detrás de los tensores, las operaciones, los gradientes y las diferencias entre frameworks. Pero la verdadera comprensión solo llega cuando escribes código. Los dos tutoriales que encontrarás a continuación te guían paso a paso en la construcción de tu primer modelo con cada framework: desde la creación de tensores y la definición de capas, hasta el entrenamiento completo con backpropagation.
Te recomendamos empezar por el que más te interese —o hacer ambos para comparar—. Cada tutorial es autocontenido y asume que has leído esta sección de teoría. Al terminarlos, tendrás las herramientas necesarias para abordar los módulos más avanzados de esta plataforma, como redes neuronales (Perceptrón y MLP) o descenso del gradiente.
Pon en práctica lo aprendido
Construye tu primer MLP paso a paso con cada framework: