IA para Científicos Sociales

Sesión 2.2: Regresión y predicción

Danilo Freire

Department of Data and Decision Sciences
Emory University

Regresión y predicción

Agenda de la sesión

Primera parte

  • Predicción vs. explicación: las “dos culturas”
  • Regresión lineal y sus limitaciones
  • El problema del sobreajuste revisitado
  • Regularización: LASSO, Ridge, Elastic Net

Segunda parte

  • Ajuste de hiperparámetros (tuning)
  • Ingeniería de variables para datos sociales
  • Aplicaciones en ciencias sociales
  • Comparación con otros métodos

Predicción vs. explicación

El debate central en ciencias sociales

  • Breiman (2001), “Statistical Modeling: The Two Cultures”:
    • Modelo de datos: se asume un modelo generador, se estiman parámetros (\(\hat{\beta}\)). Foco en identificación causal
    • Modelo algorítmico: caja negra, se optimiza la predicción (\(\hat{y}\)). Foco en generalización a datos nuevos
  • En ciencias sociales solemos explicar, pero predecir deserción escolar o focalizar intervenciones también requiere buenos modelos

Ejemplos

Pregunta Tipo
¿Las becas aumentaron la asistencia escolar? Explicación
¿Qué estudiantes van a desertar? Predicción
¿La vacunación redujo la mortalidad? Explicación
¿Qué pacientes necesitan cuidados intensivos? Predicción


¡Ambos son valiosos! El ML aporta herramientas de predicción que complementan los métodos causales

¿Por qué la regresión lineal “clásica” no es suficiente para predecir?

  • La regresión lineal (OLS) minimiza el error en los datos de entrenamiento
  • Pero si tenemos muchas variables relativas al número de observaciones, OLS se ajusta demasiado al ruido
  • Ejemplo: con 50 variables y 100 observaciones, OLS encuentra coeficientes que parecen funcionar pero no generalizan
  • OLS no tiene un mecanismo para descartar variables irrelevantes ni para reducir coeficientes inflados
  • Solución: regularización, que agrega una penalización por la magnitud de los coeficientes

A medida que agregamos variables, el error de entrenamiento baja pero el error de test sube: el modelo memoriza el ruido

Regularización

La idea de la regularización

  • Regularización = agregar una penalización por la complejidad del modelo
  • En la regresión OLS, minimizamos el error:

\[\min \sum_{i=1}^{n} (y_i - \hat{y}_i)^2\]

  • Con regularización, agregamos un término de penalización:

\[\min \sum_{i=1}^{n} (y_i - \hat{y}_i)^2 + \lambda \cdot \text{penalización}(\beta)\]

  • \(\lambda\) controla la fuerza de la penalización:
    • \(\lambda = 0\): sin penalización (OLS clásico)
    • \(\lambda\) grande: penalización fuerte (coeficientes se reducen hacia 0)
  • El truco está en elegir el \(\lambda\) óptimo (por validación cruzada)

Elegir λ es moverse por el equilibrio sesgo-varianza

  • λ chico → bajo sesgo, alta varianza (sobreajuste)
  • λ grande → alto sesgo, baja varianza (subajuste)
  • El mejor λ está en el punto intermedio

LASSO (L1) y Ridge (L2)

LASSO (L1, Tibshirani, 1996): suma de valores absolutos

\[\min \sum (y_i - \hat{y}_i)^2 + \lambda \sum |\beta_j|\]

  • Reduce algunos coeficientes exactamente a cero → selecciona variables automáticamente
  • Útil cuando solo algunas variables son relevantes

Ridge (L2): suma de cuadrados

\[\min \sum (y_i - \hat{y}_i)^2 + \lambda \sum \beta_j^2\]

  • No elimina variables: las reduce pero nunca a cero exacto
  • Útil con variables correlacionadas (multicolinealidad)

LASSO vs Ridge con λ = 0.1

                LASSO    Ridge
edad             0.023   0.018
educacion        0.085   0.072
ingreso          0.041   0.035
zona             0.000   0.008
genero           0.000   0.003
confianza_gob    0.019   0.015
satisf_democ     0.052   0.044
percepcion_econ  0.000   0.009

