El Modelo Mental de Deep Learning
Los tensores de PyTorch, su motor autograd y el bucle de entrenamiento pueden parecer tres temas separados que se aprenden uno tras otro.
Busca en todas las páginas de la documentación
Los tensores de PyTorch, su motor autograd y el bucle de entrenamiento pueden parecer tres temas separados que se aprenden uno tras otro.
En realidad, son una sola máquina: un grafo computacional construido a partir de operaciones de tensores, un pase hacia atrás que recorre ese grafo para calcular gradientes y un bucle de entrenamiento que utiliza repetidamente esos gradientes para ajustar los parámetros hacia una pérdida menor.
Esta página conecta Conceptos básicos de PyTorch, Autograd, Bucles de entrenamiento y GPU y precisión mixta en un solo modelo, en lugar de cuatro temas separados para memorizar de forma independiente.
Un tensor es la estructura de datos central de PyTorch, un array n-dimensional muy similar a un array de NumPy, pero con dos adiciones que importan para el deep learning: puede residir en una GPU y puede rastrear las operaciones aplicadas a él.
Esa segunda propiedad es lo que hace posible autograd: cada operación realizada en un tensor con requires_grad=True se registra en un grafo computacional, un grafo dirigido donde los nodos son operaciones y los bordes son los tensores que fluyen entre ellos.
Una analogía útil es una receta con un recibo adjunto.
El pase hacia adelante es seguir la receta, mezclando tensores a través de operaciones para producir una salida, y el recibo es el grafo computacional, que enumera exactamente qué operaciones ocurrieron en qué orden para que luego pueda recorrerlo hacia atrás para ver cuánto contribuyó cada ingrediente al resultado final.
El pase hacia adelante es el cálculo ordinario: los tensores de entrada fluyen a través de capas como multiplicaciones de matrices, activaciones y normalización, para producir una salida como una clase predicha o una probabilidad del siguiente token.
El pase hacia atrás es donde aparece la función real de autograd.
Llamar a .backward() en una pérdida escalar recorre el grafo registrado en sentido inverso, aplicando la regla de la cadena en cada nodo para calcular el gradiente de la pérdida con respecto a cada parámetro que contribuyó a ella.
El bucle de entrenamiento es este par de pases repetidos muchas veces, y cada paso en él existe para responder una pregunta específica en un orden fijo:
para cada lote:
pase hacia adelante -> ¿qué predijo el modelo? (construir grafo)
calcular pérdida -> ¿qué tan errónea fue la predicción?
pase hacia atrás -> ¿cuánto contribuyó cada parámetro a ese error? (recorrer grafo)
paso del optimizador -> ajustar cada parámetro en dirección opuesta a su gradiente
poner a cero gradientes -> limpiar gradientes antiguos antes del siguiente lote
El paso del optimizador —SGD, Adam y sus variantes— es donde ocurre el aprendizaje real, tomando los gradientes que autograd calculó y actualizando cada parámetro mediante alguna función de ese gradiente, típicamente escalado por una tasa de aprendizaje.
Poner a cero los gradientes existe porque PyTorch acumula gradientes en .grad por defecto en lugar de sobrescribirlos.
Ese diseño admite técnicas como la acumulación de gradientes a través de múltiples lotes pequeños, pero corrompe silenciosamente un bucle de entrenamiento normal si olvidas limpiar los gradientes antes de la siguiente llamada hacia atrás.
for batch in loader:
optimizer.zero_grad() # limpiar gradientes del último paso
output = model(batch.x) # el pase hacia adelante construye el grafo
loss = loss_fn(output, batch.y)
loss.backward() # recorrer el grafo hacia atrás
optimizer.step() # actualizar parámetros usando .gradEl grafo computacional se reconstruye desde cero en cada pase hacia adelante y se descarta después de .backward() por defecto, por eso PyTorch se describe como "define-by-run" (definir por ejecución).
El grafo es un registro de lo que realmente sucedió en este pase hacia adelante específico, no una estructura estática declarada de antemano, lo que hace que el flujo de control dependiente de los datos sea sencillo en comparación con frameworks que requieren un grafo definido antes de que cualquier dato fluya a través de él.
Las GPU importan aquí porque los pases hacia adelante y hacia atrás son casi en su totalidad multiplicaciones de matrices y operaciones elemento a elemento, que se paralelizan muy bien en los miles de núcleos de una GPU en comparación con el número mucho menor de núcleos de una CPU optimizada para trabajo secuencial.
El entrenamiento con precisión mixta aprovecha el hecho de que la mayor parte de esta aritmética no necesita una precisión de punto flotante completa de 32 bits para converger.
Ejecutar multiplicaciones de matrices en 16 bits, o bfloat16, mientras se mantiene una copia maestra de 32 bits de los parámetros, reduce el uso de memoria y a menudo duplica aproximadamente el rendimiento en los núcleos tensoriales de GPU modernos, con un escalador de gradientes utilizado para evitar que los valores pequeños de gradiente se desborden a cero en el formato de menor precisión.
A escala, el costo de memoria del grafo computacional se convierte en la restricción principal en lugar del cómputo bruto, ya que cada activación intermedia del pase hacia adelante debe permanecer en memoria hasta que el pase hacia atrás la consuma.
Los modelos más profundos y los lotes más grandes cuestan memoria aproximadamente linealmente en ambas dimensiones como resultado.
El checkpointing de gradientes intercambia cómputo por memoria directamente: en lugar de almacenar cada activación intermedia, almacena solo un subconjunto y recalcula el resto durante el pase hacia atrás, una aplicación deliberada del mismo modelo mental hacia adelante/hacia atrás en lugar de una técnica separada.
El aprendizaje por transferencia y el ajuste fino reutilizan este modelo también.
Congelar las capas tempranas de una red preentrenada con requires_grad=False elimina esos parámetros del recorrido hacia atrás del grafo por completo, por lo que los gradientes solo se calculan —y solo se aplican pasos del optimizador— para las capas que realmente se están adaptando, lo que hace que el ajuste fino sea más económico que el entrenamiento desde cero.
El entrenamiento distribuido extiende el mismo bucle a través de múltiples GPU o máquinas: cada dispositivo ejecuta su propio pase hacia adelante y hacia atrás en una porción del lote, y son los gradientes, no los datos ni el modelo, lo que se sincroniza entre los dispositivos antes de cada paso del optimizador.
Es por eso que el ancho de banda de la red para la sincronización de gradientes, no el cómputo bruto, a menudo es el cuello de botella a escala.
| Estrategia de Precisión / Escala | Fortaleza | Debilidad | Mejor Ajuste |
|---|---|---|---|
| Precisión completa de 32 bits, GPU única | Más simple de razonar; sin sorpresas numéricas | Opción más lenta; limita el modelo/tamaño del lote a la memoria de una GPU | Modelos pequeños, depuración, establecimiento de una línea base de corrección |
| Precisión mixta, GPU única | Ahorro significativo de velocidad y memoria en núcleos tensoriales modernos | Añade un escalador de gradientes e inestabilidad numérica ocasional para razonar | La mayoría de los entrenamientos de producción una vez establecida la corrección |
| Entrenamiento distribuido (multi-GPU) | Escala el modelo y el tamaño del lote más allá de la memoria de un dispositivo | La sincronización de gradientes se convierte en un cuello de botella real; añade complejidad operativa | Modelos o conjuntos de datos demasiado grandes para una sola GPU independientemente de la precisión |
.backward() dos veces seguidas simplemente recalcula los mismos gradientes." Sin limpiarlos primero, los gradientes se acumulan en .grad en lugar de sobrescribirse, por lo que una zero_grad() faltante corrompe silenciosamente un bucle de entrenamiento estándar.PyTorch recorre el grafo computacional registrado durante el pase hacia adelante, en sentido inverso, aplicando la regla de la cadena en cada operación para calcular el gradiente de la pérdida con respecto a cada tensor que tiene requires_grad=True y contribuyó a esa pérdida.
PyTorch acumula los nuevos gradientes en el atributo .grad existente de un tensor por defecto en lugar de sobrescribirlo. Sin limpiarlo primero, los gradientes del lote anterior todavía se sumarían a los gradientes del lote actual, corrompiendo la actualización.
Un grafo dirigido registrado durante el pase hacia adelante, donde cada nodo es una operación aplicada a un tensor y cada borde es un tensor que fluye entre operaciones. Es lo que autograd recorre hacia atrás para calcular gradientes, y se reconstruye desde cero en cada pase hacia adelante.
Porque el grafo computacional se construye dinámicamente a medida que las operaciones se ejecutan realmente con datos reales, en lugar de declararse estáticamente antes de que cualquier dato fluya a través de él. Esto hace que el flujo de control dependiente de los datos, como un número diferente de iteraciones de bucle por entrada, sea sencillo de expresar.
Actualiza cada parámetro utilizando alguna función de su gradiente y una tasa de aprendizaje. SGD resta un gradiente escalado directamente, mientras que los optimizadores adaptativos como Adam también rastrean estadísticas recurrentes por parámetro de gradientes pasados para ajustar el tamaño efectivo del paso.
Los pases hacia adelante y hacia atrás están dominados por multiplicaciones de matrices y operaciones elemento a elemento, que se paralelizan muy bien en el gran número de núcleos simples de una GPU, a diferencia de cargas de trabajo más secuenciales que se adaptan mejor a un número menor de núcleos potentes de una CPU.
Reduce el uso de memoria y aumenta el rendimiento al ejecutar la mayor parte de la aritmética en un formato de menor precisión, 16 bits o bfloat16, en lugar de flotantes completos de 32 bits, que los núcleos tensoriales de GPU modernos ejecutan significativamente más rápido, mientras se mantiene una copia maestra de 32 bits de los parámetros para la estabilidad numérica.
Los valores pequeños de gradiente pueden desbordarse a cero cuando se representan en punto flotante de 16 bits, perdiendo información silenciosamente. Un escalador de gradientes multiplica la pérdida por un factor de escala antes del pase hacia atrás, y desescala los gradientes antes del paso del optimizador, para mantener los valores en un rango representable.
Establecer requires_grad=False en los parámetros de una capa la elimina por completo de la computación de gradientes del pase hacia atrás. Autograd nunca calcula un gradiente para ella y el optimizador nunca la actualiza, por lo que tanto el pase hacia atrás como el paso de actualización son más económicos.
Cada dispositivo calcula su propio pase hacia adelante y hacia atrás en su porción del lote, y son los gradientes resultantes, no los datos brutos ni los pesos del modelo a mitad del entrenamiento, lo que se sincroniza, típicamente se promedia, entre los dispositivos antes de cada paso del optimizador.
Almacena solo un subconjunto de las activaciones del pase hacia adelante en lugar de todas ellas, y recalcula el resto durante el pase hacia atrás cuando es necesario. Es útil cuando un modelo está limitado por memoria en lugar de por cómputo, ya que la memoria de la GPU es a menudo el límite más difícil de sortear.
No incondicionalmente. Los lotes más grandes utilizan mejor el paralelismo de la GPU hasta cierto punto, pero también aumentan la memoria necesaria para almacenar activaciones para el pase hacia atrás, y los lotes muy grandes pueden cambiar la dinámica de entrenamiento en lugar de simplemente entrenar más rápido de forma gratuita.
Versiones de la pila: Esta página fue escrita para PyTorch 2.6+ y Python 3.14 (estable) / 3.13 (mantenimiento).
Revisado por Chris St. John·Última actualización: 15 jul 2026