/ Proyectos / Educación / Deserción estudiantil

Proyectos de Machine Learning con Ejemplos: Predicción de Deserción Estudiantil Paso a Paso

Modelado Predictivo para Retención Estudiantil y Evaluación de Riesgo de Abandono

25 min de lectura Python · Scikit-learn · SHAP Principiante · Intermedio Aplicable a tesis de ciencias sociales y educación

Resumen del proyectoWiki

¿Qué encontrarás en este proyecto?

  • Un proyecto real de ML construido alrededor de un problema claramente definido — no solo código.
  • Cómo identificar el research gap antes de construir cualquier modelo.
  • Cómo validar un dataset y evitar data leakage.
  • Baseline, iteración, interpretabilidad y despliegue: el flujo completo.

Cuando alguien busca proyectos de machine learning con ejemplos, generalmente espera más que código: quiere un caso real, un dataset justificable, métricas claras y una explicación estructurada que pueda replicar.

En este tutorial desarrollamos un modelo predictivo de deserción estudiantil siguiendo un enfoque profesional: primero definimos el problema con rigor metodológico, identificamos el research gap reciente (2023–2026), justificamos el dataset y establecemos un baseline; luego escribimos el código, iteramos el modelo con las métricas correctas e incorporamos interpretabilidad para explicar las causas de la deserción.

El objetivo no es solo construir un modelo — es mostrar paso a paso cómo estructurar un proyecto real de machine learning aplicado a educación, desde la definición estratégica hasta el despliegue como sistema de alerta temprana.

Casos de uso del modelo

  • Alerta temprana: detectar estudiantes en riesgo antes de que abandonen, permitiendo intervención oportuna.
  • Priorización de recursos: enfocar tutorías, becas y apoyo psicosocial en los casos de mayor riesgo.
  • Análisis de causas: entender qué factores académicos y socioeconómicos impulsan la deserción para diseñar políticas institucionales.

Stack tecnológico

  • Python 3.10+ — lenguaje principal
  • Scikit-learn — Logistic Regression, Random Forest, XGBoost, GridSearchCV, StandardScaler
  • pandas / numpy — manipulación y análisis de datos
  • SHAP — interpretabilidad de predicciones individuales y globales
  • matplotlib / seaborn — visualización de datos y métricas
  • FastAPI / Streamlit / Gradio — despliegue como sistema de alerta temprana
  • pickle — serialización del modelo entrenado

Setup del entornoConfiguración del sistema

El proyecto puede correrse localmente o en Google Colab. El notebook original fue desarrollado en Colab con acceso a Google Drive para leer el dataset de datos académicos.

Instalación de dependencias

Terminal — instalar dependencias
pip install scikit-learn pandas numpy matplotlib seaborn shap xgboost fastapi pydantic uvicorn

Estructura del proyecto

Estructura de carpetas recomendada
proyecto-desercion-estudiantil/
├── data/
│   └── student_dropout_uci.csv          # dataset UCI Student Dropout
├── models/
│   └── xgb_desercion.pkl                # modelo XGBoost guardado
├── notebooks/
│   ├── 01_eda.ipynb
│   ├── 02_feature_selection.ipynb
│   ├── 03_benchmarking.ipynb
│   ├── 04_shap_interpretabilidad.ipynb
│   └── 05_deploy.ipynb
├── src/
│   ├── preprocessing.py
│   └── model.py
├── api/
│   └── main.py                          # endpoint FastAPI
└── requirements.txt

Carga del dataset

El dataset UCI Student Dropout contiene variables académicas, demográficas y socioeconómicas de estudiantes universitarios, con una columna objetivo que indica si el estudiante desertó, se graduó o está matriculado. Para este proyecto, filtramos solo las categorías Dropout y Graduate para construir un problema de clasificación binaria.

Python — carga inicial de datos
import pandas as pd

df = pd.read_csv("data/student_dropout_uci.csv", sep=";")

# Filtrar solo Dropout y Graduate (excluir Enrolled)
df = df[df["Target"].isin(["Dropout", "Graduate"])].copy()

# Codificar la variable objetivo: 1 = Dropout, 0 = Graduate
df["desercion"] = (df["Target"] == "Dropout").astype(int)
df.drop("Target", axis=1, inplace=True)

print(df.shape)
print(df["desercion"].value_counts(normalize=True))

EDA, Research Gap y ValidaciónPreparación de datos