LASSO eliminó 3 variables;
Ridge mantuvo todas con
coeficientes pequeños

La penalización, con números

  • La penalización es una función: todos los coeficientes → un número
  • Ese número mide qué tan grandes son los coeficientes juntos
  • Se suma al error: el modelo minimiza error + λ · penalización
  • Se recalcula en cada combinación que el optimizador prueba
  • Un coeficiente grande baja el error, pero sube la penalización
  • Con λ grande no compensa, gana la simplicidad

Con los coeficientes anteriores:

penalización(β): el tamaño de los coeficientes (sin λ)
  LASSO:  Σ|βⱼ|  = |0.023|+|0.085|+...+0  = 0.220
  Ridge:  Σβⱼ²   = 0.018²+0.072²+...+0.009² = 0.009

término que se suma al error = λ · penalización
  (el mismo λ = 0.1, una sola vez)
  LASSO:  0.1 · 0.220 = 0.022
  Ridge:  0.1 · 0.009 = 0.0009
  • Los coeficientes ya son la solución para λ = 0.1; multiplicar por λ no penaliza de nuevo, solo calcula el valor de ese término
  • Al elevar al cuadrado, Ridge castiga sobre todo a los coeficientes grandes y casi ignora los chicos: por eso nunca los lleva a cero exacto
  • El valor absoluto de LASSO cobra lo mismo por unidad sin importar el tamaño: por eso empuja los chicos hasta cero

Elastic Net: lo mejor de ambos

\[\min \sum (y_i - \hat{y}_i)^2 + \lambda \left[ \alpha \sum |\beta_j| + (1-\alpha) \sum \beta_j^2 \right]\]

  • Dos hiperparámetros:
    • \(\lambda\): fuerza general de la penalización
    • \(\alpha\): mezcla entre LASSO y Ridge
    • \(\alpha = 1\): LASSO puro
    • \(\alpha = 0\): Ridge puro
    • \(0 < \alpha < 1\): Elastic Net
  • Selecciona variables (como LASSO) pero maneja mejor las correlaciones (como Ridge)
  • En la práctica, Elastic Net suele funcionar mejor que LASSO o Ridge por separado
LASSO Ridge Elastic Net
Penalización L1 (\(|\beta|\)) L2 (\(\beta^2\)) L1 + L2
Selección No
Correlación Problemas Bien Bien
Hiperparámetros \(\lambda\) \(\lambda\) \(\lambda\), \(\alpha\)


Regla general:

  • Pocas variables importantes → LASSO
  • Muchas variables correlacionadas → Ridge
  • No estoy seguro → Elastic Net

El sesgo de la regularización

  • OLS es insesgado: bajo los supuestos clásicos (Gauss-Markov), en promedio sus coeficientes aciertan el efecto verdadero
  • Ridge, LASSO y Elastic Net son sesgados a propósito: la penalización empuja todos los coeficientes hacia cero (shrinkage)
  • Ese sesgo es el precio que pagamos por reducir la varianza y predecir mejor en datos nuevos
  • Un coeficiente penalizado ya no es una estimación insesgada del efecto: está encogido hacia cero
Objetivo Herramienta Coeficientes
Explicar OLS (MCO) Insesgados, interpretables
Predecir LASSO, Ridge, Elastic Net Sesgados, no causales


  • Para estimar el efecto de una variable: OLS
  • Para la mejor predicción: regularización
  • La regularización vive del lado de predecir, no de explicar

Regularización en R

Con tidymodels + glmnet:

# LASSO (mixture = 1)
modelo_lasso <- linear_reg(penalty = 0.1, mixture = 1) |>
  set_engine("glmnet") |>
  set_mode("regression")

# Ridge (mixture = 0)
modelo_ridge <- linear_reg(penalty = 0.1, mixture = 0) |>
  set_engine("glmnet") |>
  set_mode("regression")

# Elastic Net (mixture entre 0 y 1)
modelo_enet <- linear_reg(penalty = 0.1, mixture = 0.5) |>
  set_engine("glmnet") |>
  set_mode("regression")

