LECCIÓN 9

PyTorch y autograd

Hiciste backprop a mano en la lección 7. Ahora deja que PyTorch lo haga por ti — y comprueba con tus propios ojos que calcula EXACTAMENTE los mismos números, automáticamente.

Recuerda de las lecciones anteriores: en la lección 7 calculaste a mano los gradientes de los 9 pesos de XOR — un proceso largo pero claro. En la lección 8, esos gradientes se usaron para entrenar la red hasta que resolvió XOR. Hoy PyTorch reproduce exactamente esos mismos gradientes con una sola línea.

1. ¿Qué cambia con PyTorch?

Hasta ahora calculaste cada derivada a mano. En la práctica nadie hace eso: lo hace autograd, el motor de diferenciación automática (calcular derivadas automáticamente, sin que tú escribas las fórmulas) de PyTorch.

Tú escribes solo el forward; PyTorch lleva un "diario de anotaciones": cada vez que haces una suma o multiplicación, apunta qué pesos participaron. Al llamar loss.backward(), lee ese diario al revés — ese diario es lo que los técnicos llaman el grafo de operaciones — y aplica la regla de la cadena automáticamente. Es exactamente lo que hiciste en la lección 7, pero sin cálculo manual.

Tensor + requires_grad=True le dice a PyTorch "vigila este valor, quiero su gradiente". Tras loss.backward(), cada tensor tendrá su gradiente en .grad. Cero cálculo manual.

2. Reproducimos el forward de XOR con tensores

Usamos los mismos pesos canónicos y la misma entrada x = (1, 0):

import torch

# Pesos canonicos, marcados como entrenables
W1 = torch.tensor([[0.20,-0.30],[0.40,0.10]], requires_grad=True)
b1 = torch.tensor([-0.10, 0.20],              requires_grad=True)
W2 = torch.tensor([[0.50],[-0.40]],           requires_grad=True)
b2 = torch.tensor([0.10],                     requires_grad=True)

x = torch.tensor([1., 0.])
t = torch.tensor([1.])

# Forward (idéntico a la lección 4)
a1 = torch.sigmoid(x @ W1 + b1)      # capa oculta
a2 = torch.sigmoid(a1 @ W2 + b2)     # salida
loss = 0.5 * (a2 - t)**2             # MSE ½
a2 = 0.543014   loss = 0.104418   ← idéntico a las lecciones 4 y 5 ✅

3. Una línea reemplaza toda la lección 7

loss.backward()   # autograd calcula TODOS los gradientes

print(W2.grad)    # gradiente de los pesos de salida
print(b2.grad)
print(W1.grad)    # gradiente de los pesos de entrada
print(b1.grad)

PyTorch imprime:

W2.grad = [-0.059533, -0.053868]
b2.grad = [-0.113401]
W1.grad = [[-0.014140, 0.011312], [-0.000000, 0.000000]]
b1.grad = [-0.014140, 0.011312]

4. La comprobación que lo demuestra todo

Comparemos, lado a lado, lo que calculaste a mano (lección 7) con lo que dio autograd:

GradienteA mano (lección 7)autograd (PyTorch)
dL/dW₂[−0.059533, −0.053868][−0.059533, −0.053868]
dL/db₂−0.113401−0.113401
dL/dW₁ (de x₁)[−0.014140, 0.011312][−0.014140, 0.011312]
dL/db₁[−0.014140, 0.011312][−0.014140, 0.011312]
# Verificación automática
assert torch.allclose(W2.grad.flatten(),
                      torch.tensor([-0.059533, -0.053868]), atol=1e-5)
# ✅ pasa — autograd == tu backprop a mano
Coinciden hasta el último decimal. Esto no es coincidencia: autograd es la regla de la cadena de la lección 7, aplicada por el computador. Entender backprop a mano te da la intuición; PyTorch te da la velocidad y la escala. Lo mejor de los dos mundos.

5. Lo que aprendiste

Lo que aprendiste hoy: PyTorch autograd calcula las derivadas automáticamente — tú solo escribes el forward. Con requires_grad=True le dices a PyTorch que vigile ese tensor. Con loss.backward() calcula todos los gradientes. Los números coinciden exactamente con los calculados a mano en la lección 7: autograd es la regla de la cadena implementada por el computador.

Una red real tiene millones de pesos — derivar a mano sería imposible. Autograd funciona con cualquier forward que escribas, por complejo que sea (incluyendo el Transformer completo).

En la última lección: construir la red con las herramientas de alto nivel de PyTorch (nn.Module, nn.Linear) y entrenarla en pocas líneas.