Antes de modelar, necesitas entender el estado del arte, validar el dataset y evitar errores metodológicos que inflarían artificialmente tus métricas. Esta etapa es la que diferencia un proyecto profesional de un ejercicio de código.

Research Gap 2023–2026

Filtramos artículos publicados entre 2023 y 2026 con la siguiente combinación de palabras clave en Google Scholar, Scopus e IEEE:

Query de búsqueda — Google Scholar / Scopus / IEEE
"student dropout prediction model"
AND "machine learning"
AND "feature importance"
AND "early intervention"

Con estos filtros obtuvimos 20 artículos relevantes. El 95.2% eran artículos de revista y 4.8% ponencias de conferencias. El patrón encontrado: casi todos los artículos hacen bien una de dos cosas — o predicen bien, o explican bien. Muy pocos integran ambas de forma sólida y operacional.

La oportunidad clara: desarrollar un modelo que prediga la deserción estudiantil Y identifique sus principales causas. No solo clasificar — entender por qué.
Artículo Qué Cómo Métricas Solapamiento Research Gap
Predictive Modeling of Student Dropout Using Academic Data and ML Techniques (Aini et al., 2025) Predecir deserción estudiantil ML sobre datos académicos (árboles, RF, boosting) Accuracy, Precision, Recall, F1, AUC Alto Fuerte enfoque predictivo pero interpretabilidad profunda (SHAP) limitada y explícitamente conectada a decisiones institucionales concretas.
Early Identification of Student Dropout Using Classification, Clustering and Association Methods (Chicon et al., 2025) Identificación temprana de estudiantes en riesgo Clasificación, clustering y reglas de asociación bajo CRISP-DM Accuracy, F1, Recall, AUC Alto Enfocado en identificación temprana pero sin análisis causal estructurado ni evaluación del impacto institucional.
Data Mining to Identify University Student Dropout Factors (Marín et al., 2025) Identificar factores asociados a la deserción Data mining + análisis de importancia de características Accuracy, Recall, feature importance Alto Fuerte componente explicativo pero menor integración con modelos predictivos comparativos robustos o validación longitudinal.
Student Dropout Prediction through ML Optimization: Insights from Moodle Log Data (Marcolino et al., 2025) Predecir deserción usando datos de LMS Optimización de ML con datos de interacción (logs de Moodle) Accuracy, AUC, F1 Medio-Alto Enfocado en LMS; integración limitada de variables socioeconómicas e institucionales para explicación multifactorial.
Using ML to Predict Student Retention from Socio-Demographic Characteristics and App-Based Engagement Metrics (Matz et al., 2023) Predecir retención/deserción ML con variables sociodemográficas y métricas digitales AUC, Accuracy, modelos comparativos Medio Predicción demográfica/digital sin profundidad en análisis académico institucional ni conexión directa con sistema formal de alerta temprana.

Dataset Novelty Score

El Dataset Novelty Score no es una métrica matemática exacta. Es una guía práctica: busca en Google o repositorios académicos hasta tres páginas de resultados y verifica si ese dataset ha sido usado en estudios recientes.

Dataset DNS estimado Nivel de novedad
CONALEP abandono escolar 0.99 Muy alto
Tecnológico de Monterrey 0.97 Muy alto
Kaggle – Dropout & Success 0.94 Alto
UCI – Student Dropout 0.82 Moderado-alto
OULAD 0.77 Moderado

Validación del dataset y riesgo de data leakage

Antes de escribir una sola línea de código de modelado, hazte estas preguntas:

1

¿Es válido usar este dataset para el problema que quiero resolver?

El dataset debe representar genuinamente el fenómeno que quieres modelar — deserción, no rendimiento académico genérico.

2

¿El split está bien estructurado? ¿Debe ser temporal o aleatorio?

Si predices deserción futura, un split temporal — entrenar con cohortes anteriores, validar con cohortes posteriores — es más coherente que uno aleatorio.

3

¿Hay riesgo de data leakage?

Mezclar datos futuros en el entrenamiento infla artificialmente tus métricas. En problemas de deserción, la dimensión temporal es crítica.

4

¿Hay desbalance de clases?

Si el 80% de los estudiantes no desertan, un modelo que siempre diga "no va a desertar" tendrá 80% de accuracy. Hay que medir Recall y AUC, no solo Accuracy.

Todo este proceso — evaluar validez, revisar novedad, evitar leakage y justificar la fuente — es lo que convierte este contenido en algo más que un tutorial. No solo estás viendo código. Estás viendo cómo se estructura correctamente un proyecto profesional.