# Ajustar cualquiera de ellos (misma sintaxis)
ajuste <- fit(modelo_lasso, satisfaccion_vida ~ ., data = datos_train)
  • penalty = \(\lambda\) (fuerza de la penalización)
  • mixture = \(\alpha\) (1 = LASSO, 0 = Ridge, entre 0 y 1 = Elastic Net)
  • En la práctica, usamos validación cruzada para encontrar los mejores valores de penalty y mixture

Ingeniería de variables

Ingeniería de variables para datos sociales

Crear, transformar o seleccionar variables para mejorar el modelo:

  • Variables categóricas: dummies (one-hot encoding)
  • Variables ordinales: ¿numéricas o categóricas?
  • Interacciones: productos entre variables (edad * educacion)
  • Transformaciones: log(ingreso), edad^2 para no linealidades
  • Discretización: edad → grupos etarios
  • Imputación: reemplazar valores faltantes (mediana, KNN)
  • https://recipes.tidymodels.org/reference/index.html

Con recipes de tidymodels:

# Los pasos más comunes (hay muchos más step_*: ver el enlace de arriba)
receta <- recipe(voto ~ ., data = datos_train) |>
  step_impute_median(all_numeric_predictors()) |>   # imputar faltantes
  step_dummy(all_nominal_predictors()) |>           # categóricas → dummies
  step_normalize(all_numeric_predictors()) |>       # media=0, sd=1
  step_zv(all_predictors()) |>                      # quitar varianza cero
  step_interact(terms = ~ edad:educacion_anios)     # interacción edad×educación

Una receta es un plan, no los datos

  • recipe() no transforma nada todavía: guarda la lista de pasos para ejecutarla después
  • prep() calcula con los datos de entrenamiento los valores que cada paso necesita: medianas para imputar, medias y desvíos para normalizar
  • juice() devuelve el conjunto de entrenamiento ya transformado
  • bake() aplica esa misma receta a datos nuevos, con los valores aprendidos del entrenamiento
  • Separar el plan de su ejecución es lo que evita el data leakage: el test set nunca influye en las medianas ni en los desvíos
# Inspeccionar el resultado de la receta
receta |>
  prep() |>
  juice() |>
  glimpse()

# Aplicar la receta a datos nuevos
bake(prep(receta), new_data = datos_test)

Dentro de un workflow() esto pasa automáticamente. Usamos prep() y juice() solo para revisar que la receta haya hecho lo que esperábamos

El flujo completo: workflow()

  • En tidymodels, recipe y model se combinan en un workflow()
  • El workflow conecta el preprocesamiento con el modelo en un solo objeto
  • Ventaja: se aplica automáticamente a datos nuevos, sin repetir pasos manualmente
# Combinar receta + modelo en un workflow
# (en los labs lo verán como wf_<modelo>: 
# wf_lasso, wf_rf, etc.)
wf_lasso <- workflow() |>
  add_recipe(receta) |>
  add_model(modelo_lasso)

# Ajustar todo junto
ajuste <- fit(wf_lasso, data = datos_train)

# Predecir con datos nuevos
predicciones <- predict(ajuste,
                        new_data = datos_test)

Evaluación final con last_fit():

# Evaluar en el test set (una sola vez)
resultado <- last_fit(wf_lasso,
                      split = datos_split)

# Métricas finales
collect_metrics(resultado)

# Ver predicciones
collect_predictions(resultado)


last_fit() ajusta con todo el train set y evalúa con el test set en un solo paso. Es la forma recomendada de obtener las métricas finales del modelo

Es el fit() y el predict() de la izquierda en una sola llamada, pero sobre el split, no sobre un data frame: por eso el test se toca una sola vez

¿Qué hace un workflow() por dentro?

  • Un workflow() empaqueta la receta y el modelo en un solo objeto
  • Antes teníamos pasos sueltos: preparar la receta, aplicarla al train, ajustar el modelo, aplicar la receta al test, predecir. Es fácil equivocarse en el orden
  • El workflow los une: cuando llamamos a fit() o predict(), aplica la receta y después el modelo en el orden correcto, por sí solo
  • Garantiza que el test pase por exactamente la misma transformación que el train, con las medianas y desvíos aprendidos solo del train: así se evita el data leakage
  • Un solo objeto viaja por todo el flujo: ajustar, predecir, tunear y evaluar

