Predicción de ingresos anuales de restaurantes (regresión) usando PyCaret y despliegue de una app Streamlit en Google Cloud Run.
La app acepta CSV RAW (sin escalar y con categóricas como City Group y Type), ejecuta un pipeline de inferencia y devuelve las predicciones de revenue.
- Objetivo y arquitectura
- Stack y requisitos
- Estructura del proyecto
- Artefactos de entrenamiento / inferencia
- Ejecución local
- Despliegue en Google Cloud Run
- Uso de la app
- Troubleshooting
- Extras (Hydra, pdoc, pre-commit)
Objetivo: predecir revenue de restaurantes a partir de variables como ubicación, tipo y otras características.
Arquitectura de inferencia (pipeline en producción):
FeatureDropper→ elimina columnas no necesarias (p. ej.Id).OrdinalEncoderCols→ codifica categóricas (City Group,Type) conOrdinalEncoder(unknown_value=-1).FeatureAligner→ alinea/ordena columnas exactamente como el modelo espera; agrega faltantes con0.0.PrefitMinMaxScaler→ aplica scaler preentrenado sobre selected features.PyCaretPredictor→ llama al modelo final guardado por PyCaret y produce la predicción derevenue.
La app Streamlit carga un pipeline ya fiteado (serializado con joblib), por lo que no reentrena nada en producción.
- Python: 3.11
- Librerías principales:
pycaret==3.3.2scikit-learn==1.3.2numpy==1.26.4,scipy==1.11.4,pandas==2.1.4,joblib==1.3.2lightgbm==4.3.0(o el backend que use tu modelo final)streamlit==1.33.0,matplotlib==3.7.5,seaborn==0.13.2
- SO deps:
libgomp1(requerido por LightGBM) - GCP: Cloud Build, Artifact Registry, Cloud Run
Archivo de producción: requirements.txt ya pinneado para evitar conflictos.
.
├── .devcontainer/
│ └── devcontainer.json # Config del contenedor de desarrollo (Dockerfile.dev)
├── src/
│ ├── modelo_ml_streamlit.py # App Streamlit (inferencia)
│ ├── images/
│ │ └── datapath-logo.png # (opcional) logo
│ └── models/
│ └── restaurant_revenue_pipeline.pkl # Pipeline completo fiteado (joblib)
├── data/
│ ├── raw/ # CSVs crudos (train/test/unseen)
│ └── processed/ # CSVs procesados (si se usan para validación)
│ ├── selected_features.csv # Lista de features finales
│ ├── xtrain.csv / xtest.csv # Features numéricos (opcional)
│ └── ytrain.csv / ytest.csv # Target (opcional)
├── models/
│ ├── restaurant_revenue_pycaret.pkl # Modelo final guardado por PyCaret
│ └── minmax_scaler_selected.joblib # Scaler entrenado SOLO en selected_features
├── Dockerfile.prod # Imagen de producción (Streamlit)
├── cloudbuild.yaml # Build y push a Artifact Registry
├── service.yaml # Definición del servicio Cloud Run
├── gcr-service-policy.yaml # IAM policy (invoker)
├── requirements.txt # Dependencias de producción
├── README.md
└── notebooks/ # EDA, training, etc.Nota:
restaurant_revenue_pipeline.pkles el artefacto que se usa en producción (contiene el encoder, aligner, scaler y el predictor).
Generados y versionados en este proyecto:
models/restaurant_revenue_pycaret.pkl
Modelo final guardado por PyCaret (incluye preprocesado de PyCaret si se usó).models/minmax_scaler_selected.joblib
Scaler entrenado solo sobre lasselected_features(evita mismatch de columnas).data/processed/selected_features.csv
Orden de columnas esperado por el pipeline.src/models/restaurant_revenue_pipeline.pkl
Pipeline completo de inferencia (el que carga la app Streamlit).
python3.11 -m venv .venv
source .venv/bin/activate # Windows: .venv\\Scripts\\activate
pip install -r requirements.txt
# Ejecutar SIEMPRE desde la raíz del repo
streamlit run src/modelo_ml_streamlit.pyLa app busca el pipeline relativo a
src/, así que ejecuta desde la raíz del proyecto.
docker build -t restaurant-revenue:prod -f Dockerfile.prod .
docker run --rm -p 8080:8080 -e PORT=8080 restaurant-revenue:prod
# abre http://localhost:8080.devcontainer/devcontainer.json+Dockerfile.dev(Python 3.11-slim + gcloud CLI)- Abre con la extensión Dev Containers → Reopen in Container.
-
Artifact Registry (una sola vez por repo)
gcloud artifacts repositories create repo-proyecto-revenue-streamlit \ --repository-format docker \ --project mlops12-carlos-sanchez \ --location us-central1
-
Build & Push con Cloud Build
# cloudbuild.yaml steps: - name: 'gcr.io/cloud-builders/docker' args: ['build', '-f', 'Dockerfile.prod', '-t', 'us-central1-docker.pkg.dev/mlops12-carlos-sanchez/repo-proyecto-revenue-streamlit/image-v7-api-datapath:latest', '.'] - name: 'gcr.io/cloud-builders/docker' args: ['push', 'us-central1-docker.pkg.dev/mlops12-carlos-sanchez/repo-proyecto-revenue-streamlit/image-v7-api-datapath:latest']
gcloud builds submit --config=cloudbuild.yaml --project mlops12-carlos-sanchez
-
Cloud Run (Service manifest)
# service.yaml apiVersion: serving.knative.dev/v1 kind: Service metadata: name: servicio-modelo-revenue-carlos-sanchez spec: template: spec: containers: - image: us-central1-docker.pkg.dev/mlops12-carlos-sanchez/repo-proyecto-revenue-streamlit/image-v7-api-datapath:latest ports: - containerPort: 8080
gcloud run services replace service.yaml \ --region us-central1 \ --project mlops12-carlos-sanchez
-
IAM (hacer público el servicio)
# gcr-service-policy.yaml bindings: - members: - allUsers role: roles/run.invoker
gcloud run services set-iam-policy servicio-modelo-revenue-carlos-sanchez \ gcr-service-policy.yaml \ --region us-central1 \ --project mlops12-carlos-sanchez
URL de ejemplo:
https://servicio-modelo-revenue-carlos-sanchez-770575062081.us-central1.run.app
-
Subir CSV RAW con columnas como:
Id(opcional)City Group(categórica)Type(categórica)P1...Pn(numéricas)revenue(opcional; si existe, se usan métricas RMSE/MAE/R²)
-
La app mostrará:
- Vista previa (muestra recortada para CSVs grandes)
- Predicciones (columna
revenue_pred) - Si hay
revenue, métricas y gráfico “Predicho vs Real” - Botón para descargar CSV con predicciones
-
“No se encontró el pipeline en 'src/models/restaurant_revenue_pipeline.pkl'”
Ejecutastreamlit run src/modelo_ml_streamlit.pydesde la raíz del repo (o usa rutas basadas en__file__).
Verifica que el.pklesté dentro de la imagen ensrc/models/. -
Error cargando pipeline: Can't get attribute 'PyCaretPredictor' ...
Las clases custom (p. ej.PyCaretPredictor,FeatureDropper, etc.) deben estar definidas en el script donde se hacejoblib.load, o en un módulo importable estable. Ya están incluidas enmodelo_ml_streamlit.py. -
'numpy.ndarray' object has no attribute 'to_numpy'
No encadenes.to_numpy()sobre algo que ya esndarray. Primeropd.to_numeric(serie).to_numpy()y después recorta/usa comondarray. -
Invalid value for the index parameter. There are duplicate indices ...(PyCaret)
Resetea el índice del DataFrame de predicción antes de llamar apredict_model(el pipeline ya lo hace internamente). -
Columnas esperadas por el scaler ausentes
Asegúrate de usar el scaler entrenado solo conselected_features(minmax_scaler_selected.joblib) y de alinear columnas conFeatureAligner. -
Front-end Streamlit “Importing a module script failed.”
Suele ocurrir por tablas muy grandes (p. ej., 100k filas). Muestra muestras (head(200)) y ofrece la descarga completa. -
Cloud Run no responde
- Ver logs:
gcloud logs tail --region us-central1 --project mlops12-carlos-sanchez \ --service servicio-modelo-revenue-carlos-sanchez
- Asegura
--server.port=${PORT}en el ENTRYPOINT. - Ajusta memoria/CPU si procesas archivos grandes.
- Ver logs:
Este repo viene del template e incluye integración lista para:
- Hydra: gestión de configs (opcional para este proyecto)
- pdoc: generar documentación de la API
- pre-commit: hooks (
ruff,black,mypy)
pre-commit installpdoc src -o docs # generar estático
pdoc src --http localhost:8080 # server local