EDA inicial y análisis de correlación

Antes de modelar, necesitas entender los datos. Observa: distribución de la variable objetivo, variables numéricas y categóricas, valores nulos, posibles desbalances y correlaciones con la variable de deserción.

Python — EDA inicial
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

df = pd.read_csv("data/student_dropout_uci.csv", sep=";")
df = df[df["Target"].isin(["Dropout", "Graduate"])].copy()
df["desercion"] = (df["Target"] == "Dropout").astype(int)
df.drop("Target", axis=1, inplace=True)

print(df.head())
print(df.info())
print(df["desercion"].value_counts(normalize=True))

# Correlación de features con la variable objetivo
corr_target = df.corr()["desercion"].sort_values(ascending=False)
print(corr_target.head(10))

# Visualizar desbalance de clases
df["desercion"].value_counts().plot.bar()
plt.title("Distribución de la variable objetivo")
plt.xticks([0, 1], ["Graduate (0)", "Dropout (1)"], rotation=0)
plt.ylabel("Número de estudiantes")
plt.tight_layout()
plt.show()

Split y normalización

Python — split train/val/test y StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler

X = df.drop("desercion", axis=1)
y = df["desercion"]

# Split: 80% train, 10% val, 10% test
X_train, X_temp, y_train, y_temp = train_test_split(X, y, test_size=0.2, random_state=42)
X_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp, test_size=0.5, random_state=42)

# Normalizar: media 0, varianza 1
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_val_scaled   = scaler.transform(X_val)
X_test_scaled  = scaler.transform(X_test)

print(f"Train: {X_train_scaled.shape}")
print(f"Val:   {X_val_scaled.shape}")
print(f"Test:  {X_test_scaled.shape}")

Benchmarking y modeladoEntrenamiento

Se comparan cinco modelos de clasificación antes de invertir tiempo en sintonización: Logistic Regression (baseline), Random Forest, Gradient Boosting, XGBoost y SVM. El criterio principal de selección es Recall y AUC en validación cruzada.

Baseline: Regresión Logística

Sin deep learning. Sin optimización. Sin grid search. Un modelo clásico y robusto como Regresión Logística es suficiente para empezar y establecer el punto de comparación.

Python — Baseline con Regresión Logística
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report, roc_auc_score

modelo_base = LogisticRegression(max_iter=1000, random_state=42)
modelo_base.fit(X_train_scaled, y_train)

y_pred  = modelo_base.predict(X_test_scaled)
y_proba = modelo_base.predict_proba(X_test_scaled)[:, 1]

print(classification_report(y_test, y_pred))
print(f"AUC: {roc_auc_score(y_test, y_proba):.4f}")
Si el Recall es bajo, estás dejando sin detectar a estudiantes en riesgo. Si el AUC está cerca de 0.5, el modelo no está aprendiendo nada útil. Ese es el pensamiento crítico aplicado a machine learning en educación.

Benchmarking: comparación de modelos con validación cruzada

Python — comparación de modelos
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier
from sklearn.svm import SVC
from xgboost import XGBClassifier
from sklearn.model_selection import cross_val_score
import numpy as np
import pandas as pd

modelos = {
    "Logistic Regression": LogisticRegression(max_iter=1000, random_state=42),
    "Random Forest":       RandomForestClassifier(random_state=42),
    "Gradient Boosting":   GradientBoostingClassifier(random_state=42),
    "XGBoost":             XGBClassifier(random_state=42, eval_metric="logloss"),
    "SVM":                 SVC(probability=True, random_state=42),
}

resultados = {}
for nombre, modelo in modelos.items():
    auc_scores = cross_val_score(modelo, X_train_scaled, y_train,
                                 scoring="roc_auc", cv=5)
    rec_scores = cross_val_score(modelo, X_train_scaled, y_train,
                                 scoring="recall", cv=5)
    resultados[nombre] = {
        "AUC":    auc_scores.mean(),
        "Recall": rec_scores.mean()
    }
    print(f"{nombre}: AUC={auc_scores.mean():.4f} | Recall={rec_scores.mean():.4f}")

df_resultados = pd.DataFrame(resultados).T
print(df_resultados.sort_values("AUC", ascending=False))

Sintonización del mejor modelo: XGBoost con GridSearchCV

XGBoost supera a los demás modelos en AUC y Recall. Se sintoniza con GridSearchCV para encontrar el número óptimo de estimadores, profundidad máxima, tasa de aprendizaje y submuestra.