Un objeto, todo el flujo:

   receta   +   modelo
        │
        ▼
     workflow()
        │
   ┌────┴─────┐
 fit()    last_fit()
   │          │
   ▼          ▼
 ajuste    métricas
   │        en test
   ▼
 predict(datos nuevos)

add_recipe() y add_model() son las dos piezas; el workflow sabe aplicarlas en orden

Ajuste de hiperparámetros para regularización

¿Cómo elegir λ (penalty)?

  • \(\lambda\) controla la fuerza de la regularización
  • El valor correcto depende de los datos, así que lo buscamos empíricamente

Estrategia práctica (3 pasos):

  1. Definir una grilla de valores candidatos de \(\lambda\) (en escala logarítmica, ej: \(10^{-4}\) a \(10^{0}\))
  2. Evaluar cada \(\lambda\) con validación cruzada (5 o 10 folds)
  3. Elegir el \(\lambda\) que minimice el error de validación (RMSE, MAE, etc.)

Consejo: tidymodels genera la grilla automáticamente con grid_regular(penalty(), levels = 30). No hace falta adivinar los valores 😉

¿Qué pasa al variar λ?

  • λ pequeño → muchas variables, riesgo de sobreajuste
  • λ grande → pocas variables, riesgo de subajuste
  • La curva en U nos muestra el punto medio donde el modelo generaliza mejor

Validación cruzada (recordatorio)

  • Ya la usamos en el Día 1 y en la sesión 2.1: en vez de una sola división train/test, la validación cruzada entrena y evalúa \(k\) veces (rotando el fold de validación) y promedia las métricas, para una estimación más estable
  • Acá la usamos para elegir \(\lambda\): evaluamos cada valor candidato con CV y nos quedamos con el mejor
  • En tidymodels: folds <- vfold_cv(datos_train, v = 5); después tune_grid() la usa por dentro
  • \(k\) = 5 o 10 es lo habitual: buena estimación sin gastar demasiado cómputo

Tuning de la penalización con tidymodels

# 1. Modelo con penalty a ajustar (tune() = "búscalo con CV")
# mixture = 1 → LASSO. Para Elastic Net: mixture = tune() también
modelo_lasso_tune <- linear_reg(penalty = tune(), mixture = 1) |> set_engine("glmnet") |> set_mode("regression")

# 2. Workflow (mismo patrón que antes, ahora con penalty pendiente)
wf_lasso <- workflow() |> add_recipe(receta) |> add_model(modelo_lasso_tune)

# 3. Grilla de valores candidatos de penalty (λ) en escala log10
grilla_lambda <- grid_regular(penalty(range = c(-4, 0)), levels = 30)

# 4. Validación cruzada con 5 folds
folds <- vfold_cv(datos_train, v = 5)

# 5. Probar cada λ con CV (ahora le pasamos el workflow)
resultados <- tune_grid(wf_lasso,
  resamples = folds,
  grid = grilla_lambda,
  metrics = metric_set(rmse, rsq))

# 6. Seleccionar el mejor λ
mejor_lambda <- select_best(resultados, metric = "rmse")

# 7. Finalizar y evaluar en test (last_fit = fit en train + métricas en test)
resultado_final <- wf_lasso |>
  finalize_workflow(mejor_lambda) |>
  last_fit(split = datos_split,
           metrics = metric_set(rmse, rsq))

collect_metrics(resultado_final)
  • select_best() da el λ con menor RMSE. Veremos una alternativa más parsimoniosa más adelante
  • Para Elastic Net, marca también mixture = tune() y agrega mixture(range = c(0, 1)) a la grilla con levels = c(20, 5) (20 de λ × 5 de α)
  • Es el mismo patrón de tuning de la sesión 2.1; lo corren paso a paso en el laboratorio

