🏁 PROYECTO FINAL

Predicción de precios de vivienda

El cierre del curso: un proyecto tabular completo, de los datos a una predicción real, juntando TODO lo aprendido. Cada salida que verás es de una ejecución real en PyTorch.

1. El problema

Queremos predecir el precio de una vivienda a partir de 4 características: tamaño (m²), habitaciones, antigüedad (años) y ubicación (puntuación 1–10). Es un problema de regresión (predecir un número continuo) — el tipo más común en datos de negocio.

📊 Usamos un dataset sintético pero realista (600 viviendas generadas con una fórmula + ruido), para que el proyecto sea reproducible sin descargas. El pipeline es idéntico al que usarías con un CSV real.

2. Los datos

# cada fila: [tamaño, habitaciones, antigüedad, ubicación] → precio
X.shape = (600, 4)      y.shape = (600, 1)
salida real (un ejemplo)features [tamaño=114.9, hab=4, antigüedad=7, ubicación=5] → precio = 389.672 €

3. Paso 1 — Dividir en train / validación / test

La regla de oro de la generalización: separar antes de tocar nada (70% / 15% / 15%):

n1, n2 = int(0.7*N), int(0.85*N)
Xtr, Xva, Xte = X[:n1], X[n1:n2], X[n2:]
ytr, yva, yte = y[:n1], y[n1:n2], y[n2:]
salida realtrain: 420 | val: 90 | test: 90

4. Paso 2 — Normalizar (el paso que todos olvidan)

Las features están en escalas muy distintas: el tamaño ronda 100, la ubicación 1–10. Sin normalizar, la red da más "peso" a las magnitudes grandes y entrena mal. Las estandarizamos (media 0, desviación 1) usando solo las estadísticas del train:

mu, sd = Xtr.mean(0), Xtr.std(0)     # stats SOLO de train
Xtr = (Xtr - mu) / sd                # aplicar a los tres conjuntos
Xva = (Xva - mu) / sd
Xte = (Xte - mu) / sd
salida real (medias de las features en train)[tamaño=100.7, hab=2.93, antigüedad=19.55, ubicación=5.55]
🔑 Crítico: calcula mu y sd con el train y aplícalos a val/test. Si usaras las estadísticas de test, estarías "filtrando" información del test al entrenamiento (data leakage) y tus resultados serían mentira. También normalizamos el precio para estabilizar el entrenamiento, y lo des-normalizamos al final.

5. Paso 3 — El modelo

Una red de 2 capas ocultas con ReLU. Entrada 4 features → salida 1 número (el precio):

net = nn.Sequential(
    nn.Linear(4, 32), nn.ReLU(),
    nn.Linear(32, 16), nn.ReLU(),
    nn.Linear(16, 1),       # 1 salida: sin activación (regresión)
)
opt = torch.optim.Adam(net.parameters(), lr=0.01)
loss_fn = nn.MSELoss()        # regresión → MSE

6. Paso 4 — Entrenar (vigilando validación)

dl = DataLoader(TensorDataset(Xtr, ytr), batch_size=32, shuffle=True)

for epoca in range(1, 51):
    net.train()
    for xb, yb in dl:
        opt.zero_grad()
        loss_fn(net(xb), yb).backward()
        opt.step()
    net.eval()
    with torch.no_grad():
        val = loss_fn(net(Xva), yva)
salida real (pérdida normalizada)epoca 1: train=0.1474 val=0.1840 epoca 10: train=0.0597 val=0.0537 epoca 25: train=0.0445 val=0.0521 epoca 50: train=0.0415 val=0.0566

Train y val bajan juntas y se estabilizan: la red aprendió y generaliza bien (la val no se dispara, así que no hay overfitting serio). Reconoces cada línea de este bucle: son los 5 pasos de la lección 7, con lotes (lección 8) y train/eval (lección 9).

7. Paso 5 — Evaluar en test (en euros de verdad)

La pérdida MSE normalizada no dice mucho a un humano. Calculamos el error absoluto medio (MAE) en euros reales, des-normalizando las predicciones:

net.eval()
with torch.no_grad():
    pred = net(Xte).numpy() * ysd + ymu      # des-normalizar a euros
mae = np.abs(pred - yte).mean()
salida realtest MAE = 13.287 €
Sobre precios de cientos de miles de euros, fallar de media ~13.000 € (un ~3%) es un resultado muy bueno. Y lo medimos en el conjunto de test, que el modelo nunca vio durante el entrenamiento — es una evaluación honesta.

8. Paso 6 — Predecir una vivienda nueva

El objetivo final: estimar el precio de una vivienda que no está en los datos. Hay que aplicarle la misma normalización y des-normalizar la salida:

nueva = [[120, 3, 5, 8]]      # 120m², 3 hab, 5 años, ubicación 8
nueva = (nueva - mu) / sd          # misma normalización que el train
with torch.no_grad():
    precio = net(nueva).item() * ysd + ymu
salida realvivienda [120m², 3 hab, 5 años, ubicación 8] → precio estimado: 412.361 €
✍️ Verificación con la fórmula que generó los datos
Los precios se crearon con:
   precio = 1500·tamaño + 20000·hab − 800·antigüedad + 15000·ubicación + 50000

Para [tamaño=120, hab=3, antigüedad=5, ubicación=8]:
   1500·120        = 180.000
   20000·3         =  60.000
   −800·5          =  −4.000
   15000·8         = 120.000
   + base          =  50.000
   ─────────────────────────
   precio "real"   = 406.000 €

La red, que NUNCA vio esta fórmula (solo ejemplos con ruido), predijo
412.361 € → error de solo ~6.000 € (<2%). ¡Dedujo la relación casi exacta!

9. 🎓 Lo que usaste de TODO el curso

Paso del proyectoLección
Tensores y operaciones2, 3
El modelo con nn.Module / nn.Linear5
ReLU y MSELoss6
Optimizador Adam y bucle de 5 pasos7
Dataset y DataLoader (lotes)8
train/eval, no_grad, validación9
Vigilar overfitting con validación9 + curso de redes
¡Completaste el curso de PyTorch! Empezaste creando un tensor y terminaste entrenando una red que predice precios con ~3% de error, entendiendo cada paso. Este mismo pipeline — datos → normalizar → split → modelo → entrenar → evaluar → predecir — es el que se usa en proyectos reales de la industria, cambiando solo los datos y el tamaño del modelo. 🚀