Python — GridSearchCV sobre XGBoost
from sklearn.model_selection import GridSearchCV
from xgboost import XGBClassifier

param_grid = {
    "n_estimators":   [100, 200, 300],
    "max_depth":      [3, 5, 7],
    "learning_rate":  [0.05, 0.1, 0.2],
    "subsample":      [0.8, 1.0],
    "colsample_bytree": [0.8, 1.0],
}

grid_search = GridSearchCV(
    XGBClassifier(random_state=42, eval_metric="logloss"),
    param_grid,
    cv=5,
    scoring="roc_auc",
    n_jobs=-1,
    verbose=1
)

grid_search.fit(X_train_scaled, y_train)

print(f"Mejores parámetros: {grid_search.best_params_}")
print(f"Mejor AUC CV: {grid_search.best_score_:.4f}")

Guardar el modelo entrenado

Python — serializar modelo con pickle
import pickle

# Entrenar el modelo final con todos los datos de entrenamiento
XGB_final = XGBClassifier(**grid_search.best_params_, random_state=42, eval_metric="logloss")
XGB_final.fit(X_train_scaled, y_train)

# Guardar modelo y scaler
with open("models/xgb_desercion.pkl", "wb") as f:
    pickle.dump(XGB_final, f)

with open("models/scaler_desercion.pkl", "wb") as f:
    pickle.dump(scaler, f)

print("Modelo y scaler guardados correctamente.")

Métricas y comparaciónEvaluación

Se evalúan todos los modelos sobre el conjunto de test — datos que ningún modelo vio durante el entrenamiento ni la sintonización.

Evaluación en test

Python — evaluación final en conjunto de test
from sklearn.metrics import (classification_report, roc_auc_score,
                             ConfusionMatrixDisplay)
import matplotlib.pyplot as plt

mejor_modelo = grid_search.best_estimator_

y_pred  = mejor_modelo.predict(X_test_scaled)
y_proba = mejor_modelo.predict_proba(X_test_scaled)[:, 1]

print(classification_report(y_test, y_pred))
print(f"AUC: {roc_auc_score(y_test, y_proba):.4f}")

# Matriz de confusión
fig, ax = plt.subplots(figsize=(6, 5))
ConfusionMatrixDisplay.from_predictions(
    y_test, y_pred,
    display_labels=["Graduate", "Dropout"],
    ax=ax, colorbar=False
)
plt.title("Matriz de confusión — XGBoost sintonizado")
plt.tight_layout()
plt.show()

Tabla comparativa de modelos

Modelo Accuracy Recall F1 AUC
Baseline LogReg 0.78 0.60 0.65 0.74
Random Forest 0.81 0.68 0.72 0.79
Gradient Boosting 0.83 0.70 0.74 0.82
XGBoost (GridSearchCV) 0.84 0.73 0.76 0.85
SVM 0.80 0.62 0.67 0.76

Hay mejora real si: el AUC aumenta al menos 0.03–0.05, el Recall mejora significativamente y el F1 mejora sin sacrificar demasiado la Precision. Si solo sube Accuracy pero baja Recall, no mejoraste el modelo para el caso de uso de deserción estudiantil.

Cuándo dejar de optimizar

  • La mejora del AUC es menor a 0.01 después de varias iteraciones
  • El modelo se vuelve demasiado complejo para el beneficio que aporta
  • La varianza entre train y test aumenta significativamente (overfitting)
  • La interpretabilidad se pierde completamente
  • Ya superaste el rango promedio reportado en la literatura reciente
Si la mayoría de los artículos reportan AUC entre 0.78 y 0.85, y tú estás en 0.84 con buena interpretabilidad — probablemente estás en un punto sólido. Optimizar más puede ser marginal.

Interpretabilidad, despliegue y conclusionesHallazgos

Interpretabilidad con SHAP

Aquí es donde tu proyecto se diferencia. No es suficiente decir "XGBoost es mejor". Necesitas responder: ¿por qué está prediciendo eso?

Python — interpretabilidad con SHAP
import shap

explicador   = shap.Explainer(mejor_modelo, X_train_scaled)
shap_values = explicador(X_test_scaled)

# Summary plot — importancia global de características
shap.summary_plot(shap_values, X_test_scaled,
                  feature_names=X.columns.tolist())

# Explicación individual de una predicción
shap.waterfall_plot(shap_values[0])

Hallazgos principales

Hallazgo 1

El rendimiento en el primer semestre es el predictor más potente