Del código anterior: ¿qué hacen los pasos 5 a 7?

  • Paso 5, tune_grid(): prueba los 30 valores de λ de la grilla
  • Para cada λ ajusta el modelo en los folds de validación cruzada y mide el error (RMSE, R²). Devuelve una tabla con el rendimiento de cada λ
  • Paso 6, select_best(): mira esa tabla y elige el λ con el menor RMSE promedio entre folds
  • Paso 7, finalize_workflow() + last_fit(): hasta acá el workflow tenía un hueco (penalty = tune()) y no se podía ajustar
  • finalize_workflow() escribe el λ ganador en ese hueco; recién ahí last_fit() puede reajustar con todo el train y evaluar una sola vez en el test
  • Más información en https://www.tidymodels.org/start/tuning/

El λ se elige sin tocar el test:

resultados        (rendimiento de
     │             cada λ, vía CV)
     │  select_best()
     ▼
mejor_lambda      (un solo valor)
     │  finalize_workflow()
     │  + last_fit()
     ▼
resultado_final   (métricas en test)

La grilla y la CV eligen λ; el test set entra recién en el paso 7, una sola vez, para la métrica final

Dos criterios para elegir λ

λ mínimo (select_best)

  • El valor que minimiza el error de CV
  • Mejor rendimiento predictivo
  • Modelo más complejo (más variables)
select_best(resultados, metric = "rmse")

λ 1SE (select_by_one_std_err)

  • El λ más grande dentro de 1 error estándar del mínimo
  • Modelo más parsimonioso
  • Sacrifica un poco de rendimiento por simplicidad
select_by_one_std_err(
  resultados,
  metric = "rmse",
  desc(penalty)
)


En ciencias sociales, el λ 1SE suele preferirse por producir modelos más interpretables

Regularización en alta dimensionalidad

  • Con \(p > n\) hay infinitas soluciones que pasan por todos los puntos
  • OLS no puede elegir entre ellas: deja coeficientes sin estimar y da un ajuste perfecto aunque los datos sean puro ruido
  • La regularización rompe el empate: se queda con la de coeficientes más chicos (Ridge) o con la que usa menos variables (LASSO)
  • Pasa con encuestas de cientos de ítems, registros administrativos y, sobre todo, análisis de texto: cada palabra es una variable. Lo ven mañana con TF-IDF
  • LASSO/Ridge/Elastic Net dan una respuesta con \(p > n\); OLS ni siquiera se puede calcular

Una regla práctica:

Situación Significado Recomendación
\(n \gg p\) Muchos datos, pocas variables OLS probablemente está bien
\(n \approx p\) Datos y variables similares Regularización ayuda
\(n \ll p\) Pocas observaciones, muchas variables Regularización es necesaria (OLS no funciona)

Regularización también para clasificación

  • LASSO, Ridge y Elastic Net no son solo para regresión
  • Funcionan igual con regresión logística: se penalizan los coeficientes para evitar sobreajuste y seleccionar variables
  • En tidymodels, basta con cambiar linear_reg() por logistic_reg():
# Regresión logística con LASSO
modelo_clasif <- logistic_reg(
  penalty = tune(),
  mixture = 1          # LASSO
) |>
  set_engine("glmnet") |>
  set_mode("classification")

# El resto del flujo es idéntico:
# workflow, recipe, tune_grid, etc.

Ejemplo: predecir voto

¿Qué variables predicen si alguien vota o no? Con 40 predictores de una encuesta, LASSO logístico selecciona los que realmente importan:

Variable              Coef
─────────────────────────────
edad                   0.52
educacion_anios        0.34
interes_politica       0.41
confianza_partidos     0.28
ingreso_hogar          0.00 ← eliminada
genero                 0.00 ← eliminada
satisf_servicios       0.00 ← eliminada

Misma lógica que antes: LASSO descarta las variables redundantes

Aplicaciones en ciencias sociales

Ejemplo: Predicción de pobreza con datos de encuestas

El problema:

  • Los censos de pobreza son costosos y poco frecuentes
  • Las encuestas de hogares tienen más detalle pero menos cobertura
  • ¿Podemos predecir la pobreza en áreas sin datos usando características observables?

