Juntamos todo lo aprendido en un entrenamiento de verdad: datos, lotes, bucle, validación para vigilar el overfitting, y cómo guardar y recargar el modelo entrenado.
Un entrenamiento real combina todo el bloque intermedio:
Dataset + DataLoader (lección 8)nn.Module (lección 5)Predeciremos y = 2·x₁ − 3·x₂ + 1 (con ruido) a partir de 200 ejemplos, 160 para entrenar y 40 para validar:
# datos: 200 ejemplos con 2 features
X = torch.randn(200, 2)
y = (2*X[:,0] - 3*X[:,1] + 1).reshape(-1, 1)
Xtr, Ytr = X[:160], y[:160] # entrenamiento
Xva, Yva = X[160:], y[160:] # validación
tr_dl = DataLoader(TensorDataset(Xtr, Ytr), batch_size=32, shuffle=True)
net = nn.Sequential(nn.Linear(2, 16), nn.ReLU(), nn.Linear(16, 1))
opt = torch.optim.Adam(net.parameters(), lr=0.05)
loss_fn = nn.MSELoss()
for epoca in range(1, 41):
net.train() # modo entrenamiento
for xb, yb in tr_dl: # bucle de lotes
opt.zero_grad()
loss = loss_fn(net(xb), yb)
loss.backward()
opt.step()
net.eval() # modo evaluación
with torch.no_grad(): # sin gradientes al validar
val_loss = loss_fn(net(Xva), Yva)
| Llamada | Cuándo | Qué hace |
|---|---|---|
net.train() | antes de entrenar | Activa dropout y BatchNorm en modo entrenamiento |
net.eval() | antes de validar/predecir | Los pone en modo evaluación (comportamiento determinista) |
net.eval() con with torch.no_grad(): al validar o
predecir: eval() ajusta capas como dropout, y no_grad() ahorra memoria al
no construir el grafo. Olvidar eval() da resultados peores e inconsistentes.
Entrenar cuesta tiempo; no quieres repetirlo cada vez. Se guarda el state_dict (un
diccionario con todos los pesos):
# guardar
torch.save(net.state_dict(), 'modelo.pt')
# cargar (hay que recrear la MISMA arquitectura primero)
net2 = nn.Sequential(nn.Linear(2,16), nn.ReLU(), nn.Linear(16,1))
net2.load_state_dict(torch.load('modelo.pt'))
¿Qué hay dentro del state_dict? Una entrada por cada peso y bias de cada capa:
Tras cargar, el modelo predice igual que el original:
state_dict (solo los pesos), no el objeto entero. Por eso al cargar
debes recrear la arquitectura idéntica y luego inyectarle los pesos. Es la práctica
recomendada en PyTorch.
train()/eval(), y guardando con state_dict. Con esto cierras
el nivel intermedio: ya puedes construir y entrenar redes funcionales. El nivel
avanzado (GPU, regularización, CNNs, transfer learning) son mejoras sobre esta misma base.