Demo_churn / app.py
Larxmind's picture
Despliegue inicial limpio sin binarios
1ec1a50
Raw
History Blame Contribute Delete
6.04 kB
import gradio as gr
import pandas as pd
import joblib
import os
from huggingface_hub import hf_hub_download
from dotenv import load_dotenv
load_dotenv()
# ==============================================================================
# 1. CARGA DEL MODELO DESDE HUGGING FACE (CLOUD)
# ==============================================================================
# Cambia 'TU_USUARIO' por tu nombre de usuario en Hugging Face
repo_id = "larxmind/student-dropout-predictor-rf"
nombre_archivo = "modelo_rf_optimizado.pkl"
try:
print(f"⏳ Conectando con Hugging Face para descargar el modelo '{repo_id}'...")
ruta_modelo_cache = hf_hub_download(repo_id=repo_id, filename=nombre_archivo)
modelo = joblib.load(ruta_modelo_cache)
print("✅ Modelo Cloud cargado correctamente y listo para inferencia.")
except Exception as e:
print(f"❌ Error crítico al cargar el modelo desde la nube: {e}")
print("Asegúrate de que el repositorio es público o de que tu HF_TOKEN está en el .env")
# ==============================================================================
# 2. FUNCIÓN DE PREDICCIÓN
# ==============================================================================
def predecir_abandono(genero, region, nivel_educativo, nivel_economico, rango_edad,
intentos_previos, creditos, discapacidad,
clics, entregas, nota, asistencia):
# Preparamos el dataframe emulando las columnas que espera tu modelo
datos_entrada = pd.DataFrame([{
'code_module': 0,
'code_presentation': 0,
'genero': 0 if genero == 'F' else 1,
'region': 0,
'nivel_educativo': 1,
'nivel_economico': 5,
'rango_edad': 0 if rango_edad == '0-35' else (1 if rango_edad == '35-55' else 2),
'intentos_previos': intentos_previos,
'creditos_matriculados': creditos,
'discapacidad': 1 if discapacidad == 'Sí' else 0,
'total_clics': clics,
'evaluaciones_entregadas': entregas,
'nota_media': nota,
'dias_asistencia_mes': asistencia
}])
# -------------------------------------------------------------------------
# 🛡️ BLINDAJE DE COLUMNAS: Reordenar exactamente como en el entrenamiento
# -------------------------------------------------------------------------
if hasattr(modelo, 'feature_names_in_'):
# Rellenamos columnas que falten (por si el encoder creó más de la cuenta)
for col in modelo.feature_names_in_:
if col not in datos_entrada.columns:
datos_entrada[col] = 0
# Forzamos el orden estricto
datos_entrada = datos_entrada[modelo.feature_names_in_]
# -------------------------------------------------------------------------
# Extraemos probabilidad (clase 1 = Abandono) y la predicción final
probabilidad = modelo.predict_proba(datos_entrada)[0][1]
prediccion = modelo.predict(datos_entrada)[0]
if prediccion == 1:
return f"⚠️ RIESGO ALTO DE ABANDONO ({probabilidad:.1%})", "Se recomienda intervención inmediata por parte del tutor."
else:
return f"✅ ESTUDIANTE SEGURO ({1 - probabilidad:.1%} de permanencia)", "El ritmo de estudio es adecuado."
# ==============================================================================
# 3. INTERFAZ GRÁFICA (GRADIO BLOCKS)
# ==============================================================================
with gr.Blocks() as demo:
gr.Markdown(
"""
# 🎓 Sistema de Alerta Temprana (Early Warning System)
### Predicción de Abandono Estudiantil basada en Telemetría (Primeros 45 días)
"""
)
with gr.Row():
# COLUMNA 1: Perfil
with gr.Column():
gr.Markdown("### 👤 Perfil del Estudiante")
genero = gr.Radio(choices=["M", "F"], label="Género", value="F")
rango_edad = gr.Dropdown(choices=["0-35", "35-55", "55<="], label="Rango de Edad", value="0-35")
discapacidad = gr.Radio(choices=["Sí", "No"], label="¿Discapacidad declarada?", value="No")
region = gr.Textbox(label="Región (Ej. Scotland)", placeholder="Opcional")
nivel_educativo = gr.Textbox(label="Nivel Educativo", placeholder="Opcional")
nivel_economico = gr.Textbox(label="Nivel Socioeconómico", placeholder="Opcional")
# COLUMNA 2: Telemetría y Notas
with gr.Column():
gr.Markdown("### 📚 Historial Académico")
intentos_previos = gr.Slider(minimum=0, maximum=5, step=1, label="Intentos Previos", value=0)
creditos = gr.Slider(minimum=30, maximum=300, step=30, label="Créditos Matriculados", value=60)
gr.Markdown("### 🖱️ Telemetría (Primeros 45 días)")
clics = gr.Number(label="Total de Clics en el Campus", value=120)
entregas = gr.Slider(minimum=0, maximum=3, step=1, label="Evaluaciones Entregadas", value=2)
nota = gr.Slider(minimum=0, maximum=100, step=1, label="Nota Media Provisional", value=65)
asistencia = gr.Slider(minimum=0, maximum=30, step=1, label="Días de Asistencia en el Mes", value=12)
btn_predecir = gr.Button("🔍 Analizar Riesgo", variant="primary")
with gr.Row():
# Quitamos el text_color="red" para evitar el TypeError en Gradio 6.0
resultado_estado = gr.Textbox(label="Estado de Alerta")
resultado_accion = gr.Textbox(label="Acción Recomendada")
btn_predecir.click(
fn=predecir_abandono,
inputs=[genero, region, nivel_educativo, nivel_economico, rango_edad,
intentos_previos, creditos, discapacidad,
clics, entregas, nota, asistencia],
outputs=[resultado_estado, resultado_accion]
)
# 4. Lanzar el servidor
if __name__ == "__main__":
# Pasamos el tema directamente a la función de lanzamiento
demo.launch(share=True, theme=gr.themes.Soft())