Juan Diego Andrés PRADA··RAMÍREZ Entrar
Lección 4 de 7

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

  1. Lee la brecha train/val. Modifica el loop para calcular también la loss sobre train al final de cada época e imprímela junto a la de validación. Sube n_hidden a 256 y entrena 200 épocas. ¿La brecha entre train y validación crece? Interpreta: ¿qué te dice esa brecha sobre el sobreajuste?
  2. 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.
Tu progreso se guarda en este navegador. Inicia sesión para guardarlo en tu cuenta y verlo desde cualquier dispositivo.