El cierre. Todo lo que aprendiste a mano, ahora en su forma profesional: definir la red con nn.Module, elegir un optimizador y entrenar XOR en unas pocas líneas hasta resolverlo.
loss.backward(). Hoy usamos las herramientas de alto nivel de PyTorch para hacer todo esto en pocas líneas.
nn.Module es la forma de PyTorch de definir una red. Piénsalo como una ficha técnica: describes los ingredientes en __init__ (qué capas tiene) y el proceso en forward (cómo fluyen los datos). Cada nn.Linear(entradas, salidas) es la operación x @ W + b de la lección 3 — PyTorch guarda los pesos y los inicializa al azar por ti.
import torch import torch.nn as nn class XORNet(nn.Module): def __init__(self): super().__init__() self.fc1 = nn.Linear(2, 2) # capa oculta: 2 → 2 self.fc2 = nn.Linear(2, 1) # capa salida: 2 → 1 def forward(self, x): h = torch.sigmoid(self.fc1(x)) # oculta + activación y = torch.sigmoid(self.fc2(h)) # salida + activación return y net = XORNet()
nn.Linear(2,2) es
literalmente la operación x @ W + b de la lección 3. Solo que ahora PyTorch guarda y
actualiza los pesos por ti.
# Las 4 filas de la tabla de verdad de XOR X = torch.tensor([[0.,0.],[0.,1.],[1.,0.],[1.,1.]]) Y = torch.tensor([[0.], [1.], [1.], [0.]]) loss_fn = nn.MSELoss() # pérdida (lección 5) optim = torch.optim.Adam(net.parameters(), lr=0.1) # el que da los pasos
El optimizador automatiza la regla w = w − lr·gradiente de la lección 6. Usamos Adam en vez del descenso simple. ¿Qué lo hace diferente? Adam recuerda cuánto se ha movido cada peso en los pasos anteriores y ajusta el tamaño del salto: si un peso ha cambiado mucho, Adam es más cauteloso; si apenas se ha movido, da un paso más grande. Resultado: converge mucho más rápido (sin la meseta lenta de la lección 8).
torch.manual_seed(1) # PyTorch inicializa los pesos al azar; fijar la semilla hace reproducible el resultado for epoca in range(2001): optim.zero_grad() # 0. limpiar gradientes del paso anterior (PyTorch los acumula; si no los limpias, se suman y los números se distorsionan) pred = net(X) # 1. forward → predicciones loss = loss_fn(pred, Y) # 2. pérdida loss.backward() # 3. backward → gradientes (autograd) optim.step() # 4. actualizar pesos
¿Reconoces los 4 pasos? Son exactamente el ciclo de la lección 8. Esto imprime:
print(net(X)) # predicciones finales
| x₁ | x₂ | XOR (target) | predicción de la red |
|---|---|---|---|
| 0 | 0 | 0 | 0.006 ✓ |
| 0 | 1 | 1 | 0.996 ✓ |
| 1 | 0 | 1 | 0.996 ✓ |
| 1 | 1 | 0 | 0.004 ✓ |
seed=1. En redes
reales esto se mitiga con más neuronas y mejores inicializaciones — pero es bueno que sepas que
el entrenamiento no siempre es mágico al primer intento.
nn.Module permite definir la red describiendo ingredientes (__init__) y proceso (forward). nn.Linear(2,2) es exactamente la operación x·W+b de la lección 3. Adam mejora el descenso simple adaptando el paso de cada peso. Con 2001 épocas y Adam (lr=0.1), XOR se resuelve completamente: predicciones 0.006, 0.996, 0.996, 0.004.
| Lección | Concepto |
|---|---|
| 1 | La neurona: suma ponderada (w·x + b) |
| 2 | Activación: la chispa no-lineal (sigmoid) |
| 3 | Capa = multiplicación de matrices |
| 4 | Forward pass: el viaje completo del dato |
| 5 | Pérdida: medir el error con un número |
| 6 | Gradiente: la pendiente que indica por dónde bajar |
| 7 | Backpropagation: la regla de la cadena hacia atrás |
| 8 | Entrenamiento: repetir el ciclo miles de veces |
| 9 | Autograd: PyTorch reproduce tu backprop, automático |
| 10 | nn.Module: la red completa, lista para escalar |