Sebastián Cadavid-Sánchez
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Sesiones de Capacitación: Experimentos en `triage` **ITAM Centro de Ciencia de Datos** **Elizabeth Rodriguez Sánchez - Sebastián Cadavid-Sánchez** **Febrero 15, 2021** ### 0. Recordatorio Para utilizar `triage`, es necesario que tengamos estructurados nuestros datos de acuerdo al _Schema_ _Semantic_. Es decir, con tablas de entidades (estudiantes, profesores, cursos) y las tablas de sus respectivos eventos. En particular, las tablas deben poder relacionarse entre sí, para ello debemos tener un identificador de las entidades, y un identificador de los eventos que les ocurren a las mismas. - Por ejemplo, si quisieramos ver los datos estáticos del el estudiante con identificador 116,862 en la base de datos: podríamos realizar el siguiente _query_: ```{SQL} select * from semantic.alumnos a where alumno = 116862; ``` - Si quisieramos ver todos los eventos en la historia académica del mismo alumno: ```{SQL} select * from semantic.eventos_alumnos where alumno = 116862; ``` **Nota:** `triage` requiere que los identificadores de entidades y alumnos sean de tipo `int`. ## 1. Crear `virtualenv` en bastión Triage es compatible con `python 3.6+` , por lo que primero lo debemos instalar en `pyenv`: ```bash pyenv install 3.7.9 ``` Creamos un ambiente virtual para el proyecto: ```{bash} pyenv virtualenv triage-ccd ``` Lo activamos: ```{bash} pyenv activate triage-ccd ``` Instalar `triage`: ```bash pip install triage ``` O bien, si queremos la versión más actualizada (recomendado): ```bash pip install git+https://github.com/dssg/triage ``` **Nota: Spot Instances AWS** En la sección de AMIs de AWS del CCD creamos la imagen `proyecto_itam_triage` con un`pyenv` estable ( _name:_`triage_itam`, _AMI name:_ triage-ccd-official-v2) para correr los experimentos en `SPOT instances`. ## 2. Crear configuración del experimento La configuración de un experimento en `triage` es un archivo `.yaml` y tiene las siguientes secciones: - Información del experimento - Configuración temporal (_temporal_config_) - Definición de cohortes (_cohort_config_) - Configuración del la etiqueta (_label_config_) - Variables explicativas (_features_) - Metricas a optimziar (_scoring_) - _Grid_ de modelos (grid_config) Por medio de un ejemplo, donde el `config_file` del experimento se llamaŕa `reprobados.yaml` explicaremos cada una de las secciones y como ejecutar el experimento en una `SPOT instance`. ### 2.1 Información del experimento Utilizando la notación de archivos `yaml`, podemos definir las siguientes claves con sus valores asociados, las cuales tienen como objetivo enriquecer los metadatos del experimento ejecutado. ```{yaml} config_version: 'v7' random_seed: 6174 model_comment: 'primera_prueba' user_metadata: label_definition: 'perderan_materias' experiment_type: 'baseline' file_name: 'reprobados.yaml' description: | Primer experimento con triage - modelos básicos purpose: 'baseline' org: 'ITAM' team: 'Centro de Ciencia de Datos' author: 'Paola-Elizabeth-Sebastian' etl_date: '27-10-2020' ``` De las anteriores claves, `model_comment` es la única obligatoria, y será esencial para la lectura de los resultados de los experimentos. ### 2.2 Configuración temporal, Timechop (_temporal_config_) En este tipo de ejercicios utilizamos data longitudinal de entidades y cada cierta periodicidad hacermos predicciones sobre las etiquetas de las mismas. Para el momento en que nuestros modelos hacen predicción tienen disponible solo cierta cantidad de información, y esto va a determinar totalmente la forma en que hacemos predicción, pues debemos usar ''validación cruzada temporal". Con base en lo anterior debemos "cortar" (_chop_) los datos para construir nuestras matrices de entrenamiento y validación temporales. Podemos evidenciar lo anterior gráficamente: ![](https://i.imgur.com/LGaIjvo.png =600x700) > Tomado de la [documentación](https://dssg.github.io/triage/dirtyduck/triage_intro/) de `triage`. En `triage` esta sección se inicia con la clave `temporal_config`, y nos va a permitir definir el **_timechop_** que determina la forma en que hacemos predicción. En particular, determinar la forma como se define la matriz de entrenamiento, y las etiquetas a utilizar. A continuación podemos ver un ejemplo sencillo de la estructura de esta sección: ```yaml temporal_config: feature_start_time: '1990-08-01' feature_end_time: '2020-08-01' label_start_time: '2003-08-15' label_end_time: '2020-08-15' label_timespans: ['1y'] model_update_frequency: '1y' training_as_of_date_frequencies: '1y' max_training_histories: '10y' test_durations: '1y' test_as_of_date_frequencies: '1y' ``` - `feature_start_time` y `feature_end_time`: Los datos de _features_ son agregados desde y antes de dichas fechas, respectivamente. - `label_start_time` y label_end_time: Los datos de _labels_ son agregados desde y antes de dichas fechas, respectivamente. - `label_timespans`: Cuanto tiempo hacia adelante se predice el _label_. - `max_training_histories`: La máxima cantidad de información que se utiliza para una entidad. Depende de que tanto se espera que tanto hayan cambiado los patrones de comportamiento. - `test_durations y `model_update_frequency`: `cuanto tiempo debe ser usado un modelo para hacer predicciones, y cada cuanto se actualiza, respectivamente. - `test_as_of_date_frequencies`: Espaciamiento entre columnas apra una misma entidad. Gŕaficamente, el _timechop_ para este experimento se ve así: ![](https://i.imgur.com/H9YkwPf.png) Más adelante mostraremos como obtener esta imagen a partir de la configuración del experimento. ### 2.3 Cohorte Esta sección se inicia con la clave `label_config`, y en ella debemos definir el **_query_ parametrizado** que nos permite seleccionar la cohorte en cada periodo de interés. En el siguiente ejemplo presentamos el query que extrae los estudiantes sin materias reprobadas al inicio del semestre. ```yaml cohort_config: query: | with semestres as( select semestre_codigo from semantic.semestres where nivel = 'licenciatura' and tipo in ('otoño', 'primavera') and fecha_inicio >= (timestamp '{as_of_date}'- interval '3 weeks')::date and fecha_fin <= (timestamp '{as_of_date}'+ interval '6 months')::date ), reprobados as( select alumno from semantic.eventos_alumnos as current_semester_students where fecha < '{as_of_date}' and tipo = 'reprobar curso' ) -- alumnos al inicio del semestre select distinct alumno::int as entity_id from semantic.eventos_alumnos as current_semester_students inner join semestres on current_semester_students.atributos->>'semestre_codigo' = semestres.semestre_codigo where current_semester_students.tipo = 'dar de alta curso' and alumno not in (select alumno from reprobados) name: 'alumnos_sin_reprobadas' ``` ### 2.4 Configuración del la etiqueta (_label_config_) Esta sesión empiza con la clave `label_config`, seguida por el **_query_ parametrizado** para determinar la etiqueta de los alumnos sobre los que se realiza predicción. ```yaml label_config: query: | select alumno::int as entity_id, '{as_of_date}'::date as as_of_date, bool_or(tipo = 'reprobar curso')::int as outcome from semantic.eventos_alumnos where fecha >= ('{as_of_date}'::timestamp + interval '12 months')::date and fecha < ('{as_of_date}'::timestamp + interval '{label_timespan}')::date group by alumno name: 'reprueban' ``` ### 2.5 Features Esta sección se inicia con la clave `feature_aggregations`. ```yaml ## Features feature_aggregations: ### Proceso de admisión en el ITAM - prefix: 'examen_admision' from_obj: | (select alumno::int as entity_id, fecha, (atributos->>'calificacion')::int as calificacion from semantic.eventos_alumnos where tipo = 'presentar examen administrativo' and atributos->>'examen'= 'paat') as examen knowledge_date_column: 'fecha' aggregates_imputation: count: type: 'zero_noflag' max: type: 'constant' value: 1375 aggregates: - quantity: total: "calificacion" metrics: - 'max' intervals: ['all'] groups: - 'entity_id' ### sociodemográficas # género - prefix: 'genero' from_obj: | (with primer_semestre as( select alumno, fecha from semantic.eventos_alumnos where tipo = 'terminar semestre' and atributos->>'semestre_nominal' = '1' ) select alumno::int as entity_id, genero, fecha from semantic.alumnos left join primer_semestre using(alumno) ) as generos knowledge_date_column: 'fecha' categoricals_imputation: all: type: 'mean' categoricals: - # gender column: genero choice_query: 'select distinct genero from semantic.alumnos' metrics: - 'max' intervals: ['all'] groups: - 'entity_id' ``` Aunque definimos dos grupos de _features anteriormente_, podemos ver los efectos que tienen las diferentes combinaciones de las mismas dentro de nuestros modelos. Para lo anterior, utilizamos la clave `feature_group_definition`. Dentro de sus valores, podemos especificar una o varias de las siguientes opciones: - `all` - `leave-one-out` - ` leave-one-in` - `all-combinations` La opción _default_ es `all`. ```yaml feature_group_definition: prefix: - 'examen_admision' - 'genero' feature_group_strategies: ['all-combinations'] ``` En este ejemplo no explotamos mucho la naturaleza de las opciones que evaluan distintas combinaciones. Sin embargo, si consideramos un ejemplo con tres _features_: `examen_admision`, `genero`, y `promedio`, entonces la opción `all-combinations`, evaluaría los siguientes grupos de _features_: - `examen_admision` - `genero` - `promedio` - `examen_admision` y `genero` - `examen_admision`, y `promedio` - `genero`, y `promedio` - `examen_admision`, `genero`, y `promedio` ### 2.6 Métricas a optimizar (_scoring_) Esta sección se inicia con la clave `scoring`, y posteriormente se deben incluir las métricas a evaluar, y estas van a depender del objetivo y población de interés de nuestro proyecto. Por ejemplo, si sabemos que la institución tiene recursos para intervenir asistivamente a 50 estudiantes, probablemente qusieramos optimizar la métrica de `cobertura@50`: ```yaml scoring: testing_metric_groups: - metrics: [recall@] thresholds: top_n: [50] ``` Supongamos que se intervendrá de manera asistiva sobre el 5% de la población, en ese caso: ```yaml scoring: testing_metric_groups: - metrics: [recall@] thresholds: percentiles: [5.0] ``` Sin embargo, supongamos que en una primera fase sabemos que se intervendrá sobre 50 o 100 estudiantes, y no sabemos si la intervención será punitiva o asistiva. En ese caso, podríamos optimizar nuestro modelo de la siguiente forma: ```yaml scoring: testing_metric_groups: - metrics: [recall@, precision@] thresholds: percentiles: [50, 100] ``` ### 2.7 Modelos (_grid_config_) En esta sección podemos definir los experimentos que queremos utilizar. En particular, `triage` incorpora todos los modelos de la librería [`scikit-learn`](https://scikit-learn.org/stable/) y podemos utilizar la documentación de la misma para especificar los hiperparámetros deseados. La sección es una lista de diccionarios contenida en el diccionario `grid_config`. Posteriormente se especifica cada modelo como clase, y dentro del mismo, los hiperparámetros asociados. En este ejemplo consideraremos incluir dos tipos de modelos [`DecisionTreeClassifier`](https://scikit-learn.org/stable/modules/generated/sklearn.tree.DecisionTreeClassifier.html) y [`RandomForestClassifier`](https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html). ```yaml grid_config: 'sklearn.tree.DecisionTreeClassifier': criterion: ['entropy'] max_depth: [1, null] min_samples_split: [25] min_samples_leaf: [0.025, 0.10] 'sklearn.ensemble.RandomForestClassifier': n_estimators: [10, 50] max_depth: [null, 1, 2, 10] max_features: ['log2'] min_samples_split: [10] criterion: ['gini'] random_state: [2193] n_jobs: [-1] ``` #### 2.5.1 _Grids_ pre-definidos No es trivial definir qué y cuántos modelos queremos probar en nuestro experimento, ni acotar el especio de búsqueda de los hiperparámetros. Muchas veces esta decisión también depende de nuestra capacidad de computo. En esta dirección `triage` incluye algunos _grids_ recomendados, disponibles en [esta](https://github.com/dssg/triage/blob/master/src/triage/experiments/model_grid_presets.yaml) liga: - quickstart - small - medium - large Para correr nuestro experimento con alguno de ellos podemos en lugar de la sección `grid_config`, incluir por ejemplo: ```yaml model_grid_presets: ['quickstart'] ``` ## 3. Correr el experimento Para correr experimentos en triage podemos añadir algunos _flags_ utiles al comando `triage experiment config.yaml` dependiendo de nuestros objetivos. ### 3.1 Validar el experimento Para verificar que nuestro archivo de configuración es correcto sin correr el experimento, debemos ejecutar: ```bash triage experiment reprobados.yaml --validate-only ``` ### 3.2 Correr el experimento Una vez se ha verificado que el archivo de configuración es correcto, es posible ejecutar el experimento con diferentes opciones. ### 3.2.1 Base de datos Para que `triage` tenga acceso a la base de datos y pueda escribir los resultados de los experimentos en la misma, debemos proveerle las credenciales correspondientes en un archivo `.yaml`. A continuación se presenta un ejemplo con la estructura que debe tener dicho archivo: ```yaml # database.yaml host: database-amai.cfdemdotqpti.us-east-1.rds.amazonaws.com user: unusuario db: itam pass: unusuario_pass port: 5432 ``` Al correr el experimento, debemos añadir el _flag_ `-d `: ```bash triage experiment reprobados.yaml -d database.yaml ``` ### 3.2.2 Role Queremos que la escritura de los resultados en la base de datos brinde permisos de lectura a los demas usuarios del CCD, por lo tanto asignamos el _role_ a `itam_readwrite`. Para lo anterior corremos los experimentos utilizando el _script_ `itam_config.py`, presentado a continuación: ```python # itam_config.py from sqlalchemy.event import listens_for from sqlalchemy.pool import Pool @listens_for(Pool, "connect") def assume_role(dbapi_con, connection_record): print("Triage is assuming the role!") dbapi_con.cursor().execute('set role itam_readwrite;') print("Everything is OK!") ``` Al correr el experimento, debemos añadir el _flag_ `-s`: ```{bash} triage experiment reprobados.yaml -s PATH/itam_cong.py ``` ### 3.2.3 Guardar modelos entrenados y matrices de entrenamiento Para guardar los archivos `pickle` de los modelos y las matrices de entrenamiento podemos elegir distintos métodos, dado que estamos utilizando datos sensibles, no los debemos las matrices en nuestro PC personal. A continuación explicaremos como almacenar esta información en un bucket de S3. **Bucket de S3** Si queremos guardar nuestros resultados en un _bucket_ de S3, por ejemplo, `s3://ccd-itam` , primero, debemos tener nuestras credenciales de acceso guardadas en `~/.AWS/credentials`. Si se cumple la anterior condición, podemos ejecutar el experimento indicando la ruta del _bucket_ donde queremos que se guarden los experimentos con el _flag_ `--project-path`: ```bash triage experiment reprobados.yaml --project-path s3://ccd-itam/ejemplos_experimentos ``` ### 3.2.4 Paralelización Si el experimento contiene modelos para los cuales se puede paralelizar el entrenamiento podemos hacer uso de los núcleos que tenga nuestra CPU. Supongamos que son 4. Podemos utilizar el _flag_ `-n-processes`: ```bash triage experiment reprobados.yaml --n-processes 4 ``` ### 3.2.5 Reemplazar experimentos Si se desean sobreescribir experimentos con un `model_comment` existente, podemos ejecutar el _flag_ `--replace`: ```bash triage experiment reprobados.yaml --replace ``` ## 4 Ejemplo conjunto Para correr el experimento de ejemplo en paralelo, guardar los resultados en base de datos, y los modelos en `S3`: ```bash triage -s PATH/itam_config.py \ -d PATH/database.yaml \ experiment reprobados.yaml \ --n-processes 4 \ --project-path s3://BUCKETPATH ``` > Ejemplo en _spot_: > > Primero activamos el environment: > > ```bash > pyenv activate triage_itam > ``` > > Luego ejecutamos el comando: > > ```bash > triage -s itam_config.py -d database.yaml experiment src/experiments/reprobados.yaml --project-path 's3://ccd-itam/capacitacion' --n-processes 4 --validate-only > ``` > > Una vez validamos el experimento, podemos extraer el _timechop_ del mismo > > ```bash > triage -d database.yaml experiment src/experiments/reprobados.yaml --project-path 's3://ccd-itam/capacitacion' --show-timechop > ``` > > La imagen del _timechop_ se genera en el la ruta especificada en `project-path` dentro de una carpeta que se crear, con el nombre `images`. En este caso, en `s3://ccd-itam/capacitacion/images` se creó la imagen `reprobados.png` . Podemos extraerla desde Bastión. > > ```bash > ## En bastión > aws s3 ls s3://ccd-itam/capacitacion/images/ # revisar que se haya creado la imagen > aws s3 sync s3://ccd-itam/capacitacion/images/ ./capacitacion # copiarla > > ## En local (transferirla para verla) > scp -r -i ~/.ssh/id_scs_ccd scadavid@bastion.ccdatos.itam.mx:/home/scadavid/capacitacion /home/sebastian/Documents > > ``` ## 5 Exploración de resultados Cada combinación de matriz de entrenamiento, clasificador e hiperparámetros se considerará un **modelo**. A cada uno de los modelos que formen parte del experimento se le asignará un _hash_. Una vez entrenado, cada modelo se almacenará en un archivo pickle nombrado con el _hash_ que le corresponda, usando `joblib`. - Un `model_group` comprende todos los modelos que comparten parámetros e hiperparámetros, pero difieren en sus ventanas de entrenamiento. - `model` es un modelo entrenado para una ventana de tiempo específica. Al correr un experimento, se generan tres nuevos esquemas en la base de datos. Se trata de `triage_metadata`, `train_results`, y `test_results`. A continuación presentamos las principales tablas de los esquemas anteriores que utilizaremos para evaluar los resultados de nuestro experimento. El detalle de todas las tablas creadas puede consultarse en este [enlace](https://dssg.github.io/triage/experiments/running/#evaluating-results-of-an-experiment). ### 5.1 Esquema ```triage_metadata``` En este esquema encontraremos distintas tablas con metadata de los modelos y experimentos ejecutados. Las que usaremos más son: * `triage_metadata.models` - Contiene metadata sobre los modelos entrenados; contendrá un registro por cada modelo entrenado que se haya guardado, indicando el tipo de clasificador, sus hiper parámetros, el hash que se le asignó y el grupo de modelos a los que pertenece. ```bash= itam=> select * from triage_metadata.models m limit 3; model_id | model_group_id | model_hash | run_time | batch_run_time | model_type | hyperparameters | model_comment | batch_comment | config | built_by_experiment | train_end_time | test | train_matrix_uuid | training_label_timespan | model_size | random_seed | built_in_experiment_run ----------+----------------+----------------------------------+----------------------------+----------------------------+--------------------------------------------------------------------------+----------------------------------------------------------------------------------------------+---------------+---------------+--------+----------------------------------+---------------------+------+----------------------------------+-------------------------+------------+-------------+------------------------- 1 | 1 | 2e09ec7a1c919998537ebeb68720dd8b | 2020-10-28 06:30:36.564199 | 2020-10-28 06:30:30.664236 | sklearn.tree.DecisionTreeClassifier | {"criterion": "entropy", "max_depth": 1, "min_samples_leaf": 0.025, "min_samples_split": 25} | baseline_2c | | | f1da842d6f5d7e2a0a6c5631859dc8f5 | 2005-08-15 00:00:00 | f | 1f7adb221409ea2c308d3273d02c7e50 | 2 years | 0.0625 | 868976053 | 5 2 | 2 | c4c0ef5479793352c7c1eb9985d9600e | 2020-10-28 06:30:36.865583 | 2020-10-28 06:30:30.664236 | triage.component.catwalk.estimators.classifiers.ScaledLogisticRegression | {"C": 1.0, "penalty": "l2"} | baseline_2c | | | f1da842d6f5d7e2a0a6c5631859dc8f5 | 2005-08-15 00:00:00 | f | 1f7adb221409ea2c308d3273d02c7e50 | 2 years | 0.0625 | 1396809932 | 5 3 | 3 | 5efd69ff084e43c35e2422bc24dfae11 | 2020-10-28 06:30:37.13721 | 2020-10-28 06:30:30.664236 | sklearn.tree.DecisionTreeClassifier | {"criterion": "entropy", "max_depth": 1, "min_samples_leaf": 0.1, "min_samples_split": 25} | baseline_2c | | | f1da842d6f5d7e2a0a6c5631859dc8f5 | 2005-08-15 00:00:00 | f | 1f7adb221409ea2c308d3273d02c7e50 | 2 years | 0.0625 | 131653638 | ``` * `triage_metadata.model_groups` - Contiene los parámetros propios del grupo de modelos, así como las variables o _features_ utilizadas. ```bash= itam=> select * from triage_metadata.model_groups limit 3; model_group_id | model_type | hyperparameters | feature_list | model_config ----------------+--------------------------------------------------------------------------+----------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 1 | sklearn.tree.DecisionTreeClassifier | {"criterion": "entropy", "max_depth": 1, "min_samples_leaf": 0.025, "min_samples_split": 25} | {examen_admision_entity_id_all_total_imp,examen_admision_entity_id_all_total_max,prepa_entity_id_all_pase_8_0_max,prepa_entity_id_all_pase_8_1_max,prepa_entity_id_all_pase_8__NULL_max,reprobar_examen_intro_mate_entity_id_all_reprobar_0_max,reprobar_examen_intro_mate_entity_id_all_reprobar_1_max,reprobar_examen_intro_mate_entity_id_all_reprobar__NULL_max,reprobar_examen_redaccion_entity_id_all_reprobar_0_max,reprobar_examen_redaccion_entity_id_all_reprobar_1_max,reprobar_examen_redaccion_entity_id_all_reprobar__NULL_max} | {"state": "active", "label_name": "faltar_al_reglamento_2c", "cohort_name": "alumnos_inscritos_2c", "feature_groups": ["prefix: prepa", "prefix: examen_admision", "prefix: reprobar_examen_redaccion", "prefix: reprobar_examen_intro_mate"], "label_timespan": "2y", "as_of_date_frequency": "1y", "max_training_history": "10y"} 2 | triage.component.catwalk.estimators.classifiers.ScaledLogisticRegression | {"C": 1.0, "penalty": "l2"} | {examen_admision_entity_id_all_total_imp,examen_admision_entity_id_all_total_max,prepa_entity_id_all_pase_8_0_max,prepa_entity_id_all_pase_8_1_max,prepa_entity_id_all_pase_8__NULL_max,reprobar_examen_intro_mate_entity_id_all_reprobar_0_max,reprobar_examen_intro_mate_entity_id_all_reprobar_1_max,reprobar_examen_intro_mate_entity_id_all_reprobar__NULL_max,reprobar_examen_redaccion_entity_id_all_reprobar_0_max,reprobar_examen_redaccion_entity_id_all_reprobar_1_max,reprobar_examen_redaccion_entity_id_all_reprobar__NULL_max} | {"state": "active", "label_name": "faltar_al_reglamento_2c", "cohort_name": "alumnos_inscritos_2c", "feature_groups": ["prefix: prepa", "prefix: examen_admision", "prefix: reprobar_examen_redaccion", "prefix: reprobar_examen_intro_mate"], "label_timespan": "2y", "as_of_date_frequency": "1y", "max_training_history": "10y"} 3 | sklearn.tree.DecisionTreeClassifier | {"criterion": "entropy", "max_depth": 1, "min_samples_leaf": 0.1, "min_samples_split": 25} | {examen_admision_entity_id_all_total_imp,examen_admision_entity_id_all_total_max,prepa_entity_id_all_pase_8_0_max,prepa_entity_id_all_pase_8_1_max,prepa_entity_id_all_pase_8__NULL_max,reprobar_examen_intro_mate_entity_id_all_reprobar_0_max,reprobar_examen_intro_mate_entity_id_all_reprobar_1_max,reprobar_examen_intro_mate_entity_id_all_reprobar__NULL_max,reprobar_examen_redaccion_entity_id_all_reprobar_0_max,reprobar_examen_redaccion_entity_id_all_reprobar_1_max,reprobar_examen_redaccion_entity_id_all_reprobar__NULL_max} | {"state": "active", "label_name": "faltar_al_reglamento_2c", "cohort_name": "alumnos_inscritos_2c", "feature_groups": ["prefix: prepa", "prefix: examen_admision", "prefix: reprobar_examen_redaccion", "prefix: reprobar_examen_intro_mate"], "label_timespan": "2y", "as_of_date_frequency": "1y", "max_training_history": "10y"} ``` * `triage_metadata.experiment_runs` - Contiene información sobre la ejecución del experimento: hora de inicio, usuario, versión de triage, tipo de instancia EC2, status de la ejecución, y más. ```bash= itam=>select * from triage_metadata.experiment_runs er limit 3; id | start_time | start_method | git_hash | triage_version | experiment_hash|platform| os_user|working_directory| ec2_instance_type | log_location |experiment_class_path| experiment_kwargs|installed_libraries| matrix_building_started | matrices_made | matrices_skipped | matrices_errored | model_building_started | models_made | models_skipped | models_errored | last_updated_time | current_status | stacktrace | python_version | random_seed ----+----------------------------+--------------+------------------------------------------+----------------+----------------------------------+----------------------------------------------------+-----------+-------------------------------------------+-------------------+--------------+--------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-------------------------+---------------+------------------+------------------+------------------------+-------------+----------------+----------------+----------------------------+----------------+------------+----------------------------------------------------+------------- 1 | 2020-10-28 06:16:54.8044 | | 40ebc53c53f658a80d9a8d155cf64daa23fd075a | 4.1.1 | 64b30706529c110bb3c8301a9be9aed5 | Linux-5.3.0-1034-aws-x86_64-with-debian-buster-sid | sebastian | /home/sebastian/issue-196/src/experiments | t2.large | | triage.experiments.multicore.MultiCoreExperiment | {"cleanup": false, "profile": false, "replace": true, "partial_run": false, "project_path": "s3://ccd-itam/validated_experiments/2_conds", "cleanup_timeout": null, "skip_validation": false, "save_predictions": true, "matrix_storage_class": "triage.component.catwalk.storage.CSVMatrixStore", "features_ignore_cohort": false, "materialize_subquery_fromobjs": true} | {adjustText==0.7.3,aequitas==0.38.0,alembic==1.4.2,argcmdr==0.6.0,argcomplete==1.9.4,boto3==1.14.45,botocore==1.17.56,certifi==2020.6.20,chardet==3.0.4,click==7.1.2,coloredlogs==14.0,cycler==0.10.0,Dickens==1.0.1,docutils==0.15.2,dominate==2.5.2,Flask==0.12.2,Flask-Bootstrap==3.3.7.1,fsspec==0.8.0,graphviz==0.14,html5lib==1.1,httplib2==0.18.1,humanfriendly==8.2,idna==2.10,inflection==0.5.0,itsdangerous==1.1.0,Jinja2==2.11.2,jmespath==0.10.0,joblib==0.16.0,kiwisolver==1.2.0,Mako==1.1.3,markdown2==2.3.5,MarkupSafe==1.1.1,matplotlib==3.2.2,numpy==1.19.0,ohio==0.5.0,pandas==1.0.5,Pebble==4.5.3,Pillow==7.2.0,pip==20.1.1,plumbum==1.6.4,psycopg2==2.8.5,psycopg2-binary==2.8.5,pyparsing==2.4.7,PyPDF2==1.26.0,python-dateutil==2.8.1,python-editor==1.0.4,pytz==2020.1,PyYAML==5.3.1,reportlab==3.5.49,requests==2.24.0,retrying==1.3.3,s3fs==0.4.2,s3transfer==0.3.3,scikit-learn==0.23.1,scipy==1.5.0,seaborn==0.10.1,setuptools==47.1.0,signalled-timeout==1.0.0,six==1.15.0,SQLAlchemy==1.3.18,sqlalchemy-postgres-copy==0.5.0,sqlparse==0.3.1,tabulate==0.8.2,threadpoolctl==2.1.0,triage==4.1.1,urllib3==1.25.10,verboselogs==1.7,visitor==0.1.3,webencodings==0.5.1,Werkzeug==1.0.1,wrapt==1.12.1,xhtml2pdf==0.2.2} | | 0 | 0 | 0 | | 0 | 0 | 0 | 2020-10-28 06:16:54.811704 | started | | 3.7.9 (default, Sep 5 2020, 01:20:01) [GCC 7.5.0] | 4296593 58 | 2020-11-04 06:17:59.678631 | | dea943528749125c242c3aaf6fe15aeaad112ef0 | 4.1.1 | 3dafba558e59bf455bd0d8817213510c | Linux-5.3.0-1034-aws-x86_64-with-debian-buster-sid | sebastian | /home/sebastian/EWS_ITAM/src/experiments | t2.2xlarge | | triage.experiments.multicore.MultiCoreExperiment | {"cleanup": false, "profile": false, "replace": false, "partial_run": false, "project_path": "s3://ccd-itam/validated_experiments/3_conds", "cleanup_timeout": null, "skip_validation": false, "save_predictions": true, "matrix_storage_class": "triage.component.catwalk.storage.CSVMatrixStore", "features_ignore_cohort": false, "materialize_subquery_fromobjs": true} | {adjustText==0.7.3,aequitas==0.38.0,alembic==1.4.2,argcmdr==0.6.0,argcomplete==1.9.4,boto3==1.14.45,botocore==1.17.56,certifi==2020.6.20,chardet==3.0.4,click==7.1.2,coloredlogs==14.0,cycler==0.10.0,Dickens==1.0.1,docutils==0.15.2,dominate==2.5.2,Flask==0.12.2,Flask-Bootstrap==3.3.7.1,fsspec==0.8.0,graphviz==0.14,html5lib==1.1,httplib2==0.18.1,humanfriendly==8.2,idna==2.10,inflection==0.5.0,itsdangerous==1.1.0,Jinja2==2.11.2,jmespath==0.10.0,joblib==0.16.0,kiwisolver==1.2.0,Mako==1.1.3,markdown2==2.3.5,MarkupSafe==1.1.1,matplotlib==3.2.2,numpy==1.19.0,ohio==0.5.0,pandas==1.0.5,Pebble==4.5.3,Pillow==7.2.0,pip==20.1.1,plumbum==1.6.4,psycopg2==2.8.5,psycopg2-binary==2.8.5,pyparsing==2.4.7,PyPDF2==1.26.0,python-dateutil==2.8.1,python-editor==1.0.4,pytz==2020.1,PyYAML==5.3.1,reportlab==3.5.49,requests==2.24.0,retrying==1.3.3,s3fs==0.4.2,s3transfer==0.3.3,scikit-learn==0.23.1,scipy==1.5.0,seaborn==0.10.1,setuptools==47.1.0,signalled-timeout==1.0.0,six==1.15.0,SQLAlchemy==1.3.18,sqlalchemy-postgres-copy==0.5.0,sqlparse==0.3.1,tabulate==0.8.2,threadpoolctl==2.1.0,triage==4.1.1,urllib3==1.25.10,verboselogs==1.7,visitor==0.1.3,webencodings==0.5.1,Werkzeug==1.0.1,wrapt==1.12.1,xgboost==1.2.1,xhtml2pdf==0.2.2} | | 0 | 0 | 0 | | 0 | 0 | 0 | 2020-11-04 06:17:59.685965 | started | | 3.7.9 (default, Sep 5 2020, 01:20:01) [GCC 7.5.0] | 4296593 4 | 2020-10-28 06:29:27.870642 | | 40ebc53c53f658a80d9a8d155cf64daa23fd075a | 4.1.1 | f1da842d6f5d7e2a0a6c5631859dc8f5 | Linux-5.3.0-1034-aws-x86_64-with-debian-buster-sid | sebastian | /home/sebastian/issue-196/src/experiments | t2.medium | | triage.experiments.multicore.MultiCoreExperiment | {"cleanup": false, "profile": false, "replace": false, "partial_run": false, "project_path": "s3://ccd-itam/validated_experiments/2_conds", "cleanup_timeout": null, "skip_validation": false, "save_predictions": true, "matrix_storage_class": "triage.component.catwalk.storage.CSVMatrixStore", "features_ignore_cohort": false, "materialize_subquery_fromobjs": true} | {adjustText==0.7.3,aequitas==0.38.0,alembic==1.4.2,argcmdr==0.6.0,argcomplete==1.9.4,boto3==1.14.45,botocore==1.17.56,certifi==2020.6.20,chardet==3.0.4,click==7.1.2,coloredlogs==14.0,cycler==0.10.0,Dickens==1.0.1,docutils==0.15.2,dominate==2.5.2,Flask==0.12.2,Flask-Bootstrap==3.3.7.1,fsspec==0.8.0,graphviz==0.14,html5lib==1.1,httplib2==0.18.1,humanfriendly==8.2,idna==2.10,inflection==0.5.0,itsdangerous==1.1.0,Jinja2==2.11.2,jmespath==0.10.0,joblib==0.16.0,kiwisolver==1.2.0,Mako==1.1.3,markdown2==2.3.5,MarkupSafe==1.1.1,matplotlib==3.2.2,numpy==1.19.0,ohio==0.5.0,pandas==1.0.5,Pebble==4.5.3,Pillow==7.2.0,pip==20.1.1,plumbum==1.6.4,psycopg2==2.8.5,psycopg2-binary==2.8.5,pyparsing==2.4.7,PyPDF2==1.26.0,python-dateutil==2.8.1,python-editor==1.0.4,pytz==2020.1,PyYAML==5.3.1,reportlab==3.5.49,requests==2.24.0,retrying==1.3.3,s3fs==0.4.2,s3transfer==0.3.3,scikit-learn==0.23.1,scipy==1.5.0,seaborn==0.10.1,setuptools==47.1.0,signalled-timeout==1.0.0,six==1.15.0,SQLAlchemy==1.3.18,sqlalchemy-postgres-copy==0.5.0,sqlparse==0.3.1,tabulate==0.8.2,threadpoolctl==2.1.0,triage==4.1.1,urllib3==1.25.10,verboselogs==1.7,visitor==0.1.3,webencodings==0.5.1,Werkzeug==1.0.1,wrapt==1.12.1,xhtml2pdf==0.2.2} | | 0 | 0 | 0 | | 0 | 0 | 0 | 2020-10-28 06:29:27.880262 | started | | 3.7.9 (default, Sep 5 2020, 01:20:01) [GCC 7.5.0] | 4296593 ``` ### 5.2 Esquema ```train_results``` * `train_results.feature_importance` - Contiene los valores de _feature_importance_ dados por ```sklearn``` para cada modelo entrenado. >Depende de si el modelo tiene la viabilidad de calcular _FI_, como por ejemplo, árboles de decisión. ```bash= itam=> select * from train_results.feature_importances limit 10; model_id | feature | feature_importance | rank_abs | rank_pct ----------+------------------------------------------------------------+--------------------+----------+---------- 1 | prepa_entity_id_all_pase_8_0_max | 0.0 | 2 | 1 1 | prepa_entity_id_all_pase_8_1_max | 0.0 | 2 | 1 1 | prepa_entity_id_all_pase_8__NULL_max | 0.0 | 2 | 1 1 | examen_admision_entity_id_all_total_max | 1.0 | 1 | 0.5 1 | examen_admision_entity_id_all_total_imp | 0.0 | 2 | 1 1 | reprobar_examen_redaccion_entity_id_all_reprobar_0_max | 0.0 | 2 | 1 1 | reprobar_examen_redaccion_entity_id_all_reprobar_1_max | 0.0 | 2 | 1 1 | reprobar_examen_redaccion_entity_id_all_reprobar__NULL_max | 0.0 | 2 | 1 1 | reprobar_examen_intro_mate_entity_id_all_reprobar_0_max | 0.0 | 2 | 1 1 | reprobar_examen_intro_mate_entity_id_all_reprobar_1_max | 0.0 | 2 | 1 ``` * `train_results.predictions` ```bash= itam=> select * from train_results.predictions limit 5; model_id | entity_id | as_of_date | score | label_value | rank_abs_no_ties | rank_abs_with_ties | rank_pct_no_ties | rank_pct_with_ties | matrix_uuid | test_label_timespan ----------+-----------+---------------------+---------+-------------+------------------+--------------------+------------------+--------------------+----------------------------------+--------------------- 1 | 3394 | 2003-08-15 00:00:00 | 0.13203 | 0 | 1 | 1 | 0.00023 | 0.50000 | 1f7adb221409ea2c308d3273d02c7e50 | 2 years 1 | 26418 | 2003-08-15 00:00:00 | 0.13203 | 0 | 2 | 1 | 0.00045 | 0.50000 | 1f7adb221409ea2c308d3273d02c7e50 | 2 years 1 | 26507 | 2003-08-15 00:00:00 | 0.13203 | 0 | 3 | 1 | 0.00068 | 0.50000 | 1f7adb221409ea2c308d3273d02c7e50 | 2 years 1 | 28960 | 2003-08-15 00:00:00 | 0.13203 | 0 | 4 | 1 | 0.00091 | 0.50000 | 1f7adb221409ea2c308d3273d02c7e50 | 2 years 1 | 31245 | 2003-08-15 00:00:00 | 0.13203 | 0 | 5 | 1 | 0.00114 | 0.50000 | 1f7adb221409ea2c308d3273d02c7e50 | 2 years ``` ### 5.3 Esquema ```test_results``` * ```test_results.evaluations``` - Scores asignados por los modelos entrenados, para las métricas definidas. ```bash= itam=> select * from test_results.evaluations limit 5; model_id | evaluation_start_time | evaluation_end_time | as_of_date_frequency | metric | parameter | num_labeled_examples | num_labeled_above_threshold | num_positive_labels | sort_seed | matrix_uuid | subset_hash | best_value | worst_value | stochastic_value | num_sort_trials | standard_deviation ----------+-----------------------+---------------------+----------------------+---------+-----------+----------------------+-----------------------------+---------------------+-----------+----------------------------------+-------------+---------------------+---------------------+----------------------+-----------------+---------------------- 1 | 2005-08-15 00:00:00 | 2005-08-15 00:00:00 | 1 year | recall@ | 100_abs | 4268 | 100 | 474 | | 9a1cb4acc106f07cd7812d2242a4cd48 | | 0.2109704641350211 | 0.0 | 0.0339662447257384 | 30 | 0.008922381390340517 2 | 2005-08-15 00:00:00 | 2005-08-15 00:00:00 | 1 year | recall@ | 100_abs | 4268 | 100 | 474 | | 9a1cb4acc106f07cd7812d2242a4cd48 | | 0.04219409282700422 | 0.04219409282700422 | 0.04219409282700422 | 0 | 0 4 | 2005-08-15 00:00:00 | 2005-08-15 00:00:00 | 1 year | recall@ | 100_abs | 4268 | 100 | 474 | | 9a1cb4acc106f07cd7812d2242a4cd48 | | 0.04008438818565401 | 0.04008438818565401 | 0.04008438818565401 | 0 | 0 3 | 2005-08-15 00:00:00 | 2005-08-15 00:00:00 | 1 year | recall@ | 100_abs | 4268 | 100 | 474 | | 9a1cb4acc106f07cd7812d2242a4cd48 | | 0.2109704641350211 | 0.0 | 0.029957805907172997 | 30 | 0.006375021440950903 5 | 2005-08-15 00:00:00 | 2005-08-15 00:00:00 | 1 year | recall@ | 100_abs | 4268 | 100 | 474 | | 9a1cb4acc106f07cd7812d2242a4cd48 | | 0.06751054852320675 | 0.0189873417721519 | 0.030520393811533052 | 30 | 0.003867681616001847 ``` ### 5.4 Ejemplo de consulta para selección de modelos Si nos interesara elegir los grupos de modelos que, en promedio, tuvieron el mejor `recall@100` para todos los periodos, podríamos ejecutar la siguiente consulta: ```sql= select model_group_id, mg.model_type, mg.hyperparameters, --count (*) as num_registros, avg(stochastic_value) as avg_recall from triage_metadata.models as mo inner join triage_metadata.model_groups as mg using(model_group_id) inner join test_results.evaluations as ev using(model_id) where mo.model_comment = 'baseline_2c' and ev.metric || ev.parameter = 'recall@100_abs' group by model_group_id, mg.model_type, mg.hyperparameters order by avg_recall desc limit 4 ``` La consulta anterior nos van a devolver un listado con 4 modelos de la siguiente forma: ```bash= model_group_id | model_type | hyperparameters | avg_recall ----------------+--------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------+------------------------ 2 | triage.component.catwalk.estimators.classifiers.ScaledLogisticRegression | {"C": 1.0, "penalty": "l2"} | 0.03826952739091737177 4 | triage.component.catwalk.estimators.classifiers.ScaledLogisticRegression | {"C": 0.01, "penalty": "l2"} | 0.03686464358122507315 6 | sklearn.tree.DecisionTreeClassifier | {"criterion": "entropy", "max_depth": null, "min_samples_leaf": 0.1, "min_samples_split": 25} | 0.03685125825374580485 5 | sklearn.tree.DecisionTreeClassifier | {"criterion": "entropy", "max_depth": null, "min_samples_leaf": 0.025, "min_samples_split": 25} | 0.03465288001405915238 ``` Ahora supongamos una búsqueda que amplía la anterior. Supongamos que queremos saber los modelos con mejor rendimiento promedio para `recall@100` para un periodo seleccionado. En este caso, podriamos hacer el siguiente _query_: ```sql= select model_group_id, mg.model_type,mg.hyperparameters, avg(stochastic_value) as avg_recall from triage_metadata.models as mo inner join triage_metadata.model_groups as mg using(model_group_id) inner join test_results.evaluations as ev using(model_id) where mo.model_comment = 'baseline_2c' and ev.metric||ev.parameter = 'recall@100_abs' and evaluation_start_time >'2010-01-01' and evaluation_start_time <'2020-01-01' group by model_group_id,mg.model_type,mg.hyperparameters order by avg_recall desc limit 4 ``` La consulta anterior nos van a devolver un listado con 4 modelos de la siguiente forma: ```bash= model_group_id | model_type | hyperparameters | avg_recall ----------------+--------------------------------------------------------------------------+-------------------------------------------------------------------------------------------------+------------------------ 2 | triage.component.catwalk.estimators.classifiers.ScaledLogisticRegression | {"C": 1.0, "penalty": "l2"} | 0.03854168380621023688 6 | sklearn.tree.DecisionTreeClassifier | {"criterion": "entropy", "max_depth": null, "min_samples_leaf": 0.1, "min_samples_split": 25} | 0.03813129850933780550 5 | sklearn.tree.DecisionTreeClassifier | {"criterion": "entropy", "max_depth": null, "min_samples_leaf": 0.025, "min_samples_split": 25} | 0.03705653565779174175 4 | triage.component.catwalk.estimators.classifiers.ScaledLogisticRegression | {"C": 0.01, "penalty": "l2"} | 0.03682861827958306638 ``` Como podemos observar, aunque el _Top 4_ sigue teniendo los mismos `model_group_id` si variamos los periodos de estudio de la métrica promedio, varía el orden de desempeño. ### 5.5 Revisión de estabilidad en el tiempo Como vimos en los dos ejemplos anteriores, podemos realizar _queries_ que nos permitan relacionar las tablas con los resultados y los metadatos de los experimentos de acuerdo a nuestro interés. En las consultas que se vieron solo obteníamos un listado de los modelos con mejor rendimiento en el tiempo. Si quisieramos evaluar a detalle cuál fue su rendimiento de forma dinámica, podríamos parametrizar las consultas. La factibilidad de poder realizar consultas a la base de los resultados nos permite entender y visualizar nuestros resultados de distintas maneras, y en distintos lenguajes de programación. El ejemplo que presentamos a continuacion lo implementamos en lenguaje `R` de forma secuencial y buscaba evaluar la estabilidad de los resultados de un 4 grupos de modelos en el tiempo para el experimento 'baseline_2c'. 0. Conexión a la base de datos ```R= # Conexión con <- dbConnect(RPostgres::Postgres(), dbname = credentials$dbname, host = credentials$host, port = credentials$port, user = credentials$user, password = credentials$password ) # Ejecución de queries query_db <- function(query){ rs <- dbSendQuery(con, statement=query) dbFetch(rs, n= -1) } ``` 1. Identificar los modelos con mejor rendimiento promedio. Esta función parametriza el _query_ que vimos anteriormente. ```R= table_top10_at_k_between <- function(mat, metric, k, model_comment, date_1, date_2, top){ " Función para crear tabla con los componentes de los top mejores modelos entre date_1 y date_2 * Inputs - mat(chr) : \"train\" or \"test\" - metric: métrica para evaluar modelos - k(int): Top k - model_comment (chr): model comment de triage - date_1(chr): Fecha de inicio de la evaluación. p.ej '01-01-2000' - date_2(chr): Fecha de fin de la evaluación. p.ej '01-01-2020' - top (int): Top de modelos a evaluar " # se define query a la base de datos query_models <- sprintf(" select model_group_id, mg.model_type,mg.hyperparameters, avg(stochastic_value) as avg_recall from triage_metadata.models as mo inner join triage_metadata.model_groups as mg using(model_group_id) inner join %s_results.evaluations as ev using(model_id) where mo.model_comment = '%s' and ev.metric||ev.parameter = '%s@%d_abs' and evaluation_start_time > '%s' and evaluation_start_time < '%s' group by model_group_id, mg.model_type, mg.hyperparameters order by avg_recall desc limit %d ;", mat, model_comment, metric, k, date_1, date_2, top) tbl_models <- query_db(query_models) # ajusta información tbl_models <- tbl_models %>% select(model_group_id) } ``` Ejemplo: ```R= > top4avg <- table_top10_at_k_between(mat='test', metric='recall', k=100, model_comment='baseline_2c', date_1='01-8-2010', date_2='01-8-2019', top=4) > top4avg model_group_id model_type hyperparameters avg_recall 2 triage.component.catwalk.estimators.classifiers.ScaledLogisticRegression {"C": 1.0, "penalty": "l2"} 0.03854168 6 sklearn.tree.DecisionTreeClassifier {"criterion": "entropy", "max_depth": null, "min_samples_leaf": 0.1, "min_samples_split": 25} 0.03813130 5 sklearn.tree.DecisionTreeClassifier {"criterion": "entropy", "max_depth": null, "min_samples_leaf": 0.025, "min_samples_split": 25} 0.03705654 4 triage.component.catwalk.estimators.classifiers.ScaledLogisticRegression {"C": 0.01, "penalty": "l2"} 0.03682862 ``` 2. Identificar las métricas de evaluación para cada punto del tiempo, y además la lista de features utilizados. ```R= table_top10_at_k <- function(mat, metric, k, model_comment, date_1, date_2, top){ " Función para crear tabla con los componentes de los 10 mejores modelos. * Inputs - mat(chr) : \"train\" or \"test\" - metric: métrica para evaluar modelos - k(int): Top k - model_comment (chr): model comment de triage - date_1(chr): Fecha de inicio de la evaluación. p.ej '01-01-2000' - date_2(chr): Fecha de fin de la evaluación. p.ej '01-01-2020' - top (int): Top de modelos a evaluar * Outputs: - model_group_id - train_end_time - best_value - worst_valie - stochatic value - model_type - hyperparameters - feature_list " # se define query a la base de datos query_models <- sprintf("with best as( select model_comment, model_group_id, train_end_time, best_value, worst_value, stochastic_value, model_type, hyperparameters from %s_results.evaluations left join triage_metadata.models using (model_id) where metric = '%s@' and parameter = '%d_abs' and model_comment = '%s' and model_group_id in (%s) and evaluation_start_time > '%s' and evaluation_start_time < '%s') select * from best left join triage_metadata.model_groups using (model_group_id) order by model_group_id, stochastic_value desc ;", mat, metric, k, model_comment, top, date_1, date_2) tbl_models <- query_db(query_models) return(tbl_models) } ``` Ejemplo: ```R= > metricsTop4 <- table_top10_at_k(mat='test', metric='recall', k=100, model_comment='baseline_2c', date_1='01-8-2010', date_2='01-8-2019', top=4) model_group_id train_end_time best_value worst_value stochastic_value hyperparameters feature_list model 2 2011-08-15 0.04662005 0.04662005 0.04662005 {"C": 1.0, "penalty": "l2"} {examen_admision_entity_id_all_total_imp,examen_admision_entity_id_all_total_max,prepa_entity_id_all_pase_8_0_max,prepa_entity_id_all_pase_8_1_max,prepa_entity_id_all_pase_8__NULL_max,reprobar_examen_intro_mate_entity_id_all_reprobar_0_max,reprobar_examen_intro_mate_entity_id_all_reprobar_1_max,reprobar_examen_intro_mate_entity_id_all_reprobar__NULL_max,reprobar_examen_redaccion_entity_id_all_reprobar_0_max,reprobar_examen_redaccion_entity_id_all_reprobar_1_max,reprobar_examen_redaccion_entity_id_all_reprobar__NULL_max} Scaled Logistic Regression 2 2017-08-15 0.04578755 0.04395604 0.04499389 {"C": 1.0, "penalty": "l2"} {examen_admision_entity_id_all_total_imp,examen_admision_entity_id_all_total_max,prepa_entity_id_all_pase_8_0_max,prepa_entity_id_all_pase_8_1_max,prepa_entity_id_all_pase_8__NULL_max,reprobar_examen_intro_mate_entity_id_all_reprobar_0_max,reprobar_examen_intro_mate_entity_id_all_reprobar_1_max,reprobar_examen_intro_mate_entity_id_all_reprobar__NULL_max,reprobar_examen_redaccion_entity_id_all_reprobar_0_max,reprobar_examen_redaccion_entity_id_all_reprobar_1_max,reprobar_examen_redaccion_entity_id_all_reprobar__NULL_max} Scaled Logistic Regression 2 2016-08-15 0.04268293 0.04065041 0.04126016 {"C": 1.0, "penalty": "l2"} {examen_admision_entity_id_all_total_imp,examen_admision_entity_id_all_total_max,prepa_entity_id_all_pase_8_0_max,prepa_entity_id_all_pase_8_1_max,prepa_entity_id_all_pase_8__NULL_max,reprobar_examen_intro_mate_entity_id_all_reprobar_0_max,reprobar_examen_intro_mate_entity_id_all_reprobar_1_max,reprobar_examen_intro_mate_entity_id_all_reprobar__NULL_max,reprobar_examen_redaccion_entity_id_all_reprobar_0_max,reprobar_examen_redaccion_entity_id_all_reprobar_1_max,reprobar_examen_redaccion_entity_id_all_reprobar__NULL_max} Scaled Logistic Regression 2 2010-08-15 0.03902439 0.03902439 0.03902439 {"C": 1.0, "penalty": "l2"} {examen_admision_entity_id_all_total_imp,examen_admision_entity_id_all_total_max,prepa_entity_id_all_pase_8_0_max,prepa_entity_id_all_pase_8_1_max,prepa_entity_id_all_pase_8__NULL_max,reprobar_examen_intro_mate_entity_id_all_reprobar_0_max,reprobar_examen_intro_mate_entity_id_all_reprobar_1_max,reprobar_examen_intro_mate_entity_id_all_reprobar__NULL_max,reprobar_examen_redaccion_entity_id_all_reprobar_0_max,reprobar_examen_redaccion_entity_id_all_reprobar_1_max,reprobar_examen_redaccion_entity_id_all_reprobar__NULL_max} Scaled Logistic Regression 2 2012-08-15 0.03846154 0.03846154 0.03846154 {"C": 1.0, "penalty": "l2"} {examen_admision_entity_id_all_total_imp,examen_admision_entity_id_all_total_max,prepa_entity_id_all_pase_8_0_max,prepa_entity_id_all_pase_8_1_max,prepa_entity_id_all_pase_8__NULL_max,reprobar_examen_intro_mate_entity_id_all_reprobar_0_max,reprobar_examen_intro_mate_entity_id_all_reprobar_1_max,reprobar_examen_intro_mate_entity_id_all_reprobar__NULL_max,reprobar_examen_redaccion_entity_id_all_reprobar_0_max,reprobar_examen_redaccion_entity_id_all_reprobar_1_max,reprobar_examen_redaccion_entity_id_all_reprobar__NULL_max} Scaled Logistic Regression 2 2014-08-15 0.03846154 0.03846154 0.03846154 {"C": 1.0, "penalty": "l2"} {examen_admision_entity_id_all_total_imp,examen_admision_entity_id_all_total_max,prepa_entity_id_all_pase_8_0_max,prepa_entity_id_all_pase_8_1_max,prepa_entity_id_all_pase_8__NULL_max,reprobar_examen_intro_mate_entity_id_all_reprobar_0_max,reprobar_examen_intro_mate_entity_id_all_reprobar_1_max,reprobar_examen_intro_mate_entity_id_all_reprobar__NULL_max,reprobar_examen_redaccion_entity_id_all_reprobar_0_max,reprobar_examen_redaccion_entity_id_all_reprobar_1_max,reprobar_examen_redaccion_entity_id_all_reprobar__NULL_max} Scaled Logistic Regression ``` 3. Graficar el rendimiento en el tiempo Utilizando la tabla anterior podemos graficar el rendimiento en el tiempo de los modelos con mejor ḿetricas promedio en los periodos evaluados: ![](https://i.imgur.com/Gdaagdm.png) Además del promedio también se pueden añadir otro tipo de comparaciones, dependiendo del problema. ![](https://i.imgur.com/QndZnRj.png) ## 6. _Postmodeling_ y _Crosstabs_ ### 6.1 Análisis _Postmodeling_ #### 6.1.1 Preliminares Una vez hechos los ejercicios de modelado podemos comparar modelos de distintas formas, bien sea modelos del mismo grupo, o modelos de diferentes grupos, dependiendo de nuestro propósito. `triage` incorpora una serie de rutinas útiles para este propósito, en este ejemplo solo introduciremos algunas, y nos basaremos en el [ejemplo](https://github.com/dssg/triage/blob/master/src/triage/component/postmodeling/contrast/postmodeling_tutorial.ipynb) oficial de la librería. - `postmodeling_config.yaml` Primero, debemos crear un archivo con la condifuración de nuestro análisis con la extensión `.yaml`. Este archivo, debe tener los siguienes campos: - `project_path`: ruta a las matrices del experimento - `model_group_id`: lista de modelos a comparar - `metric`: Selected metric string (i.e. precision@,recall@) - `thresholds`: Selected threshold list (i.e. rank_abs[50, 100]) - `baseline_query`:A SQL query that returns evaluation metrics for the baseline models - `n_features_plots`: Number of features to plot importances (int) Para este proyecto, tenemos el siguiente ejemplo: ```yaml= ## postmodeling_config.yaml # Postmodeling Configuration File project_path: 's3://ccd-itam/experiments/' model_group_id: # List of model_id's [optional if a audition_output_path is given] - 390 - 433 - 409 - 386 - 438 metric: recall@ thresholds: rank_abs: [100] baseline_query: | # SQL query for defining a baseline for comparison in plots. It needs a metric and parameter select g.model_group_id, m.model_id, extract('year' from m.evaluation_end_time) as as_of_date_year, m.metric, m.parameter, m.stochastic_value, m.num_labeled_examples, m.num_labeled_above_threshold, m.num_positive_labels from test_results.evaluations m left join triage_metadata.models g using(model_id) where g.model_group_id = 56 and metric = 'recall@' and parameter = '100_abs' n_features_plots: 10 # Number of features for importances figsize: [12, 8] # Default size for plots fontsize: 14 # Default fontsize for plots ``` Vamos a utilizar las siguientes clases y funciones para ete análisis: ```python= from triage.component.postmodeling.contrast.utils.aux_funcs import create_pgconn, get_models_ids from triage.component.postmodeling.contrast.parameters import PostmodelParameters from triage.component.postmodeling.contrast.model_evaluator import ModelEvaluator from triage.component.postmodeling.contrast.model_group_evaluator import ModelGroupEvaluator ``` Una vez realizado lo anterior, en `Python` podemos crear la clase con los paŕametros del análisis de la siguiente forma: ```python= > params = PostmodelParameters('postmodeling_config.yaml') > params.__dict__ {'project_path': 's3://ccd-itam/experiments/', 'model_group_id': [390, 433, 409, 386, 438], 'thresholds': {'rank_abs': [100]}, 'baseline_query': "select g.model_group_id,\n m.model_id,\n extract('year' from m.evaluation_end_time) as as_of_date_year,\n m.metric,\n m.parameter,\n m.stochastic_value,\n m.num_labeled_examples,\n m.num_labeled_above_threshold,\n m.num_positive_labels\nfrom test_results.evaluations m\nleft join triage_metadata.models g\nusing(model_id)\nwhere g.model_group_id = 56\n and metric = 'recall@'\n and parameter = '100_abs'\n", 'max_depth_error_tree': 5, 'n_features_plots': 10, 'figsize': (12, 8), 'fontsize': 14} ``` - Credenciales de la base de datos Igual que en ejemplos anteriores, dado que vamos a consutlar los resultados de los experimentos que hemos corrido y est́an en la base de datos, debemos crear la conexión en `Python`: Podemos utilizar el archivo `database.yaml` que habíamos creado anteriormente para incorporar la infromación de las credenciales: ```yaml= # database.yaml db: db_credentials: host: database-amai.cfdemdotqpti.us-east-1.rds.amazonaws.com database: itam user: usuario password: unusuario_pass port: 5432 ``` Y ahora creamos la conexión en `Python`: ``` engine = create_pgconn('db_credentials.yaml') engine ``` #### 6.1.2 Evaluación individual de modelos Podemos crear una lista de todos los modelos que queremos comparar para evaluar su desempeño en los diferentes puntos del tiempo utilizando la clase `ModelEvaluator`. ```python= # List individual models list_tuple_models = get_models_ids(params.model_group_id, engine) l_t = [ModelEvaluator(i.model_group_id, i.model_id, engine) for i in list_tuple_models] ``` En el siguiente ejemplo evaluamos el desempeño de dos modelos, utilizando el ḿetodo: ```python= lt[i].plot_precision_recall_n() ``` > Nota: realizamos algunas modificaciones al ḿetodo para graficar la linea vertical en el `k` de inteŕes. ![](https://i.imgur.com/7p1EaCx.png) ![](https://i.imgur.com/FHZbdjb.png) Aunque en este ejercicio no deseamos ver el rendimiento totalizado de los modelos (pues solo podremos intervenir sobre un número reducido de personas), podemos también graficar las curva ROC, o la curva RecallvsFPR con los siguientes métodos, respectivamente: ```python= lt[i].plot_ROC() lt[i].plot_recall_fpr_n() ``` - Feature importance En modelos como ́arboles de decisión o bosques aleatorios nos podría interesar qué variables son las ḿas relevantes para realizar clasificacíon. Para este tipo de modelos existe el ḿetodo: ```python= lt[i].plot_feature_importances() ``` ![](https://i.imgur.com/PxZZ6S9.png) > Adicionales: También se puede analizar esta característica con el método: > `lt[i].plot_feature_importances_std_err()` - Distribución de los _features_ Es posible analizar como es la distribución de los features utilizados por cada modelo por etiqueta o conjuntamente,por medio del método: ```python= lt[i].plot_feature_distribution() ``` - Distribución de los _scores_ asignados por el modelo por etiqueta o conjuntamente: ```python= >lt[i].plot_score_distribution() >lt[i].plot_score_label_distributions() ``` #### 6.1.3 Evaluación conjunta de modelos Como vimos en la sección anterior, resulta de mucha utilidad comparar conjuntamente los modelos que entrenamos. La clase `ModelGroupEvaluator` nos permite hacer este tipo de análisis. ```python= # Model group object (useful to compare across model_groups and models in time) audited_models_class = ModelGroupEvaluator(tuple(params.model_group_id), engine) ``` >Nota: Los métodos que se mencionan a continuación podemos utilizar el argumento `model_subset=[...]` si queremos restringir el análisis a un subconjunto de modelos específico. - Analisis de estabilidad ![](https://i.imgur.com/AwKC5X2.png) - Similitud de predicciones Una característica deseable es que exista alto _overlap_ en los individuos que identifican nuestros mejores modelos. Podemos analizar esto de distintas formas. Jaccard Consideremos la similitud de Jaccard: $$ \mathcal{J}(A,B)=\frac{\mid A\cap B\mid}{\mid A\cup B\mid} $$ Si lo consideramos para el grupo de modelos analizados, en agosto de 2017, tenemos: ![](https://i.imgur.com/oC8Swpn.png) En `triage` podemos generar esta matriz para todos los periodos donde se evaluan los modelos seleccionados por medio del ḿetodo: ```python= audited_models_class.plot_jaccard(temporal_comparison=True) ``` Comparación avanzada También podemos evaluar la similitud de las predicciones y su ordenamiento por percentiles utilizando el método `plot_preds_comparison`. ```python= audited_models_class.plot_preds_comparison(temporal_comparison=True) ``` ### 6.2 Crosstabs El módulo de Postmodeling de Triage también nos permite comparar los promedios poblacionales de las entidades consideradas en riesgo con los de las consideradas como de bajo riesgo. Estos datos se almacenarán en la tabla de _crosstabs_ una vez se ejecuten. Para ello, se requiere un archivo de configuración y `postmodeling_crosstabs.yaml` que contiene: * `output`: Define el esquema y tabla donde se almacenarán los resultados de _crosstabs_ * `thresholds`: umbral que define las predicciones positivas * `entity_id_list`: (opcional) listado de _entity_ids_ a considerar en el análisis de _crosstabs_ * `models_list_query`: query SQL para obtener los `model_ids` * `as_of_dates_query`: query SQL para obtener `as_of_dates` * `models_dates_join_query`: no cambiar el query a menos que sea estrictamente necesario. Valida los pares de (`model_id`, `as_of_date`) en la tabla de predicciones * `features_query`: debe hacer join entre `models_dates_join_query` con una o más tablas de features, usando `as_of_date` * `predictions_query`: debe devolver `model_id`, `as_of_date`, `entity_id`, `score` , `label_value`, `rank_abs` and `rank_pct`. Debe hacer join con `models_dates_join_query` usando `model_id` y `as_of_date`. Un par de ejemplos de dicho archivo se puede encontrar [aquí](https://github.com/dssg/triage/blob/master/example/config/postmodeling_config.yaml) o en el apéndice de este documento. Una vez listo el archivo de configuración, podremos ejecutarlo mediante la siguiente instrucción ```bash= triage crosstabs postmodeling_crosstabs.yaml ``` Nótese que también se requiere el archivo `database.yaml` para tener acceso a la base de datos. La tabla de crosstabs se verá así: ```bash= itam=> select * from test_results.crosstabs_best_models limit 6; model_id | as_of_date | metric | feature_column | value | threshold_unit | threshold_value ----------+---------------------+--------------------------+-----------------------------------------+------------------+----------------+----------------- 139 | 2005-08-15 00:00:00 | count_predicted_positive | examen_admision_entity_id_all_total_max | 100 | abs | 100 139 | 2005-08-15 00:00:00 | count_predicted_negative | examen_admision_entity_id_all_total_max | 4472 | abs | 100 139 | 2005-08-15 00:00:00 | mean_predicted_positive | examen_admision_entity_id_all_total_max | 1151.83 | abs | 100 139 | 2005-08-15 00:00:00 | mean_predicted_negative | examen_admision_entity_id_all_total_max | 1363.37947227191 | abs | 100 139 | 2005-08-15 00:00:00 | std_predicted_positive | examen_admision_entity_id_all_total_max | 58.0513626633407 | abs | 100 139 | 2005-08-15 00:00:00 | std_predicted_negative | examen_admision_entity_id_all_total_max | 68.3202541830632 | abs | 100 ``` Y podemos ver las distintas métricas incluidas: ```bash= itam=> select distinct metric from test_results.crosstabs_best_models; metric -------------------------------------------------- count_predicted_positive ratio_predicted_positive_over_predicted_negative mean_predicted_positive std_predicted_negative count_predicted_negative ttest_p std_predicted_positive ttest_T mean_predicted_negative ``` podemos centrarnos en el análisis de las variables significativas apoyándonos en queries como el siguiente: ```bash= with significant_features as ( select feature_column, as_of_date, threshold_unit from test_results.crosstabs_best_models where metric = 'ttest_p' and value < 0.05 and as_of_date = '2017-08-15 00:00:00.000' ) select distinct model_id, as_of_date::date as as_of_date, format('%s %s', threshold_value, t1.threshold_unit) as threshold, feature_column, value as "ratio PP / PN" from test_results.crosstabs_best_models as t1 inner join significant_features as t2 using(feature_column, as_of_date) where metric = 'ratio_predicted_positive_over_predicted_negative' and t1.threshold_unit = 'abs' and model_id = '4506' order by value desc ``` El resultado se verá de la siguiente forma: ```bash= model_id | as_of_date | threshold | feature_column | ratio PP / PN ----------+------------+-----------+--------------------------------------------+------------------ 4506 | 2017-08-15 | 100 abs | total_reprobadas_entity_id_1y_total_count | 6.04233236151603 4506 | 2017-08-15 | 100 abs | total_reprobadas_entity_id_2y_total_count | 5.55221908022027 4506 | 2017-08-15 | 100 abs | total_reprobadas_entity_id_all_total_count | 4.51673005153723 ``` Esto nos dice, por ejemplo, que para el modelo con _id_ 4506, la media de materias reprobadas en los predichos positivos es 6 veces mayor que en los predichos negativos. En particular, queremos comparar de que forma est́an clasificando los distintos modelos que estamos evaluando. Por ejemplo, a continuación comparamos a cuatro modelos de grupos distintos para agosto de 2017. ![](https://i.imgur.com/ggT7rzh.png) ### 6.3 Aequitas Para terminar, queremos analizar los modelos seleccionados respecto a _bias_ y _fairness_. para ello, podemos utilizar la [herramienta de línea de comandos](https://dssg.github.io/aequitas/CLI.html) de [Aequitas](http://www.datasciencepublicpolicy.org/projects/aequitas/). Una vez más, usaremos un archivo de configuración `config_aequitas.yaml` como el que se muestra [aquí](https://github.com/dssg/aequitas/blob/master/src/aequitas_cli/configs_database_example.yaml) o en el apéndice de este documento. En él se definen, entre otras cosas, el esquema y la tabla que almacenarán los resultados, los grupos de referencia y los atributos a evaluar. Cabe mencionar que estos últimos no necesitan ser alguna de las variables utilizadas en el modelo. Posteriormente ejecutaremos: ```bash= aequitas-report --config config_aequitas.yaml --create-tables ``` Esto creará una tabla con las siguientes columnas: * model_id * attribute_name * score_threshold * k * attribute_value * tpr * tnr * for * fdr * fpr * fnr * npv * precision * pp * pn * ppr * pprev * fp * fn * tn * tp * group_label_pos * group_label_neg * group_size * total_entities * prev * ppr_disparity * pprev_disparity * precision_disparity * fdr_disparity * for_disparity * fpr_disparity * fnr_disparity * tpr_disparity * tnr_disparity * npv_disparity * ppr_ref_group_value * pprev_ref_group_value * precision_ref_group_value * fdr_ref_group_value * for_ref_group_value * fpr_ref_group_value * fnr_ref_group_value * tpr_ref_group_value * tnr_ref_group_value * npv_ref_group_value * FOR Parity * FNR Parity * TypeII Parity * Supervised Fairness * group_size_pct Una vez definidas las medidas de _bias_ que nos interesan, observaremos las medidas de disparidad correspondientes, teniendo en cuenta que diremos que hay paridad dentro de un grupo si $(1-\tau)\leq MedidaDisparidad_{grupo} \leq \frac{1}{(1-\tau)}$ En nuestro caso, nos interesaron _False Omission Rate (FOR)_ y _False Negative Rate (FNR)_, e hicimos el análisis respecto a género y ser foráneo: ```bash= itam=> select '4506' as model_id,attribute_name ,attribute_value, for_disparity, "FOR Parity", fnr_disparity, "FNR Parity" itam-> from test_results.aequitas_model_4506_predefined amp; model_id | attribute_name | attribute_value | for_disparity | FOR Parity | fnr_disparity | FNR Parity ----------+----------------+-----------------+-------------------+------------+-------------------+------------ 4506 | genero | f | 0.912681981004962 | t | 1.02721088435374 | t 4506 | genero | m | 1 | t | 1 | t 4506 | foraneo_string | n | 1 | t | 1 | t 4506 | foraneo_string | y | 1.28685839848994 | f | 0.988883358221337 | t ``` # Apéndice ## Experimento (Config file completo) ```yaml ## Información del experimento config_version: 'v7' random_seed: 6174 model_comment: 'primera_prueba' user_metadata: label_definition: 'perderan_materias' experiment_type: 'baseline' file_name: 'reprobados.yaml' description: | Primer experimento con triage - modelos básicos purpose: 'baseline' org: 'ITAM' team: 'Centro de Ciencia de Datos' author: 'Paola-Elizabeth-Sebastian' etl_date: '27-10-2020' ## Timechop temporal_config: feature_start_time: '1990-08-01' feature_end_time: '2020-08-01' label_start_time: '2003-08-15' label_end_time: '2020-08-15' label_timespans: ['1y'] model_update_frequency: '1y' training_as_of_date_frequencies: '1y' max_training_histories: '10y' test_durations: '1y' test_as_of_date_frequencies: '1y' ## Cohorte cohort_config: query: | with semestres as( select semestre_codigo from semantic.semestres where nivel = 'licenciatura' and tipo in ('otoño', 'primavera') and fecha_inicio >= (timestamp '{as_of_date}'- interval '3 weeks')::date and fecha_fin <= (timestamp '{as_of_date}'+ interval '6 months')::date ), reprobados as( select alumno from semantic.eventos_alumnos as current_semester_students where fecha < '{as_of_date}' and tipo = 'reprobar curso' ) -- alumnos al inicio del semestre select distinct alumno::int as entity_id from semantic.eventos_alumnos as current_semester_students inner join semestres on current_semester_students.atributos->>'semestre_codigo' = semestres.semestre_codigo where current_semester_students.tipo = 'dar de alta curso' and alumno not in (select alumno from reprobados) name: 'alumnos_sin_reprobadas' ## Etiquetas label_config: query: | select alumno::int as entity_id, '{as_of_date}'::date as as_of_date, bool_or(tipo = 'reprobar curso')::int as outcome from semantic.eventos_alumnos where fecha >= ('{as_of_date}'::timestamp + interval '12 months')::date and fecha < ('{as_of_date}'::timestamp + interval '{label_timespan}')::date group by alumno name: 'reprueban' ## Features feature_aggregations: ### Proceso de admisión en el ITAM - prefix: 'examen_admision' from_obj: | (select alumno::int as entity_id, fecha, (atributos->>'calificacion')::int as calificacion from semantic.eventos_alumnos where tipo = 'presentar examen administrativo' and atributos->>'examen'= 'paat') as examen knowledge_date_column: 'fecha' aggregates_imputation: count: type: 'zero_noflag' max: type: 'constant' value: 1375 aggregates: - quantity: total: "calificacion" metrics: - 'max' intervals: ['all'] groups: - 'entity_id' ### sociodemográficas # género - prefix: 'genero' from_obj: | (with primer_semestre as( select alumno, fecha from semantic.eventos_alumnos where tipo = 'terminar semestre' and atributos->>'semestre_nominal' = '1' ) select alumno::int as entity_id, genero, fecha from semantic.alumnos left join primer_semestre using(alumno) ) as generos knowledge_date_column: 'fecha' categoricals_imputation: all: type: 'mean' categoricals: - # gender column: genero choice_query: 'select distinct genero from semantic.alumnos' metrics: - 'max' intervals: ['all'] groups: - 'entity_id' # Agrupación de features a probar feature_group_definition: prefix: - 'examen_admision' - 'genero' feature_group_strategies: ['all'] ## Métricas a optimizar scoring: testing_metric_groups: - metrics: [recall@, precision@] thresholds: percentiles: [50, 100] ## Grid de modelos grid_config: 'sklearn.tree.DecisionTreeClassifier': criterion: ['entropy'] max_depth: [1, null] min_samples_split: [25] min_samples_leaf: [0.025, 0.10] 'sklearn.ensemble.RandomForestClassifier': n_estimators: [10, 50] max_depth: [null, 1, 2, 10] max_features: ['log2'] min_samples_split: [10] criterion: ['gini'] random_state: [2193] n_jobs: [-1] ``` ## Crosstabs config file ```yaml= output: # Define the schema and table for crosstabs schema: 'test_results' table: 'crosstabs_best_models' thresholds: # Thresholds for defining positive predictions rank_abs: [100] rank_pct: [] #(optional): a list of entity_ids to subset on the crosstabs analysis entity_id_list: [] #SQL query for getting model_ids models_list_query: "select model_id :: int as model_id, model_group_id from triage_metadata.models where model_group_id in(390,433,409,386,438,56)" as_of_dates_query: "select distinct as_of_date :: date as as_of_date from cohort_alumnos_inscritos_3c_b743bb3631d8df996676f945221497fc" #SQL query for getting as_of_dates #don't change this query unless strictly necessary. It is just validating pairs of (model_id,as_of_date) #it is just a join with distinct (model_id, as_of_date) in a predictions table models_dates_join_query: " select model_id, as_of_date from models_list_query m cross join as_of_dates_query a join (select distinct model_id, as_of_date from test_results.predictions) p using (model_id, as_of_date)" #features_query must join models_dates_join_query with 1 or more features table using as_of_date features_query: | select m.model_id, f1.*, f2.prepa_entity_id_all_pase_8_1_max, f3.reprobar_examen_redaccion_entity_id_all_reprobar_1_max, f4.reprobar_examen_intro_mate_entity_id_all_reprobar_1_max, f5.edad_primer_semestre_entity_id_all_total_max, f5.edad_primer_semestre_entity_id_all_total_imp, f6.total_aprobadas_entity_id_all_total_count, f7.total_reprobadas_entity_id_1y_total_count, f7.total_reprobadas_entity_id_2y_total_count, f7.total_reprobadas_entity_id_all_total_count, f8.total_bajas_entity_id_all_total_count, f9.porcentaje_aprobadas_entity_id_all_total_avg,f9.porcentaje_aprobadas_entity_id_all_total_max, f9.porcentaje_aprobadas_entity_id_all_total_min, f10.cambio_carrera_entity_id_all_total_count, f11.prioridad_entity_id_1y_total_avg,f11.prioridad_entity_id_1y_total_imp, f11.prioridad_entity_id_1y_total_max,f11.prioridad_entity_id_1y_total_min, f11.prioridad_entity_id_2y_total_avg,f11.prioridad_entity_id_2y_total_imp, f11.prioridad_entity_id_2y_total_max,f11.prioridad_entity_id_2y_total_min, f11.prioridad_entity_id_all_total_avg,f11.prioridad_entity_id_all_total_imp, f11.prioridad_entity_id_all_total_max,f11.prioridad_entity_id_all_total_min, f12."genero_entity_id_all_genero__NULL_max" , f12.genero_entity_id_all_genero_f_max, f12.genero_entity_id_all_genero_m_max from features.examen_admision_aggregation_imputed f1 inner join features.prepa_aggregation_imputed f2 using (entity_id, as_of_date) inner join features.reprobar_examen_redaccion_aggregation_imputed f3 using (entity_id, as_of_date) inner join features.reprobar_examen_intro_mate_aggregation_imputed f4 using (entity_id, as_of_date) inner join features.edad_primer_semestre_aggregation_imputed f5 using (entity_id, as_of_date) inner join features.total_aprobadas_aggregation_imputed f6 using (entity_id, as_of_date) inner join features.total_reprobadas_aggregation_imputed f7 using (entity_id, as_of_date) inner join features.total_bajas_aggregation_imputed f8 using (entity_id, as_of_date) inner join features.porcentaje_aprobadas_aggregation_imputed f9 using (entity_id, as_of_date) inner join features.cambio_carrera_aggregation_imputed f10 using (entity_id, as_of_date) inner join features.prioridad_aggregation_imputed f11 using (entity_id, as_of_date) inner join features.genero_aggregation_imputed f12 using (entity_id, as_of_date) inner join models_dates_join_query m using (as_of_date) #the predictions query must return model_id, as_of_date, entity_id, score, label_value, rank_abs and rank_pct #it must join models_dates_join_query using both model_id and as_of_date predictions_query: " select model_id, as_of_date, entity_id, score, label_value, coalesce(rank_abs_no_ties, row_number() over (partition by (model_id, as_of_date) order by score desc)) as rank_abs, coalesce(rank_pct_no_ties*100, ntile(100) over (partition by (model_id, as_of_date) order by score desc)) as rank_pct from test_results.predictions JOIN models_dates_join_query USING(model_id, as_of_date) where model_id IN (select model_id from models_list_query) AND as_of_date in (select as_of_date from as_of_dates_query)" ``` ## Aequitas config file ```yaml= # Thresholds for positive class (absolutes top k and top k %) score_thresholds: rank_abs: [100] #rank_pct: [0.01, 0.02, 0.05, 0.10] #needs to be a float between 0 and 1 #available options: "predefined", "majority" and "min_metric" ref_groups_method: "predefined" # if "predefined" is selected, you need to provide the column:value pairs to be used as reference ref_groups: "genero": "m" "foraneo_string": "n" # fairness threshold to be used for assessing fairness fairness_threshold: 0.8 # available fairness_measures "Impact Parity", "Statistical Parity", "FPR Parity", "FDR Parity", "FNR Parity", "FOR Parity" fairness_measures: ["FOR Parity", "FNR Parity"] # to connect to a database instead of using "--input <filename>", use the db key, credentials, and input_query db: db_credentials: host: database-amai.cfdemdotqpti.us-east-1.rds.amazonaws.com database: itam user: user password: your_pass port: 5432 # the input query should return a table with score, label_value columns and then each attribute you want to use for bias analysis input_query: | select entity_id as id, score, label_value, genero, case when foraneo = 0 then 'n' else 'y' end foraneo_string from test_results.predictions left join features.genero_from_obj using (entity_id) left join features.foraneo_from_obj using (entity_id) where model_id in (4506) -- 4511,4528,4552,4558 # the output schema is optional, default=public output_schema: test_results output_table: aequitas_model_4506_predefined ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully