Este es el segundo documento de una serie dedicada al desarrollar modelos predictivos en el servicio de justicia. En esta serie abordamos la predicción de las sentencias que se dictarán a futuro, sin perjuicio de lo cual entendemos que con los ajustes del caso el pipeline de trabajo que describimos puede aplicarse a otros tipos de predicciones.
El objetivo de este artículo de la serie es crear modelos de
aprendizaje automático para datos temporales. Emplearemos la librería
modeltime que se utiliza para integrar modelos según la
lógica de programación de tidymodels.
Con esta implementación continuamos el workflow que vimos en la Parte
1 para el desarrollo de modelos predictivos, deteniéndonos en las
especificaciones generales de cada modelo y la receta de
preprocesamiento correspondiente. Recordemos que cada modelo exige
cierta estructura en los datos de entrada, para lo cual la
especificación que realizamos a través de la función recipe
es fundamental. Además veremos las nociones de tabla de modelos y tabla
de calibración que nos serán utiles para evaluar y pronosticar todos los
modelos al mismo tiempo para las 8 series temporales que estamos
tratando de modelar en este ejemplo (sentencias dictadas en cada
materia). Finalmente, realizaremos algunos pronósticos en datos de
testeo para tener una idea del ajuste de los modelos.
Los scripts completos de esta implementación y los documentos RMarkdown que sirven a esta presentación puede accederse en mi repositorio de github castillosebastian. En el README de ese repositorio se puede encontrar un detalle de la configuración del ambiente de trabajo que he utilizado en el proyecto y recomendaciones para su instalación.
Como veremos más adelante, algunas partes de las operaciones de desarrollo de modelos (vinculadas a un pipeline de MLOps) requieren cierto hardware particular (sobretodo RAM y CPU). En nuestro caso hemos trabajado en un Centos 7, con 12 vCPU y 32G de RAM, y en ocasiones esos recursos fueron insuficientes, por lo que algo más de disponibilidad será de ayuda.
Respecto del pipeline de trabajo nos hemos apoyado exclusivamente en R, y particularmente en tidymodels, gracias al aprecio que tenemos por tidyverse. Este framework nos brinda toda una batería de herramientas que facilitan el trabajo y la experimentación, aspecto fundamental en el desarrollo de modelos de machine learning. Para culquiera intersado en él dirigirse aquí: https://www.tidymodels.org/.
El repositorio está armado de tal forma que los scripts puedan correrse de dos formas:
/rmarkdowns, o/source.A continuación presentamos las librerías que emplearemos en este documento. Cada nueva publicación de la serie incluirá un detalle de sus recursos.
pacman::p_load(tidyverse,
timetk,
tsibble,
tsibbledata,
fastDummies,
skimr,
ranger,
lightgbm,
tidymodels,
modeltime, # workflow
treesnip, # lightgbm
kableExtra)
options(scipen = 999)
Los pasos que daremos para la generación de los modelos serán los siguientes:
Abrimos el dataset aumentado que generamos antes
(feature_engeniering.R).
artifacts <- read_rds(paste0(HOME_DIR, "/exp/001/feature_engineering_artifacts_list.rds"))
splits <- artifacts$splits
recipe_spec <- artifacts$recipes$recipe_spec
materia <- artifacts$data$materia
#rm(artifacts, recipe_spec, materia)
Los workflows son objetos que guardan/agrupan distintas partes relativas a la generación de modelos. Son contenedores que guardan mucha información que de otro modo sería difícil de administrar, y saturaría nuestros ambientes de trabajo.
Para permitir que cualquier persona consultante pueda ejecutar este
documento RMarkdown hemos condicionado el ajuste de modelos para evitar
saturar los recursos del ordenador. Si desea ejecutar este RMarkdown y
ajustar todos los modelos que contiene fije la variable
eval_models = TRUE que se encuentra en el primer chunk.
Dentro de los modelos que elegimos para la tarea hay un protagonismo evidente de los algoritmos basados en árboles de decisión. Estos algoritmos largamente documentados, con abundantes implementaciones y aplicaciones, resultan particularmente robustos para modelar relaciones no lineales y datos tabulares. Abonando esta conclusión Léo Grinsztajn et all dicen: Esta superioridad se explica por características específicas de los datos tabulares: patrones irregulares en la función objetivo,[la presencia de ] atributos no informativos y datos no rotacionalmente invariantes donde las combinaciones lineales pierden capacidad de representar apropiadamete la información. (la traducción es nuestra).
Conocidos como bosques aleatorios este tipo de modelo se
basa en la implementación de un conjunto (bagging) de
arboles de decisión. Cada random forest -compuesto por
múltiples modelos- se entrena en un subconjunto de datos (observaciones
de nuestro dataset) aleatereamente seleccionados, y luego se combina los
resultados de los modelos para obtener un resultado final. Mas sobre
Random Forest puede verse aquí.
En nuestro caso utilizaremos la librería ranger.
wflw_fit_rf <- workflow() %>%
add_model(
spec = rand_forest(
mode = "regression"
) %>%
set_engine("ranger")
) %>%
add_recipe(recipe_spec %>%
step_rm(mes)) %>%
fit(training(splits))
Este modelo, también se basa en árboles de decisión, pero implementa un algoritmo (Gradient Boosted Decision Trees) de entrenamiento secuencial que minimiza el error y mejora las predicciones. Mientras que los bosques aleatorios construyen un conjunto de árboles independientes profundos (de múltiples niveles), los algoritmos basados en Gradient Boosting construyen un conjunto de árboles poco profundos (pocas particiones), donde cada nuevo modelo busca minimizar los errores de predicción (residuos) del anterior. Mas sobre xgboos puede verse aquí.
wflw_fit_xgboost <- workflow() %>%
add_model(
spec = boost_tree(
mode = "regression"
) %>%
set_engine("xgboost")
) %>%
add_recipe(recipe_spec %>%
step_rm(mes)) %>%
fit(training(splits))
Prophet es un algoritmo desarrollado por científicos de Datos de META (puede verse la documentación aquí) con una implementación en R. Según mencionan sus creadores este algoritmo está basado en un modelo aditivo en el que las tendencias no lineales se ajustan a la estacionalidad anual, semanal y diaria. Funciona mejor con series temporales que tienen fuertes efectos estacionales y varias temporadas de datos históricos. El paper del modelo puede verse aquí.
wflw_fit_prophet <- workflow() %>%
add_model(
spec = prophet_reg(
seasonality_daily = FALSE,
seasonality_weekly = FALSE,
seasonality_yearly = TRUE
) %>%
set_engine("prophet")
) %>%
add_recipe(recipe_spec) %>%
fit(training(splits))
Este modelo combina Prophet con XGBoost para obtener -dice
su creador- lo mejor de ambos mundos (es decir, Prophet Automation +
Machine Learning). El algoritmo funciona así:
- Primero modela la serie univariante usando Prophet, y luego - Usando
los demás predictores genera una regresión de los residuos de Prophet
con el modelo XGBoost.
wflw_fit_prophet_boost <- workflow() %>%
add_model(
spec = prophet_boost(
seasonality_daily = FALSE,
seasonality_weekly = FALSE,
seasonality_yearly = FALSE
) %>%
set_engine("prophet_xgboost")
) %>%
add_recipe(recipe_spec) %>%
fit(training(splits))
Este algoritmo fue creado por Microsoft, y al igual que XGboots se
base en arboles de decisión y en el método de Gradient Boosting
para el ajuste del modelo, pero a diferencia del anterior la generación
de arboles no procede a través de niveles sino por hoja o nodos finales
(mediante la técnica de Gradient-based One-Side Sampling).
Eso le confiere al lightgbm rapidez y gran performance en dataset muy
grandes.
wflw_fit_ligthgbm <- workflow() %>%
add_model(
spec = parsnip::boost_tree(
mtry = 5,
trees = 100
) %>%
set_mode("regression") %>%
set_engine("lightgbm",
quiet = TRUE,
objective = "root_mean_squared_error")) %>%
add_recipe(recipe_spec) %>%
fit(training(splits))
En esta etapa creamos una tabla donde se agregan los modelos que
actúa como contenedora de todos los workflows que hemos
generado.
submodels_tbl <- modeltime_table(
wflw_fit_rf,
wflw_fit_xgboost,
wflw_fit_prophet,
wflw_fit_prophet_boost,
wflw_fit_ligthgbm
)
submodels_tbl
## # Modeltime Table
## # A tibble: 5 × 3
## .model_id .model .model_desc
## <int> <list> <chr>
## 1 1 <workflow> RANGER
## 2 2 <workflow> XGBOOST
## 3 3 <workflow> PROPHET W/ REGRESSORS
## 4 4 <workflow> PROPHET W/ XGBOOST ERRORS
## 5 5 <workflow> LIGHTGBM
En la etapa de calibración de los modelos se calculan predicciones y residuos a partir de datos de test, no incluidos en el entrenamiento. Una salida de esta etapa es una lista de dataset con las observaciones más las predicciones y residuos.
#Model Evaluation
calibrated_wflws_tbl <- submodels_tbl %>%
modeltime_calibrate(new_data = testing(splits))
Finalmente, la hora de la verdad: la evaluación. Los resultados de la calibración se utilizan para los obtener distintas métricas de la performance de nuestros modelos en datos de testeo.
calibrated_wflws_tbl %>%
modeltime_accuracy(testing(splits)) %>%
arrange(rmse)
## # A tibble: 5 × 9
## .model_id .model_desc .type mae mape mase smape rmse rsq
## <int> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2 XGBOOST Test 0.538 81.2 1.08 115. 0.627 0.364
## 2 4 PROPHET W/ XGBOOST E… Test 0.626 130. 1.26 133. 0.705 0.514
## 3 1 RANGER Test 0.630 86.3 1.27 140. 0.751 0.228
## 4 5 LIGHTGBM Test 0.682 135. 1.37 133. 0.828 0.0375
## 5 3 PROPHET W/ REGRESSORS Test 12.8 2385. 25.7 199. 14.6 0.00683
Vemos que el rsq (variabilidad explicada por parte de
nuestros modelos) es baja, señalando un bajo poder predictivo. Sin
embargo, hay que tener en cuenta que aquí solamente hemos ‘activado’
nuestros modelos con las configuraciones por defecto, sin ningún ajuste
de parámetros que es fundamental para mejorar resultados.
Finalmente metemos los workflow y tabla de calibración en una lista y los guardaremos como archivo RDS.
archivo_salida <- paste0(HOME_DIR,"/exp/001/workflows_artifacts_list.rds")
# Save your work
workflow_artifacts <- list(
workflows = list(
wflw_random_forest = wflw_fit_rf,
wflw_xgboost = wflw_fit_xgboost,
wflw_prophet = wflw_fit_prophet,
wflw_prophet_boost = wflw_fit_prophet_boost,
wflw_lightgbm = wflw_fit_ligthgbm
),
calibration = list(calibration_tbl = calibrated_wflws_tbl)
)
#Para guardar el trabajo generado descomente este chunk
# workflow_artifacts %>%
# write_rds(archivo_salida)
A lo largo del documento presentamos los modelos predictivos que
emplearemos para predecir las sentencias dictadas por materia,
configurando los workflowsde entrenamiento de cada uno y
sus especificaciones generales.
Luego hemos reunido toda la información de los modelos en tablas y evaluado cada uno.
En los próximos documentos avanzaremos la búsqueda de los mejores hiperparámetros de nuestros modelos: alphabet, xgboost, random forest y ligthgbm.