Enfoque con LASSO:

  • Variable objetivo: ingreso per cápita (o indicador de pobreza)
  • Predictores: características del hogar, vivienda, acceso a servicios
  • LASSO selecciona las variables más predictivas
  • El modelo se aplica a censos o registros administrativos

Variables típicas seleccionadas:

Variable              Coef LASSO
─────────────────────────────────
años_educacion_jefe    0.42
material_piso          0.28
acceso_agua_potable    0.22
num_habitaciones       0.18
tiene_refrigerador     0.15
material_techo         0.11
tiene_vehiculo         0.09
zona_urbana            0.00  ← eliminada
genero_jefe            0.00  ← eliminada

LASSO identifica automáticamente qué variables realmente predicen la pobreza

Comparación

Criterio LASSO/Ridge/Elastic Net Random Forest/Gradient Boosting
Interpretabilidad Alta (coeficientes) Baja (importancia)
Relaciones no lineales No
Interacciones Manual Automáticas
Selección de variables Sí (LASSO) No (pero da importancia)
Velocidad Muy rápido Moderado
Ajuste de hiperparámetros 1-2 parámetros Varios parámetros
Extrapolación Lineal No extrapola bien

En la práctica, es útil probar ambos enfoques y comparar. La regularización es mejor cuando las relaciones son aproximadamente lineales y queremos interpretabilidad. RF es mejor cuando hay no linealidades e interacciones complejas

¿Cómo interpretar los coeficientes de LASSO?

  • Tras ajustar un modelo LASSO, obtenemos un conjunto de coeficientes
  • Algunos son exactamente cero: esas variables fueron eliminadas por el modelo
  • Los que sobreviven son los predictores que LASSO consideró más relevantes

¿Cómo leerlos?

  • Un coeficiente positivo indica que aumentar esa variable se asocia con un aumento en la respuesta
  • Un coeficiente negativo indica lo contrario
  • La magnitud depende de la escala de las variables. Si normalizamos antes (step_normalize()), los coeficientes son directamente comparables

Atención: LASSO elige una variable entre un grupo correlacionado y descarta las demás. No significa que las eliminadas no importen, sino que son redundantes con la que quedó

Extraer coeficientes en tidymodels:

# Ajustar modelo final
ajuste_final <- fit(modelo_final,
                    satisfaccion ~ .,
                    data = datos_train)

# Ver coeficientes
tidy(ajuste_final) |>
  filter(estimate != 0) |>   # solo no-cero
  arrange(desc(abs(estimate)))
# Resultado (ejemplo):
term                  estimate
─────────────────────────────
confianza_gobierno     0.38
percepcion_economia    0.31
educacion_anios        0.22
edad                  -0.14
desempleo_regional    -0.09

Las variables con coeficiente = 0 (no mostradas) fueron descartadas por LASSO

Resumen de la sesión

Conceptos clave:

  • Predicción vs. explicación: objetivos diferentes, herramientas complementarias
  • Regularización: LASSO selecciona, Ridge reduce, Elastic Net combina
  • Ajuste de λ: siempre con validación cruzada, nunca con datos de test
  • λ mínimo vs. λ 1SE: rendimiento vs. parsimonia

Cuándo usar cada método:

  • OLS: pocos predictores, relaciones lineales, foco en explicación
  • LASSO: muchos predictores, queremos selección automática
  • Ridge: predictores correlacionados, no queremos eliminar ninguno
  • Elastic Net: no estamos seguros, queremos lo mejor de ambos
  • Random Forest/Gradient Boosting: mejor predicción posible, relaciones complejas

Próximos pasos

  • Laboratorios (2.3 y 2.4):
    • Clasificación con datos de Latinobarómetro
    • Regresión con datos socioeconómicos
    • Tuning de hiperparámetros en la práctica
    • Comparación e interpretación de modelos
  • Mañana (Día 3): Aprendizaje no supervisado y análisis de texto
    • Clustering (K-means) y reducción de dimensionalidad (PCA)
    • Análisis computacional de texto
    • Topic modeling

¡Nos vemos en el laboratorio! 🤓

¡Nos vemos en el laboratorio! 😊