LECCIÓN 4 · FUNDAMENTOS

Autograd a fondo

El motor que hace posible el aprendizaje. Autograd calcula gradientes automáticamente — es el backpropagation de tu curso, pero gratis y para cualquier cálculo que escribas.

1. El problema que resuelve

Para entrenar una red necesitas la derivada de la pérdida respecto a cada peso (dL/dw). Calcularla a mano para millones de pesos es imposible. Autograd lo hace por ti: escribes solo el cálculo "hacia adelante" y PyTorch deriva el "hacia atrás" automáticamente.

La clave: cada tensor con requires_grad=True graba las operaciones que se le hacen, formando un "grafo computacional". Cuando llamas .backward(), PyTorch recorre ese grafo al revés aplicando la regla de la cadena.

2. requires_grad + backward + .grad

El ciclo completo en 3 pasos. Derivemos y = x³ + 2x en x = 2:

x = torch.tensor(2.0, requires_grad=True)   # 1. marcar como derivable
y = x**3 + 2*x                          # 2. operar (se graba el grafo)
y.backward()                            # 3. calcular gradientes
print(x.grad)                          # el resultado queda en .grad
salida realy = 12.0 x.grad = 14.0
✍️ El cálculo a mano, paso por paso
Valor de y en x=2:
   y = x³ + 2x = 2³ + 2·2 = 8 + 4 = 12          ✓

Derivada término a término:
   d(x³)/dx = 3x²          (regla de la potencia)
   d(2x)/dx = 2
   ⇒ dy/dx = 3x² + 2

Evaluada en x=2:
   3·(2²) + 2 = 3·4 + 2 = 12 + 2 = 14           ✓ = x.grad

3. Varias variables a la vez

Una red tiene muchos parámetros. backward() calcula la derivada respecto a todos los tensores derivables de un golpe. Sea L = a² + 3b:

a = torch.tensor(1.0, requires_grad=True)
b = torch.tensor(2.0, requires_grad=True)
L = a**2 + 3*b
L.backward()
print(a.grad, b.grad)
salida realL = 7.0 dL/da = 2.0 (derivada de a² es 2a = 2·1 = 2) dL/db = 3.0 (derivada de 3b es 3)
✍️ A mano (a=1, b=2)
Valor de L:   L = a² + 3b = 1² + 3·2 = 1 + 6 = 7      ✓

Derivada parcial respecto a 'a' (b se trata como constante):
   ∂L/∂a = 2a + 0 = 2·1 = 2                       ✓
Derivada parcial respecto a 'b' (a se trata como constante):
   ∂L/∂b = 0 + 3 = 3

Cada variable recibe su propia derivada parcial, en paralelo. Esto escala a millones de pesos sin cambiar nada del código.

4. Apagar autograd: no_grad y detach

Grabar el grafo cuesta memoria y tiempo. Cuando no vas a entrenar (por ejemplo, al hacer predicciones), conviene apagarlo:

w = torch.tensor(5.0, requires_grad=True)

(w * 2).requires_grad              # normal: graba

with torch.no_grad():               # bloque sin grabación
    (w * 2).requires_grad

(w * 2).detach().requires_grad   # desconecta un tensor del grafo
salida realnormal: True dentro de no_grad: False detach(): False
Usa with torch.no_grad(): al evaluar o predecir — ahorra memoria y va más rápido porque no construye el grafo. .detach() te da una copia de un tensor "desconectada" del grafo (útil para guardar valores sin afectar el entrenamiento).

5. ¡Cuidado! Los gradientes se ACUMULAN

Un detalle que sorprende a todos: si llamas backward() dos veces, los gradientes se suman en vez de reemplazarse:

x = torch.tensor(1.0, requires_grad=True)
(x * 3).backward()
print(x.grad)        # 1er backward
(x * 3).backward()
print(x.grad)        # 2do backward... ¡acumuló!
salida real1er backward → x.grad = 3.0 2do backward → x.grad = 6.0 (3 + 3, ¡no 3!)
⚠️ Por esto el bucle de entrenamiento siempre empieza con optimizer.zero_grad() (lo verás en la lección 7): hay que poner los gradientes a cero antes de cada paso, o se irían sumando los de pasos anteriores y el entrenamiento se rompería. Es el error olvidado más típico.

6. Conexión con tu curso de redes

¿Recuerdas que en el curso de redes calculamos el backprop de XOR a mano, número por número? Eso es exactamente lo que hace backward() — y de hecho lo verificamos: daba los mismos gradientes. Autograd no es magia, es la regla de la cadena automatizada. Ahora que entiendes tensores, operaciones y autograd, tienes los 3 fundamentos. En la lección 5 empezamos a construir redes de verdad con nn.Module.