English · Español
00 — Por qué abrir el capó del framework¶
Después de 24 fases construyendo la red desde NumPy + un poco de CUDA, llegas a Fase 25 con un modelo claro: PyTorch es plomería. Pero "plomería" no significa "trivial". Detrás de
torch.matmulhay un dispatcher con miles de entradas, un motor de autograd que captura grafos en runtime, un compilador (Inductor) que reescribe forward passes a Triton/C++. Esta fase desmonta esas tapas. No para ser ingeniero de PyTorch — sino para saber dónde mirar cuando algo va mal.
Esta es la página de orientación de la Fase 25. Responde a: ¿por qué una fase separada sobre las interioridades de PyTorch (la Fase 24 ya introdujo PyTorch como herramienta)? ¿Por qué ahora? ¿Qué te aporta "interioridades" que no te aporta "PyTorch a nivel de usuario"?
La trampa del "usuario del framework"¶
Una trayectoria de ingeniería común:
- Aprendes PyTorch con tutoriales. Construyes un modelo. Lo entrenas. Funciona.
- Te topas con un problema de rendimiento.
model = torch.compile(model)lo hace 1.5× más rápido. No sabes por qué. - Te topas con un bug de correctitud en fp16. Te pasas una semana. Al final encuentras una capa personalizada con bug. Sigues sin saber por qué fp16 lo empeoró.
- Te topas con un OOM a escala. Pruebas
find_unused_parameters=True. A veces funciona. No sabes por qué.
Cada "no sé por qué" es una deuda. La Fase 25 paga la deuda. Después de esta fase, las siguientes afirmaciones no son magia:
- "PyTorch hizo dispatch de
aten::linearal backend CUDA en el conjunto de claves fp32-sin-autocast." - "El
grad_fnparayesMmBackward0porquey = x @ W.Ty el autograd de PyTorch registra el nodo gemm." - "
torch.compilefusionó el linear + softmax via Inductor; el kernel Triton generado tiene 60 líneas y vive en/tmp/torchinductor_<user>/...." - "DDP envuelve el modelo con
nn.parallel.DistributedDataParallel, que engancha cada llamada a.backward()para añadir unall_reducetras el cálculo del gradiente de cada parámetro."
Ninguna de éstas requiere compilar PyTorch desde fuentes. Requieren leer la salida de diagnóstico correcta y saber lo que significa.
La Fase 24 introdujo; la Fase 25 desmonta¶
El modelo mental de la Fase 24 era el resumen en dos líneas:
La Fase 25 desmonta cada pieza:
- Dispatcher (
theory/01): cómotorch.matmulelige entre cuBLAS, MKL, kernels personalizados. La búsqueda en tabla. El conjunto de claves. - aten (
theory/01): la biblioteca de operadores —aten::matmul,aten::linear,aten::softmax— a la que el dispatcher hace dispatch. aten es el registro de kernels de backend en C++; el frontend de PyTorch lo envuelve. - Motor de autograd (
theory/02): cómorequires_grad=Trueprovoca que se construya un grafo; cómo.backward()lo recorre. La cadenagrad_fn. - Registro de op personalizada (
theory/02+lab/02):torch.library.custom_op— cómo añadir una nueva op a la que el dispatcher haga routing, con autograd, con integración contorch.compile. - Pipeline de compile (
theory/03): Dynamo (Python-bytecode-a-FX-graph), AOTAutograd (captura conjunta forward+backward), Inductor (lowering a kernels Triton o C++). - Distributed (
theory/03): los cuatro patrones canónicos (DDP, FSDP, tensor-parallel, pipeline-parallel) y lo que cada uno es sin implementarlo.
El ejemplo corriente: nn.Linear(64, 600)¶
Cada tema de interioridades se enseña contra el LM head del grammar MiniGPT — nn.Linear(64, 600). ¿Por qué esta op?
- Concreto. Formas reales, pesos reales de la Fase 17. Nada sintético.
- Suficientemente pequeño para inspeccionar. 64 × 600 = 38.400 pesos. El grafo de autograd para un forward tiene quizás 5 nodos. Cada lanzamiento de kernel es de microsegundos. Cabe en una sesión de debugger.
- Conecta con §A13. Las 600 columnas de salida son el vocabulario de gramática. El forward produce una distribución de probabilidad sobre las formas verbales en inglés/español. El backward da gradientes usados en el entrenamiento (Fase 18). Todo lo que derives en la Fase 25 puedes ver cómo afecta a la predicción del siguiente token.
Una sesión típica de laboratorio de la Fase 25 tiene este aspecto:
import torch
W = torch.randn(600, 64, requires_grad=True)
b = torch.randn(600, requires_grad=True)
x = torch.randn(2, 64, requires_grad=True)
y = torch.nn.functional.linear(x, W, b) # y.shape = (2, 600)
print(y.grad_fn) # → <AddmmBackward0 object at 0x...>
Ese AddmmBackward0 es el nodo de autograd. El laboratorio 01 te llevará a recorrer en detalle lo que hace.
¿Por qué ahora (Fase 25)?¶
Tres razones:
- La Fase 24 acaba de importar
torchpor primera vez. Sería desorientador zambullirse en las interioridades sin tener primero el feeling de la API superficial. La Fase 24 construye el feeling; la Fase 25 desmonta la caja. - La Fase 26 (cuantización) y la Fase 27 (Flash-Attention) requieren ambas registro de ops personalizadas. Si la Fase 25 no lo cubriera, la Fase 26 tendría que dar un rodeo. Mejor aprenderlo limpiamente aquí.
torch.compileimporta para la Fase 33 (serving). Entender el pipeline de compile ahora significa que la fase de serving puede centrarse en preocupaciones de sistemas, no en "qué hace Inductor".
Lo que esta fase no es¶
- No es un curso de contribución a PyTorch. No abrirás
aten/src/ATen/native/cuda/Softmax.cu. El dispatcher se lee desde el lado del usuario (logs, registros, custom ops), no desde el lado del contribuidor. - No es un tour completo del framework. PyTorch tiene cientos de subsistemas (
torch.jit,torch.fx,torch.func,torch.profiler,torch.utils.data, ...). La Fase 25 cubre cuatro: dispatcher, autograd, compile, distributed. El resto se trata como "lo puedes encontrar cuando lo necesites". - No es un benchmark. Nada de "X% de speedup con este truco". La Fase 25 es para entender; el rendimiento es tema de las Fases 33 / 24 / 35.
Lo que deberías sentir después de la Fase 25¶
Tres cosas deberían volverse visceralmente ciertas:
- PyTorch es una máquina de búsqueda en tabla. Cada
torch.<op>esdispatcher[(op, key_set)] → backend kernel. La complejidad aparente del framework es el tamaño de la tabla, no su mecanismo. - El autograd son dos funciones. El forward registra en un grafo; el backward lo recorre en reversa, invocando la fórmula de backward de cada nodo. El autograd escalar/tensorial de las Fases ⅞ ya implementó exactamente esto; la versión de PyTorch es la misma idea a escala.
torch.compilees un tracer + optimizador + emisor. Traza tu Python, construye un grafo, lo optimiza (fusión, eliminación de código muerto, layout), emite kernels Triton o C++. Mismo patrón que cualquier compilador.
Si eso no se siente cierto al cerrar la fase, repite el laboratorio 01. El ejercicio de autograd-a-mano es el crítico.
Lo que deberías ser capaz de hacer al final de la Fase 25¶
- Trazar un forward a través del dispatcher. Nombrar la clave de dispatch para cualquier llamada a op dada.
- Recorrer un grafo de autograd a mano. Para
linear(x, W, b), derivar la fórmula backward de cadagrad_fny verificar contra el cómputo de PyTorch. - Registrar una op personalizada (
torch.library.custom_op) con autograd. Pasargradchecky ejecutarla dentro de un modelotorch.compile'd. - Volcar y leer Triton/C++ generado por Inductor desde un forward
torch.compile'd. - Enunciar la diferencia entre DDP, FSDP, tensor-parallel y pipeline-parallel en 2 frases cada uno.
Lo que esta página NO cubre¶
- Lectura del código fuente de PyTorch. No necesitas leer
aten/nic10/— esta es una fase de interioridades desde el lado del usuario. - Interioridades del lado de CUDA (
cudnn,cublasLt, etc.). Caja negra; confiamos en ellas. - Frontend C++ de PyTorch. Fuera de alcance.
Siguiente: theory/01-dispatcher-and-aten.md — la tabla de dispatch, las claves, la decisión de routing.