ENAHO 2025 · scikit-learn · Streamlit

Arquitectura del Predictor de ingresos e informalidad

Un entrenamiento que corre una vez en un PC y una app que solo lee lo que ese entrenamiento dejó escrito. Cinco vistas: contenedores, flujos, datos, despliegue y decisiones. Haz clic en cualquier caja; reproduce un flujo para ver el recorrido.

este repo otro repo o persona servicio externo tráfico
Detalle

Haz clic en un contenedor

Cada caja es una pieza que corre por separado: un conjunto de scripts, una carpeta de archivos, un servicio o el navegador de quien visita. Las líneas son llamadas reales: protocolo y qué viaja.

Por qué la app no entrena

Entrenar el torneo tarda minutos y necesita los microdatos, que no se redistribuyen. La app solo necesita la carpeta models/: lee los modelos ya ajustados y dos JSON con todo lo que se muestra. Por eso puede vivir en un hosting gratuito.

Por qué dos JSON de contrato

feature_schema.json dice qué variables existen y sus rangos; ui_artifacts.json trae lo caro ya calculado. La interfaz no escribe ningún número a mano: si el modelo cambia, el formulario cambia con él.

Por qué nada de datos personales

Los microdatos de la encuesta se descargan del INEI y quedan fuera del repo (data/ está ignorado por git). Lo que se publica son los modelos, que no contienen filas de personas, y los reportes agregados.

Secuencias · una por flujo · los mismos pasos que reproduce el C4
Estructura de archivos · de microdatos a artefactos · clic en un archivo
El contrato entre entrenamiento y app

Dos JSON y tres modelos

ArchivoQuién lo escribeQué contiene
feature_schema.jsonscripts 06, 07 y 08, por secciones (guardar_schema)variables, rangos, opciones, defaults, etiquetas, métricas de test, punto operativo y ablación
ui_artifacts.jsonscript 09curva de umbral, ROC y PR, calibración, dependencia parcial, importancia por permutación, cohortes y narrativa del torneo
ui_maquinas.jsonscript 09embudo de filas auditado y tamaños medidos de los artefactos
regresor_e9.joblibscript 07Gradient Boosting con objetivo en logaritmo (TransformedTargetRegressor)
clasificador_gb.joblibscript 06clasificador de informalidad
clasificador_gb_reducido.joblibscript 08variante sin tamaño de empresa ni categoría, de la ablación
_hiperparametros.jsontorneo y clasificadorcaché de la búsqueda en grilla; borrarlo fuerza el recálculo

Todos los JSON se escriben a un archivo temporal y se mueven con os.replace: nunca queda uno truncado.

Despliegue · lo que hay

Entrena en local, publica el resultado y la nube solo lo sirve

El entrenamiento se corre en un PC con Windows, con los microdatos descargados a mano. Lo que se versiona son los modelos y los JSON; Streamlit Cloud sirve la app desde el repo público. La página del proyecto en el portafolio enlaza a la demo.

Entrenamiento · PC Windows

Python 3.12 con versiones fijadas en requirements.txtvenv
run.ps1 corre un script de src/ y deja logs en logs/python -u
microdatos en data/raw, fuera de gitdescarga manual
semilla 42 en división, validación y búsquedasreproducible
pruebas de contrato en tests/pytest
→ git push →

Demo · Streamlit Cloud

repo público con models/ versionadosin datos
app/streamlit_app.py con requirements.txtStreamlit 1.61
tema claro por defecto en .streamlit/config.tomlsin telemetría
CI en el repo (no hay workflows)hoy: pruebas a mano
reentrenar en la nubeno: solo lee

Qué cambiaría al escalar: pruebas como paso de CI antes de publicar, y los artefactos en un almacén aparte si el repo crece.

Decisiones · inferidas del código y la documentación del repo

Cada decisión dice qué se eligió, qué se dejó y por qué

DecisiónPor quéDescartado
Formulario dirigido por el schemaLos rangos y opciones salen del pipeline ya ajustado, así que la interfaz no puede desincronizarse del modeloDeclarar cada campo a mano en la app
Precomputar lo caro al entrenarCurva de umbral, dependencia parcial y permutación se calculan una vez y viajan en un JSON de unos 46 KB; la app solo interpolaRecalcular en cada sesión de la nube
Verificar antes de escribirEl script 09 recalcula los umbrales desde las probabilidades out-of-fold y aborta sin escribir si no coinciden con los publicadosConfiar en que el schema y la curva siempre coinciden
Fragmentos de StreamlitMover el control de umbral reejecuta solo ese bloque, no el formulario ni la carga de artefactosReejecutar el script entero en cada movimiento
Objetivo en logaritmo dentro del artefactoTransformedTargetRegressor devuelve soles (mediana); la media se obtiene con el factor de smearing publicado en el schemaQue la interfaz maneje logaritmos
Caché de hiperparámetros tras cada algoritmoSi la máquina se cuelga a mitad de una grilla no se pierde lo ya buscado; el regresor se puede reajustar sin repetir el torneoGuardar la caché solo al final
Escritura atómica de JSONArchivo temporal más os.replace: lo publicado nunca queda a mediasEscribir directo al destino
Paletas como datos, no variables CSSLos gráficos viajan a iframes que no ven el CSS del padre; pasar los colores como parámetro mantiene una sola fuenteVariables CSS y bloques duplicados por tema
Microdatos fuera del repoLa app no necesita data/; se enlaza a la fuente del INEI en vez de redistribuirlaVersionar los CSV