LECCIÓN 5 · CONSTRUIR Y ENTRENAR

nn.Module y capas

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.

Recuerda de las lecciones anteriores: tienes tensores, operaciones y autograd. Ya sabes que una capa de red hace z = x @ W + b y que los gradientes se calculan con .backward(). Hoy dejamos que PyTorch gestione los pesos por nosotros.

1. nn.Linear: una capa lista para usar

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
salida realweight shape: (2, 3) bias shape: (2,)

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))
salida realtensor([[-0.8219, 0.0526]])
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.

2. nn.Module: agrupar capas en un modelo

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)
salida realNet( (fc1): Linear(in_features=2, out_features=4, bias=True) (fc2): Linear(in_features=4, out_features=1, bias=True) )
🔑 Tú nunca llamas a 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.

3. Los parámetros: lo que la red aprende

nn.Module rastrea automáticamente todos los pesos y bias de todas sus capas:

sum(p.numel() for p in net.parameters())
salida realtotal params: 17

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.

4. Lo que aprendiste

A mano (curso de redes)Con nn.Module
Creas cada W, b con requires_gradLas capas los crean solas
Tú llevas la cuenta de los parámetros.parameters() los reúne
Inicialización manualInicialización sensata automática
Fácil equivocarse con las formasCapas estándar probadas
Lo que aprendiste hoy: 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.