Contributors | GitHub's username |
---|---|
Carlos Román López Sierra | Carlosrlpzi |
José Luis Zárate Cortés | jlrzarcor |
Octavio Fuentes Ortiz | fuentesortiz |
Patricia Urriza Arellano | patyua |
Uriel Abraham Rangel Díaz | urieluard |
Mediante un algoritmo de clasificación, este proyecto tiene como objetivo ayudar a los establecimientos de comida de la ciudad de Chicago a contestar la pregunta analítica: ¿mi establecimiento pasará o no la inspección?
Este producto de datos funciona de forma programática, pues cumpliendo el objetivo planteado, el establecimiento que utilice nuestro modelo, obtendrá una predicción sobre si pasará o no la inspección, para tener oportunidad de tomar acciones preventivas y/o correctivas, en función de la salida que genere el modelo.
- Acerca de este proyecto
- Estructura básica del proyecto
- Sobre nuestro Data Pipeline
- Sesgos e Inequidades
- ¿Cómo ejecutar nuestro pipeline?
- Consultas a través de nuestra api
- Monitoreo del modelo
NOTA: Imagen tomada de la página de Chicago Data Portal.
-
Trabajamos con la base de datos de Chicago Food Inspections.
-
La información se obtiene de las inspecciones de restaurantes y otros establecimientos de comida en Chicago desde enero de 2010.
-
Las inspecciones se realizan por personal del Chicago Department of Public Health’s Food Protection Program siguiendo un procedimiento estandarizado.
NOTA: Información actualizada hasta el '25/05/2021'.
Volver a 'Tabla de Contenido' 💾 🔘
├── README.md <- The top-level README for developers using this project.
│
├── conf
│ ├── base <- Space for shared configurations like parameters.
│ └── local <- Space for local configurations, usually credentials.
│
├── notebooks <- Jupyter notebooks.
│ ├── eda
│ └── feature_engineering
│
├── images <- Contains images used in the repository.
│
├── requirements.txt <- The requirements file.
│
├── .gitignore <- Avoids uploading data, credentials, outputs, system files etc.
│
├── sql <- Contains scripts used to deploy RDS db.
│
└── src <- Source code for use in this project.
│
├── __init__.py <- Makes src a Python module.
│
│
├── api <- Contains Python modules used for app deplyment.
│
│
├── dashboard <- Contains Python modules used for dashboard deplyment.
│
│
├── utils <- Functions used across the project.
│
│
├── etl <- Scripts to transform data from raw to intermediate.
│
│
└── pipeline <- Functions used for the pipeline.
Volver a 'Tabla de Contenido' 💾 🔘
Nuestro proyecto está conformado por diferentes tasks. Para ordenar la secuencia que éstos deben seguir, utilizamos un orquestador llamado Luigi.
NOTA: Imagen tomada del Luigi's GitHub.
Fuente:Luigi's documentation
Este orquestador es la herramienta que nos permite correr nuestro data pipeline, definiendo aspectos importantes del cómo, cúando y con qué se debe correr cada proceso.
Para administrar el orden de las tareas (cómo nuestros distintos tasks correrán) en el pipeline, Luigi utiliza una estructura de datos llamada DAG (Directed Acyclic Graph). Es una herramienta visual útil y que ilustra de manera clara los procesos que nuestro proyecto sigue.
La estructura de nuestro pipeline es la siguiente:
En los módulos siguientes se integran las funciones que nos permitirán realizar todo el proceso de tasks y tasks de metadata:
📂 Se encuentran ubicadas en la rama main
dentro de la carpeta src
de la siguiente manera:
├── src
│
├── etl
│ ├── task_almacenamiento.py
│ ├── task_almacenamiento_metadata.py
│ ├── task_almacenamiento_unit_test.py
│ ├── task_feature_engineering.py
│ ├── task_feature_engineering_metadata.py
│ ├── task_feature_engineering_unit_test.py
│ ├── task_ingesta.py
│ ├── task_ingestion_metadata.py
│ ├── task_ingestion_unit_test.py
│ ├── task_limpieza.py
│ ├── task_limpieza_metadata.py
│ ├── task_limpieza_unit_test.py
│ ├── task_modelo.py
│ ├── task_modelo_metadata.py
│ ├── task_modelo_unit_test.py
│ ├── task_predicciones.py
│ ├── task_predicciones_metadata.py
│ ├── task_predicciones_unit_test.py
│ ├── task_sesgo_inequidades.py
│ ├── task_sesgo_inequidades_metadata.py
│ ├── task_sesgo_inequidades_unit_test.py
│ ├── task_training.py
│ ├── task_training_metadata.py
│ └── task_training_unit_test.py
│
├── pipeline
│ ├── ingesta_almacenamiento.py
│ ├── task_api_almacenamiento.py
│ └── task_monitoreo_modelo.py
📂 Los unit test que realizamos para probar nuestro data pipeline se encuentran ubicadas en la rama main
dentro de la carpeta src
de la siguiente manera:
├── src
│
├── test
│ ├── __init__.py
│ ├── test_almacenamiento.py
│ ├── test_feature_engineering.py
│ ├── test_ingestion.py
│ ├── test_limpieza.py
│ ├── test_modelo.py
│ ├── test_predicciones.py
│ ├── test_sesgo_inequidad.py
│ └── test_training.py
Y así se ve el DAG de nuestro data pipeline orquestado en Luigi:
NOTA: El color verde indica que los tasks corrieron de manera exitosa.
Volver a 'Tabla de Contenido' 💾 🔘
Machine Learning por naturaleza es discriminante, pues lo que hacemos es discriminar datos a través del uso de la estadística.
Esta discriminación puede ser un problema cuando brinda:
- Ventajas sistemáticas a grupos privilegiados.
- Desventajas sistemáticas a grupos no privilegiados.
Es de nuestro interés identificar y cuantificar sesgos e inequidades en diferentes grupos, para después mitigarlos y cuantificar las consecuencias en las métricas de desempeño off-line.
NOTA: Imagen tomada del Aequitas' GitHub.
Aequitas es un toolkit open source que utilizamos en nuestro proyecto para medir sesgo e inequidad. Fue desarrollado por DSSG.
Realizamos dos ejercicios:
-
Atributo protegido:
facility_type
. -
Al existir 500 tipos de 'facility_type', decidimos clasificar los grupos de mayor representación, obteniendo (por orden de mayor a menor representación) las siguientes categorías:
restaurant
, school
, grocery store
, children's services facility
, daycare
y other
.
NOTA: 'other' se creó debido a la heterogeneidad de establecimientos que ya no entraban en las categorías previas.
-
Atributo protegido:
zip
. -
Creamos una tabla de códigos postales clasificados por 4 categorías de tipo de ingreso:
high
, low-mid
, downtown
y other
.
NOTA: 'other' se creó debido a que hay algunos códigos que se encuentran fuera del área de Chicago y de los cuales no contamos con su clasificación.
- Para la creación de estas 4 categorías nos apoyamos en los siguientes 2 mapas:
Fuente: Community Areas and Related Zip Codes.
Fuente: Community Areas by Income.
-
Grupo de referencia:
restaurant
. -
¿Por qué? Porque es la que tiene mayor representación en la base de datos y el objetivo sería que no haya sesgo en las predicciones con etiqueta negativa hacia este tipo de establecimiento.
-
Grupo de referencia:
low-mid
. -
¿Por qué? Porque el objetivo sería que no haya sesgo en las predicciones con etiqueta negativa hacia este tipo de zonas (con menor ingreso), que pudieran generar mayor disparidad respecto a las demás, tomando en cuenta que la cancelación de licencias de restaurantes puede afectar sensiblemente a la economía o el desarrollo de alguna zona.
-
Nuestro modelo es
asistivo
. -
¿Por qué? De acuerdo a nuestra pregunta analítica y tomando en cuenta que el producto de datos está orientado para que el uso sea por parte de los establecimientos y no por parte del Gobierno de Chicago, consideramos que el modelo le permitirá a los dueños de los establecimientos prevenir posibles multas o cancelaciones de licencia por incumplimiento, al momento de realizar consultas sobre si su establecimiento pasaría o no una inspección.
-
Interpretación: la probabilidad de clasificar una inspección como aprobada/fallida dado su
facility_type
o dado suzip
y que realmente haya sido aprobada/fallida. -
Seleccionamos la métrica porque al ser los negocios los usuarios del modelo, éstos cuentan con recursos limitados (personal, horas laborales, recursos económicos, etc) y queremos asegurarnos que estos recursos no sean utilizados innecesariamente para poner en orden al establecimiento de tal manera que esté listo para aprobar la inspección.
-
Interpretación: la probabilidad de que hayamos clasificado una inspección como fallida dada su
facility_type
o suzip
y que la inspección sí haya aprobado. -
Seleccionamos la métrica porque al ser los negocios los usuarios del modelo, nos interesa minimizar los posibles recursos que éstos inviertan en poner al establecimiento listo para la inspección, cuando realmente no sea necesario.
Volver a 'Tabla de Contenido' 💾 🔘
- Se requiere configurar en AWS una infraestructura como la mostrada en la imagen siguiente:
NOTA: La configuración de cada instancia, así como de la RDS queda fuera del alcance de este README.
- Debido a que utilizamos RDS para almacenar tablas de los datos generados en algunos Tasks, debemos contar con credenciales que nos permitan entrar a ésta. Para ello, debemos crear un archivo
credentials.yaml
con las claves adecuadas, de tal manera que contenga la siguiente estructura:
---
s3:
aws_access_key_id: "de_tu_cuenta_de_AWS"
aws_secret_access_key: "de_tu_cuenta_de_AWS"
food_inspections:
api_token: "de_tu app_token_del_chicago_data_portal"
pg_service:
user: "tu_postgres_user"
password: "tu_postgres_user_password"
host: "direccion_de_tu_RDS.us-west-2.rds.amazonaws.com"
port: 5432
dbname: "nombre_base_datos"
El cual se debe colocar en la carpeta conf/local
.
- Crear el archivo de configuración
.pg_service.conf
para el servicio Postgres:
[alias_servicio]
user=user_rol_postgres
password=password_user_rol
host=end_point_user_RDS
port=5432
dbname=chicagofoodinsp
NOTA: 'alias_ servicio' es el identificador de las credenciales especificadas de servicio.
El cual se debe colocar en el directorio raíz de la instancia EC2:
> Directorio del archivo en sistema: ~/.pg_service.conf
-
Tener en ejecución la infraestructura de AWS.
-
Abrir su terminal, posicionarse en la carpeta
/home/.ssh
y correr:
ssh -i nombre_llave_.pem su_usuario@ec2-direccion-de-la-EC2.us-west-2.compute.amazonaws.com
para conectarse a la instancia EC2 (procesamiento).
- Clonar el repositorio del proyecto:
git clone <url del repositorio> <nombre que desea poner al repositorio dentro de su sistema>
.
- Instalar 'pyenv' en la instancia de procesamiento y crear un ambiente virtual llamado 'itam_dpa' que tenga :
pyenv install 3.7.4
.
-
Activar su ambiente virtual:
pyenv activate itam_dpa
. -
Instalar 'pip':
sudo apt install python3-pip
. Asegurarse que el usuario tiene privilegios de sudo (super user). -
Instalar nuestro requirements.txt:
pip install -r requirements.txt
. -
Posicionarse en la carpeta del repositorio clonado en el paso 3.
-
De ser necesario actualizar el repositorio clonado:
git pull
. -
Declarar las variables de ambiente con los comandos:
export PGSERVICEFILE=${HOME}/.pg_service.conf
export PGSERVICE=nombre_de_tu_service
export PYTHONPATH=$PWD
- De igual manera, es necesario crear la infraestructura de tablas en
psql
para almacenar la metadata. Para lo anterior, debe tener acceso a la RDS como usuariopostgres
. Posicionarse en la carpeta/sql
y correr los siguientes comandos:
psql -f create_api_tables.sql
psql -f create_db.sql
psql -f create_schemas.sql
psql -f create_metadata_tables.sql
psql -f create_procdata_tables.sql
- A partir de este punto ya se ejecutan los tasks de Luigi:
El siguiente task es para generar un modelo:
PYTHONPATH="." luigi --module 'src.etl.task_sesgo_inequidades_metadata' TaskBFMeta --bucket nombre_de_tu_bucket --year 2020 --month 12 --day 31 --flg-i0-c1 0 --local-scheduler
El siguiente task se corre después del anterior para generar predicciones a partir del mejor modelo seleccionado:
PYTHONPATH="." luigi --module 'src.pipeline.task_monitoreo_modelo' TaskDashData --bucket nombre_de_tu_bucket --year año_deseado --month mes_deseado --day dia_deseado --flg-i0-c1 1 --local-scheduler
🚨 NOTA: Este comando 👆 ejecuta todos los tasks de nuestro pipeline. 🚨
Tomar en cuenta:
--local-scheduler
se utiliza para no llamar luigid
.
--flg-i0-c1
se puede escribir 0 (ingesta inicial) ó 1 (ingesta consecutiva).
prc-path
es la ruta de la subcarpeta que almacena el proceso. Por default nosotros lo llamamos ingestion
.
--avg-prec
que sea mayor al promedio de la precisión por grupo que se reporte en la tabla de métricas (en general valores mayores 95 funcionan bien).
- Si el task corrió de manera exitosa, el siguiente mensaje es desplegado:
Volver a 'Tabla de Contenido' 💾 🔘
Para hacer consultas de nuestro modelo predictivo, creamos una api utilizando Flask.
-
Posicionarse en la ruta:
src/api
. -
Correr el comando:
export FLASK_APP=flask_cfi.py
. -
Correr el comando:
flask run --host=0.0.0.0
. -
En una terminal local, posicionarse en
.ssh
y correr el comando:
ssh -o ServerAliveInterval=60 -i id_rsa -N -f -L localhost:5000:localhost:5000 nombre_usuario@ec2-direccion-de-la-EC2.us-west-2.compute.amazonaws.com
.
Después abrir el browser de tu preferencia e ingresar a la siguiente dirección: localhost:5000/
.
Una vez hecho lo anterior, la api se verá de la siguiente manera:
La api cuenta con 2 endpoints:
-
cfi_license
: Devuelve las predicciones del número de licencia del establecimiento consultado. -
cfi_prediction_date
: Devuelve las predicciones realizadas en la fecha consultada.
Volver a 'Tabla de Contenido' 💾 🔘
Para realizar el monitoreo de nuestro modelo, construimos un dashboard utilizando Dash de Plotly.
-
Posicionarse en la ruta:
src/dashboard
. -
Correr el comando:
python3 app.py
. -
En una terminal local, posicionarse en
.ssh
y correr el comando:
ssh -o ServerAliveInterval=60 -i id_rsa -N -f -L localhost:8050:localhost:8050 nombre_usuario@ec2-direccion-de-la-EC2.us-west-2.compute.amazonaws.com
.
Después abrir el browser de tu preferencia e ingresar a la siguiente dirección: localhost:8050/
.
Una vez hecho lo anterior, el dashboard se verá de la siguiente manera:
El dashboard cuenta con 2 gráficas:
-
La primera representa la distribución de los scores obtenidos en el último conjunto de predicciones realizadas.
-
La segunda representa la distribución de los scores obtenidos con el último modelo entrenado.
Volver a 'Tabla de Contenido' 💾 🔘