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.
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.
Resumen
- Cada ejecución de entrenamiento de PyTorch es el mismo bucle: construir un grafo computacional a través de un pase hacia adelante, recorrerlo hacia atrás para obtener gradientes y usar esos gradientes para actualizar parámetros; todo lo demás en esta sección es una variación u optimización de ese único bucle.
- Por qué Importa: Comprender el bucle como un solo mecanismo, en lugar de una secuencia de llamadas a la API para memorizar, es lo que le permite depurar realmente un modelo que no está aprendiendo en lugar de adivinar soluciones.
- Conceptos Clave: tensor, grafo computacional, autograd, pase hacia atrás, paso del optimizador, precisión mixta.
- Cuándo Usar: Diseñar o depurar cualquier bucle de entrenamiento personalizado, decidir si la GPU o la precisión mixta valen la complejidad añadida, o razonar sobre por qué la pérdida de un modelo no disminuye.
- Limitaciones / Compensaciones: El grafo computacional y las activaciones almacenadas consumen memoria aproximadamente proporcional a la profundidad del modelo y al tamaño del lote, y la precisión mixta intercambia precisión numérica por velocidad y ahorro de memoria.
- Temas Relacionados: aprendizaje por transferencia, entrenamiento distribuido, PyTorch Lightning.
Fundamentos
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.
Mecánica e Interacciones
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.
Consideraciones Avanzadas y Aplicaciones
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 |
Conceptos Erróneos Comunes
- "El grafo computacional se construye una vez cuando se define el modelo." Se reconstruye en cada pase hacia adelante porque PyTorch es "define-by-run", no una vez en el momento de la definición del modelo; esto es lo que permite que el flujo de control de un modelo dependa de su entrada.
- "Llamar a
.backward()dos veces seguidas simplemente recalcula los mismos gradientes." Sin limpiarlos primero, los gradientes se acumulan en.graden lugar de sobrescribirse, por lo que unazero_grad()faltante corrompe silenciosamente un bucle de entrenamiento estándar. - "La precisión mixta simplemente hace que el entrenamiento sea más rápido sin inconvenientes." Intercambia algo de precisión numérica por velocidad y memoria, por eso se necesita un escalador de gradientes para evitar que los gradientes pequeños se desborden a cero en formatos de 16 bits.
- "Más GPU siempre significa un entrenamiento proporcionalmente más rápido." El entrenamiento distribuido añade sobrecarga de sincronización de gradientes entre dispositivos, por lo que los rendimientos disminuyen, e incluso pueden invertirse, una vez que ese costo de sincronización rivaliza con el tiempo de cómputo que se supone que debe ahorrar.
- "Congelar capas durante el ajuste fino es solo para ahorrar tiempo." También elimina esas capas de la computación de gradientes del pase hacia atrás por completo, que es lo que hace que el ajuste fino sea más rápido y menos propenso a sobrescribir los pesos preentrenados de forma no intencionada.
Preguntas Frecuentes
¿Qué sucede realmente cuando llamo a loss.backward()?
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.
¿Por qué necesito llamar a optimizer.zero_grad() en cada lote?
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.
¿Qué es un grafo computacional, concretamente?
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.
¿Por qué se describe a PyTorch como "define-by-run" (definir por ejecución)?
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.
¿Qué hace realmente el optimizador con los gradientes que autograd calcula?
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.
¿Por qué el entrenamiento con GPU ayuda tanto específicamente a las redes neuronales?
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.
¿Qué problema resuelve realmente el entrenamiento con precisión mixta?
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.
¿Por qué la precisión mixta necesita un escalador de gradientes?
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.
¿Cómo ahorra realmente cómputo congelar capas durante el ajuste fino?
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.
¿Qué se sincroniza realmente en el entrenamiento distribuido a través de múltiples GPU?
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.
¿Qué es el checkpointing de gradientes y por qué intercambiar cómputo por memoria?
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.
¿Es un tamaño de lote mayor siempre mejor para el rendimiento del entrenamiento?
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.
Relacionados
- Conceptos básicos de PyTorch - tensores, dispositivos y la API central sobre la que se basa este modelo
- Autograd - una mirada más profunda al pase hacia atrás y la computación de gradientes
- Bucles de entrenamiento - el bucle que describe esta página, escrito en código funcional
- GPU y precisión mixta - precisión práctica y colocación de dispositivos
- Aprendizaje por transferencia y ajuste fino - congelar capas como una aplicación de este modelo
- Entrenamiento distribuido - extender el bucle a través de múltiples dispositivos
Versiones de la pila: Esta página fue escrita para PyTorch 2.6+ y Python 3.14 (estable) / 3.13 (mantenimiento).