Redes Neuronales
¿Qué es?
Una red neuronal es un modelo formado por capas de transformaciones lineales intercaladas con funciones de activación no lineales. Una sola capa lineal solo puede trazar fronteras rectas; apilando capas con no linealidades, la red puede aprender relaciones curvas y complejas entre las features y la respuesta. La regresión logística del módulo anterior es, de hecho, una red neuronal de una sola capa.
¿Cómo funciona?
Cada capa toma su entrada, la multiplica por una matriz de pesos, suma un sesgo y aplica una activación (como ReLU, que convierte los negativos en cero). El resultado pasa a la siguiente capa. Al final, una capa de salida produce los logits. El entrenamiento es el mismo bucle que ya conoces —forward, loss, backward, paso— pero ahora PyTorch nos da las herramientas de alto nivel para no escribirlo a mano:
nn.Module: define la arquitectura del modelo.nn.Linear,nn.ReLU: las capas.- Una loss lista (
nn.BCEWithLogitsLoss). - Un optimizer (
torch.optim.Adam) que aplica el paso de descenso por nosotros.
¿Para qué sirve?
Sirve para capturar patrones que un modelo lineal no puede. Cuando la relación entre tus datos y la respuesta no es una línea recta, las capas ocultas de la red la modelan. Es la base de casi todo el deep learning moderno.
La pieza del proyecto que construimos aquí
Construimos el modelo definitivo del proyecto y su loop de entrenamiento profesional: una red con una capa oculta que clasifica el riesgo académico, entrenada con DataLoader, optimizador y una separación de validación para vigilar el sobreajuste.
Paso 1: el modelo con nn.Module
import torch
import torch.nn as nn
class RedRiesgo(nn.Module):
def __init__(self, n_features, n_hidden=16):
super().__init__()
self.red = nn.Sequential(
nn.Linear(n_features, n_hidden),
nn.ReLU(),
nn.Linear(n_hidden, 1), # 1 logit de salida
)
def forward(self, x):
return self.red(x) # devolvemos logits, sin sigmoide
modelo = RedRiesgo(n_features=X_train_t.shape[1])
print(modelo)
La salida son logits (números sin acotar), no probabilidades. La sigmoide la aplicará la loss internamente; es más estable numéricamente.
Paso 2: datos por lotes con DataLoader
from torch.utils.data import TensorDataset, DataLoader
train_ds = TensorDataset(X_train_t, y_train_t)
train_dl = DataLoader(train_ds, batch_size=32, shuffle=True)
Tech English: batch (lote, un subconjunto de ejemplos por paso), shuffle (barajar), hidden layer (capa oculta), activation function (función de activación).
Paso 3: loss, optimizer y loop de entrenamiento
loss_fn = nn.BCEWithLogitsLoss()
optimizer = torch.optim.Adam(modelo.parameters(), lr=0.01)
for epoch in range(50):
modelo.train()
for xb, yb in train_dl:
optimizer.zero_grad() # limpia gradientes
logits = modelo(xb) # forward
loss = loss_fn(logits, yb) # error
loss.backward() # gradientes
optimizer.step() # actualiza pesos
# Validación al final de cada época
modelo.eval()
with torch.no_grad():
val_logits = modelo(X_test_t)
val_loss = loss_fn(val_logits, y_test_t)
val_pred = (torch.sigmoid(val_logits) > 0.5).float()
val_acc = (val_pred == y_test_t).float().mean()
if epoch % 10 == 0:
print(f"epoch {epoch:2d} val_loss {val_loss.item():.4f} val_acc {val_acc.item():.3f}")
Vigila el sobreajuste. Si la loss de train sigue bajando pero la de validación empieza a subir, el modelo está memorizando en vez de generalizar. Esa divergencia es la señal de alarma; cuando aparece, conviene parar (early stopping) o reducir el tamaño de la red.
optimizer.zero_grad() cumple el mismo papel que el grad.zero_() manual del módulo anterior. Olvidarlo vuelve a acumular gradientes y rompe el entrenamiento.
modelo.train() y modelo.eval() cambian el comportamiento de capas como Dropout o BatchNorm. Aunque esta red no las use todavía, acostúmbrate a alternarlas: es buena higiene.
Paso 4: guardar el modelo entrenado
torch.save(modelo.state_dict(), "modelo_riesgo.pt")
print("Modelo guardado.")
Este archivo lo retomaremos en el módulo de despliegue.
Ejercicios
- Lee la brecha train/val. Modifica el loop para calcular también la
losssobre train al final de cada época e imprímela junto a la de validación. Suben_hiddena256y entrena 200 épocas. ¿La brecha entre train y validación crece? Interpreta: ¿qué te dice esa brecha sobre el sobreajuste? - Compara con el baseline. Toma el accuracy de validación de esta red y compáralo con el del baseline trivial del módulo 1 y con la regresión logística del módulo 2. ¿La capa oculta aportó algo real, o estos datos eran tan lineales que no hacía falta? Justifica con los números.