Este proyecto se ha desarrollado como una solución anticipada para un posible proyecto futuro de predicción del peso de residuos que los camiones recolectarán al final de cada día, utilizando datos sintéticos debido a la falta de datos históricos reales. Esta decisión se basa en:
- Preparación Anticipada: Nos posicionamos para una rápida implementación cuando el proyecto real se materialice.
- Prueba de Concepto: Demostramos la viabilidad y el potencial del sistema.
- Desarrollo Iterativo: Permite mejorar continuamente sin las limitaciones de datos reales incompletos.
- Flexibilidad en el Diseño: Creamos un sistema adaptable a diferentes escenarios potenciales.
El sistema está diseñado para adaptarse fácilmente a datos reales cuando estén disponibles.
El proyecto busca predecir el peso total de residuos que cada camión recolectará al final del día para los próximos 30 días, crucial para la planificación logística y la optimización de operaciones de recolección de residuos.
- Desarrollar un modelo de aprendizaje automático preciso para predecir el peso de residuos por camión al final del día.
- Implementar un pipeline automatizado para procesamiento, entrenamiento y despliegue.
- Crear un sistema de monitoreo y reentrenamiento continuo.
- Proporcionar una interfaz para acceder y visualizar predicciones de peso de residuos.
- Almacenamiento: PostgreSQL con SQLAlchemy ORM
- Desarrollo de Modelos: MLflow y XGBoost
- Orquestación: Apache Airflow con DAGs
- Despliegue: Docker y Docker Compose
- API: FastAPI
- Frontend: Vue.js
El sistema genera predicciones en lote:
- Entrenamiento con datos históricos.
- Generación de predicciones para 30 días.
- Almacenamiento en base de datos.
- API sirve predicciones pre-calculadas.
Este enfoque es adecuado porque:
- No requiere actualizaciones en tiempo real.
- Permite uso eficiente de recursos computacionales.
- Asegura tiempos de respuesta rápidos.
- Evaluación diaria del rendimiento (R²)
- Registro de métricas con MLflow
- DAGs para decisión y ejecución de reentrenamiento
- Reentrenamiento automático si R² < 0.9 durante más de 3 días en una semana
Los datos sintéticos permiten:
- Simular patrones realistas de peso de camiones de residuos.
- Crear un conjunto de datos suficiente para entrenamiento y pruebas.
- Incorporar variaciones estacionales y eventos especiales.
- Controlar las características de los datos para probar diferentes escenarios.
- Uso de datos reales
- Técnicas avanzadas de feature engineering
- Experimentación con modelos más sofisticados
- Sistema de monitoreo más robusto
- Optimización del rendimiento
- Refuerzo de seguridad y cumplimiento normativo
- Mejora de escalabilidad
- Interfaz de usuario más avanzada
- Integración con sistemas externos
- Pruebas más exhaustivas
Servicios principales:
- Bases de datos (data-db, airflow-db, mlflow-db)
- Redis
- Airflow (webserver, scheduler, worker, triggerer)
- MLflow
- MinIO
- API de predicciones
- Frontend
La generación de datos sintéticos es crucial para este proyecto, ya que simula patrones realistas de peso de residuos recolectados por los camiones al final del día, en ausencia de datos históricos reales. Esta funcionalidad está implementada en utils/ml_helpers.py
y tiene en cuenta varios factores para crear un conjunto de datos que refleje la complejidad del mundo real:
def generate_total_weight(truck_id, day_of_week, month, day_of_year):
base_weight = truck_capacities[truck_id - 1] * truck_reliability[truck_id - 1]
# Weekend effect
if day_of_week in [5, 6]:
base_weight *= weekend_multipliers[day_of_week - 5]
# Seasonal effect (assuming Southern Hemisphere)
season_effect = np.sin(2 * np.pi * day_of_year / 365) * 200 + 200
base_weight += season_effect
# Monthly variation
base_weight += monthly_variations[month - 1]
# Special events (e.g., holidays)
if month == 12 and day_of_year >= 350: # Christmas period
base_weight *= christmas_multipliers[day_of_year - 350 - 1]
# Long-term trend (slight increase over time)
trend = day_of_year * 0.5
base_weight += trend
# Ensure weight is between 500 and 4000 kg
return max(500, min(4000, round(base_weight, 2)))
Los datos sintéticos generados intentan simular varios patrones y factores que afectan el peso de residuos recolectados:
-
Características del Camión: Cada camión tiene una capacidad y fiabilidad específica, lo que afecta la cantidad de residuos que puede recolectar.
-
Efecto de Fin de Semana: Se aplican multiplicadores especiales para los viernes y sábados, simulando posibles cambios en la generación de residuos durante los fines de semana.
-
Efecto Estacional: Se utiliza una función sinusoidal para simular cambios estacionales en la generación de residuos, asumiendo un hemisferio sur (con picos en verano y valles en invierno).
-
Variaciones Mensuales: Cada mes tiene una variación específica que se añade al peso base, reflejando posibles patrones mensuales en la generación de residuos.
-
Eventos Especiales: Se incluye un efecto especial para el período navideño (últimos días de diciembre), aplicando multiplicadores que podrían representar un aumento en la generación de residuos debido a la temporada festiva.
-
Tendencia a Largo Plazo: Se añade una ligera tendencia al alza a lo largo del año, lo que podría representar un crecimiento gradual en la generación de residuos.
-
Límites Realistas: El peso final se limita entre 500 y 4000 kg para mantener valores dentro de un rango realista para la recolección diaria de residuos por camión.
Esta aproximación a la generación de datos sintéticos permite crear un conjunto de datos que exhibe patrones complejos y realistas, ideal para entrenar y probar el modelo de predicción. Además, proporciona la flexibilidad necesaria para ajustar estos patrones según sea necesario, permitiendo simular diferentes escenarios y probar la robustez del modelo bajo diversas condiciones.
ingest_and_process_data_dag.py
: Ingesta y procesamiento diariopredictor_dag.py
: Generación de prediccionesretraining_decision_dag.py
: Evaluación de rendimientomodel_retraining_deployment_dag.py
: Reentrenamiento y despliegue
La API de predicciones está implementada utilizando FastAPI en predictions_api/main.py
. Actualmente, la API ofrece un único endpoint que devuelve las predicciones para todos los camiones en los próximos 30 días:
@app.get("/predictions", response_model=list[PredictionResponse])
async def get_predictions():
db = SessionLocal()
try:
today = date.today()
predictions = db.query(Prediction).filter(Prediction.date >= today).all()
return predictions
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
finally:
db.close()
Este endpoint proporciona una vista general de todas las predicciones disponibles. Sin embargo, en futuras iteraciones, la API podría mejorarse para soportar consultas más específicas y flexibles. Algunas posibles mejoras incluyen:
- Filtrado por camión: Permitir a los usuarios obtener predicciones para camiones específicos.
- Filtrado por fecha: Habilitar la consulta de predicciones para rangos de fechas personalizados.
- Paginación: Implementar paginación para manejar grandes volúmenes de datos de manera más eficiente.
- Ordenamiento: Permitir a los usuarios ordenar los resultados por diferentes criterios (por ejemplo, fecha, peso predicho, ID del camión).
- Agregaciones: Ofrecer endpoints para obtener estadísticas agregadas, como el peso total predicho por día o por camión.
Ejemplo de un posible endpoint futuro con filtros:
@app.get("/predictions/filtered", response_model=list[PredictionResponse])
async def get_filtered_predictions(
start_date: date = Query(None),
end_date: date = Query(None),
truck_id: int = Query(None),
limit: int = Query(100, le=1000),
offset: int = Query(0, ge=0)
):
db = SessionLocal()
try:
query = db.query(Prediction)
if start_date:
query = query.filter(Prediction.date >= start_date)
if end_date:
query = query.filter(Prediction.date <= end_date)
if truck_id:
query = query.filter(Prediction.truck_id == truck_id)
total = query.count()
predictions = query.offset(offset).limit(limit).all()
return {
"total": total,
"predictions": predictions
}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
finally:
db.close()
Estas mejoras permitirían a los usuarios de la API obtener datos más específicos y relevantes para sus necesidades, mejorando la utilidad y flexibilidad del sistema de predicción.
Almacena datos brutos y predicciones con tablas optimizadas e indexadas:
CREATE TABLE IF NOT EXISTS raw_data (
id BIGSERIAL PRIMARY KEY,
date DATE,
truck_id INTEGER,
total_weight FLOAT,
day_of_week INTEGER,
month INTEGER,
day_of_year INTEGER,
UNIQUE (truck_id, date)
);
CREATE INDEX idx_raw_data_truck_id ON raw_data(truck_id);
CREATE INDEX idx_raw_data_date ON raw_data(date);
Incluidos en el pipeline CI/CD:
- name: Format with Black
run: black airflow/dags/ predictions_api/
- name: Lint with flake8
run: flake8 airflow/dags/ predictions_api/
- name: Check DAG integrity
run: |
PYTHONPATH=$PYTHONPATH:${{ github.workspace }}/airflow/dags python -c "from airflow.models import DagBag; import os; dag_folder = os.path.join(os.getcwd(), 'airflow', 'dags'); db = DagBag(dag_folder=dag_folder, include_examples=False); assert len(db.import_errors) == 0, db.import_errors"
- name: Run pytest
run: pytest
graph TD
A[Datos Sintéticos] -->|Ingesta| B(data-db)
B -->|Extracción| C{Airflow}
C -->|Entrenamiento| D[MLflow]
D -->|Registro de Modelo| E[Registro de Modelos]
E -->|Despliegue| F[FastAPI]
F -->|Servir Predicciones| G[Frontend Vue.js]
C -->|ingest_and_process_data_dag| H[Procesamiento de Datos]
H -->|Lectura/Escritura| B
H -->|Lectura| D
C -->|predictor_dag| I[Generación de Predicciones]
I -->|Lectura/Escritura| B
I -->|Lectura| D
C -->|retraining_decision_dag| J[Decisión de Reentrenamiento]
J -->|Lectura| D
C -->|model_retraining_deployment_dag| K[Reentrenamiento y Despliegue]
K -->|Lectura/Escritura| D
K -->|Lectura| B
C -->|evaluate_current_model_dag| R[Evaluación del Modelo Actual]
R <-->|Lectura/Escritura| D
R -->|Lectura| B
L[(airflow-db)] <-->|Metadata| C
M[(mlflow-db)] <-->|Experimentos| D
N[MinIO] -->|Artefactos| D
O[Redis] -->|Resultados| C
Este proyecto implementa un sistema completo de predicción del peso de residuos que los camiones recolectarán al final de cada día, desde la generación de datos sintéticos hasta la visualización de predicciones, utilizando tecnologías modernas para crear una solución robusta y escalable.