Hasta ahora movimos tensores a mano. Ahora dejamos que PyTorch organice los pesos por nosotros con capas (nn.Linear) y modelos (nn.Module) — la forma profesional de construir redes.
z = x @ W + b y que los gradientes se calculan con .backward(). Hoy dejamos que PyTorch gestione los pesos por nosotros.
En el curso de redes, una capa era z = x @ W + b, y tú creabas y gestionabas
W y b. nn.Linear(entradas, salidas) hace exactamente eso,
pero crea y guarda los pesos por ti (inicializados al azar):
import torch.nn as nn
lin = nn.Linear(3, 2) # 3 entradas → 2 salidas
print(lin.weight.shape) # matriz de pesos
print(lin.bias.shape) # vector de bias
Pasarle una entrada es solo "llamar" a la capa como si fuera una función (en Python, cuando una clase define el método especial __call__, sus objetos se pueden usar como objeto(argumento) — nn.Module lo hace, por eso lin(x) funciona):
x = torch.tensor([[1.0, 2.0, 3.0]]) # forma (1, 3)
print(lin(x))
nn.Linear calcula x @ Wᵀ + b internamente (la T pequeña indica que la matriz se transpone — es un detalle de implementación que PyTorch gestiona solo, no tienes que preocuparte). La gran ventaja: sus pesos
son parámetros entrenables que PyTorch rastrea automáticamente para el optimizador.
Tú ya no creas tensores con requires_grad a mano.
Una red es varias capas conectadas. Se define como una clase que hereda de nn.Module,
con dos partes: __init__ (declarar las capas) y forward (cómo fluyen los
datos):
class Net(nn.Module):
def __init__(self):
super().__init__() # obligatorio: configura la maquinaria interna de nn.Module
self.fc1 = nn.Linear(2, 4) # capa oculta: 2 → 4
self.fc2 = nn.Linear(4, 1) # capa salida: 4 → 1
def forward(self, x):
h = torch.relu(self.fc1(x)) # capa 1 + activación
return self.fc2(h) # capa 2 (salida)
net = Net()
print(net)
forward directamente. Usas net(x) y
PyTorch llama a forward por dentro (haciendo trabajo extra necesario). Llamar
net.forward(x) a mano es un error común que rompe algunas funcionalidades.
nn.Module rastrea automáticamente todos los pesos y bias de todas sus capas:
sum(p.numel() for p in net.parameters())
Comprobemos: capa 1 = (2×4 pesos) + 4 bias = 12; capa 2 = (4×1) + 1 = 5. Total 17. ✅
Esa lista de parameters() es justo lo que le pasarás al optimizador (lección 7) para
que los ajuste.
| A mano (curso de redes) | Con nn.Module |
|---|---|
| Creas cada W, b con requires_grad | Las capas los crean solas |
| Tú llevas la cuenta de los parámetros | .parameters() los reúne |
| Inicialización manual | Inicialización sensata automática |
| Fácil equivocarse con las formas | Capas estándar probadas |
nn.Linear(n, m) crea una capa con sus pesos y bias (calcula x @ Wᵀ + b). nn.Module agrupa capas: defines las capas en __init__ y el flujo en forward. Usas net(x) para predecir (nunca net.forward(x) directamente) y net.parameters() para acceder a todo lo entrenable.
En la próxima lección: las activaciones y funciones de pérdida que ofrece torch.nn.