El número de unidades curriculares aprobadas en el 1er semestre tiene la mayor importancia relativa en el modelo. Un bajo rendimiento inicial es el predictor más fuerte de deserción, lo que sugiere que las intervenciones deben concentrarse en los primeros meses del ciclo escolar.

Hallazgo 2

XGBoost supera a Logistic Regression y Random Forest en Recall

Incluso antes de sintonización, XGBoost tiene mejor AUC y Recall que Logistic Regression y Random Forest en sus configuraciones por defecto. La sintonización con GridSearchCV reduce la tasa de falsos negativos (estudiantes en riesgo no detectados) en aproximadamente 18% respecto al baseline.

Hallazgo 3

Las variables socioeconómicas tienen impacto moderado pero consistente

La condición de deudor (scholarship holder, debtor) y el hecho de estar al día con el pago de matrícula aparecen consistentemente entre los predictores relevantes. Su impacto es menor que el rendimiento académico, pero estadísticamente significativo y operacionalmente accionable.

Hallazgo 4

El modelo es interpretable a nivel individual con SHAP

A diferencia de una red neuronal, XGBoost + SHAP permite explicar cada predicción individual: por qué este estudiante específico tiene un 78% de probabilidad de desertar, qué factores lo elevan y cuáles lo reducen. Eso transforma el modelo en una herramienta de apoyo a la decisión institucional.

Despliegue como sistema de alerta temprana

Un modelo guardado en tu computadora no ayuda a ninguna institución. Lo que marca la diferencia es convertirlo en un sistema que permita detectar estudiantes en riesgo y actuar a tiempo.

Python — endpoint FastAPI para predicción de riesgo
from fastapi import FastAPI
from pydantic import BaseModel
import pickle
import numpy as np

# Cargar modelo y scaler al arrancar
with open("models/xgb_desercion.pkl", "rb") as f:
    modelo = pickle.load(f)

with open("models/scaler_desercion.pkl", "rb") as f:
    scaler = pickle.load(f)

app = FastAPI(title="API — Sistema de Alerta Temprana de Deserción Estudiantil")

class Estudiante(BaseModel):
    unidades_aprobadas_1sem: float
    unidades_aprobadas_2sem: float
    calificacion_promedio_1sem: float
    calificacion_promedio_2sem: float
    deudor: int           # 0 = No, 1 = Sí
    beca: int             # 0 = No, 1 = Sí
    al_corriente_pago: int  # 0 = No, 1 = Sí
    # ... resto de variables

@app.post("/predecir")
def predecir(estudiante: Estudiante):
    datos = np.array([[
        estudiante.unidades_aprobadas_1sem,
        estudiante.unidades_aprobadas_2sem,
        estudiante.calificacion_promedio_1sem,
        estudiante.calificacion_promedio_2sem,
        estudiante.deudor,
        estudiante.beca,
        estudiante.al_corriente_pago,
    ]])
    datos_norm    = scaler.transform(datos)
    probabilidad  = modelo.predict_proba(datos_norm)[0][1]
    nivel_riesgo  = "Alto" if probabilidad >= 0.6 else "Medio" if probabilidad >= 0.4 else "Bajo"
    return {
        "probabilidad_desercion": round(float(probabilidad), 4),
        "nivel_riesgo": nivel_riesgo,
        "accion_recomendada": "Intervención inmediata" if nivel_riesgo == "Alto" else "Monitoreo"
    }

# Ejecutar: uvicorn api.main:app --reload

Limitaciones y trabajo futuro

  • El dataset es de una sola institución — la generalización a otras instituciones o contextos culturales requiere reentrenamiento o transfer learning.
  • El modelo no incorpora variables de proceso educativo en tiempo real (asistencia, participación en plataforma, interacciones con tutores) que podrían mejorar la predicción.
  • Un modelo de predicción multi-output (predecir en cuál semestre específico ocurrirá la deserción) daría más valor operacional al sistema.
  • La calibración de probabilidades con Platt Scaling o Isotonic Regression mejoraría la confiabilidad de los scores de riesgo.
¿Cómo adaptar este proyecto a tu tesis? El framework es completamente reutilizable con el dataset UCI Student Dropout, que es público y ampliamente citado en la literatura. Puedes agregar SHAP para explicaciones individuales, extender el target a predicción multi-semestre, incorporar variables de LMS si tienes acceso a ellas, o aplicar el mismo pipeline exacto a churn de clientes, abandono en MOOCs o riesgo de deserción en educación media superior.