Taller Tidymodels

R-Ladies Chile (Valparaíso + Santiago + Talca + Concepción)

Hola!

saryace | Saryace | saryace.github.io

  • Sara Acevedo
  • Doctora en Ciencias de la Ingeniería UC
  • Magíster UC-Davis
  • Usuaria de R hace años 👩‍💻
  • Clases y proyectos

Hola!

soilbiophysics1 | website | soilbiophysicslab

  • Laboratorio de Biofísica de Suelos
  • Estudiamos como el agua se relaciona con el suelo y plantas
  • Temas: erosión, retención de humedad e incendios

Qué aprenderemos en este taller 💻

El megapaquete (o framework) tidymodels contiene un set de paquetes para la modelación y uso de machine learning usando los principios de tidyverse

  • Manejo de datos usando tidyverse
  • Graficar usando ggplot2
  • Modelar info usando tidymodels

Qué NO veremos en este taller 💻

  • Descripción de los modelos: por tiempo ya que alta variedad de modelos y algoritmos
  • Revisión conceptos estadísticos: uso del paquete broom será parte de otro taller

Código y materiales

Las presentaciones contienen código real (se puede copiar y pegar directamente). El repositorio contendrá el material y contenido del taller

1 + 1 # copia y pega en la consola

El repositorio lo puedes encontrar acá:

Repositorio en github: R-LadiesChile
Repositorio en github: Saryace

La filosofía del paquete tidymodels y tidyverse

  • Reusar estructuras de datos
  • Utilizar el operador pipe %>% para combinar funciones
  • Pensado para “humanos”

Ejemplo: un pedazo de torta a partir de ingredientes

  • Ingredientes (harina, azúcar, etc.) 🛒
  • Mezclar en un bowl ingredientes 🥣
  • Hornear la mezcla ⏲️
  • Decorar el bizcocho 🥧
  • Cortar 🍰

Estilo Tidyverse

comprar(ingredientes = c((harina,azucar))) %>%
mezclar() %>% 
hornear() %>% 
decorar() %>% 
cortar()

Tanto tidymodels como tidyverse comparten este estilo

Idea de Arthur Welles @ArthurWelle

Antes de comenzar: instalación de paquetes

Copiar y pegar en la consola el siguiente chunk de código

install.packages(c("tidymodels",
                   "datos",
                   "workflows",
                   "workflowsets"))

Antes de comenzar: créditos y más información

Comenzamos cargando un set de datos

Tamaño de pingüinos adultos en busca de comida cerca de la estación Palmer en la Antártica

# A tibble: 344 × 8
   especie isla      largo_pico_mm alto_pico_mm largo_alet…¹ masa_…² sexo   anio
   <fct>   <fct>             <dbl>        <dbl>        <int>   <int> <fct> <int>
 1 Adelia  Torgersen          39.1         18.7          181    3750 macho  2007
 2 Adelia  Torgersen          39.5         17.4          186    3800 hemb…  2007
 3 Adelia  Torgersen          40.3         18            195    3250 hemb…  2007
 4 Adelia  Torgersen          NA           NA             NA      NA <NA>   2007
 5 Adelia  Torgersen          36.7         19.3          193    3450 hemb…  2007
 6 Adelia  Torgersen          39.3         20.6          190    3650 macho  2007
 7 Adelia  Torgersen          38.9         17.8          181    3625 hemb…  2007
 8 Adelia  Torgersen          39.2         19.6          195    4675 macho  2007
 9 Adelia  Torgersen          34.1         18.1          193    3475 <NA>   2007
10 Adelia  Torgersen          42           20.2          190    4250 <NA>   2007
# … with 334 more rows, and abbreviated variable names ¹​largo_aleta_mm,
#   ²​masa_corporal_g

Un problema

  • Los pingüinos no se pueden manipular, solo se puede medir sus aletas a distancia

  • Nos preguntamos si podemos predecir la masa de los pingüinos a partir del largo de las aletas y pico.

  • Algo de estadística básica: la medida de la aletas es la variable independiente x y la masa es la variable dependiente y.

Un par de definiciones

  • Como conocemos los valores de x e y, este problema se aplica ML supervisado (existe ML no-supervisado)

  • Como la predicción es un variable continua (masa), es un problema de regresión (existe también la clasificación)

Como comenzar a modelar?

  • Análisis exploratorio de datos

  • Creación de un conjunto de entrenamiento y testeo

  • Decidir si y cómo preprocesar los datos para que sean apropiados para la modelación

  • Creación y ajuste de modelos en un conjunto de entrenamiento

  • Evaluación de los modelos según los parámetros seleccionados

  • Perfeccionamiento de los modelos

  • Evaluación del modelo elegido con el conjunto de testeo

Tidy Modeling with R, Max Kuhn y Julia Silge

Análisis exploratorio de datos

Por tiempo, no lo veremos en profundidad, pero conocer sobre los datos nos ayuda a crear mejores modelos

dplyr::glimpse(pinguinos)
Rows: 344
Columns: 8
$ especie         <fct> Adelia, Adelia, Adelia, Adelia, Adelia, Adelia, Adelia…
$ isla            <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgersen,…
$ largo_pico_mm   <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.1, 42…
$ alto_pico_mm    <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.1, 20…
$ largo_aleta_mm  <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, 186, …
$ masa_corporal_g <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 3475, 42…
$ sexo            <fct> macho, hembra, hembra, NA, hembra, macho, hembra, mach…
$ anio            <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, …

Análisis exploratorio de datos

pinguinos %>%
  drop_na() %>% 
      ggplot(aes(x = masa_corporal_g,
                 y = largo_aleta_mm)) +
      geom_point(alpha = 0.3) +
      labs(
      x = "masa corporal en gramos",
      y = "largo aleta",
      title = "Análisis exploratorio masa pinguinos",
      subtitle = "Masa vs largo aleta"
      ) +
  theme_bw()

Análisis exploratorio de datos

Creación de un conjunto de entrenamiento y prueba

El paquete rsample posee funciones para crear los sets de entrenamiento y testeo

set.seed(2023)

pinguinos_division <- rsample::initial_split(pinguinos, prop = 0.80, strata = masa_corporal_g)

pinguinos_entrenamiento <- rsample::training(pinguinos_division)

pinguinos_testeo  <-  rsample::testing(pinguinos_division)

Creación de un conjunto de entrenamiento y prueba

El paquete rsample posee funciones para crear los sets de entrenamiento y testeo

dplyr::glimpse(pinguinos_entrenamiento)
Rows: 273
Columns: 8
$ especie         <fct> Adelia, Adelia, Adelia, Adelia, Adelia, Adelia, Adelia…
$ isla            <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgersen,…
$ largo_pico_mm   <dbl> 36.7, 34.1, 37.8, 41.1, 38.7, 37.8, 40.6, 40.5, 37.9, …
$ alto_pico_mm    <dbl> 19.3, 18.1, 17.1, 17.6, 19.0, 18.3, 18.6, 17.9, 18.6, …
$ largo_aleta_mm  <int> 193, 193, 186, 182, 195, 174, 183, 187, 172, 178, 195,…
$ masa_corporal_g <int> 3450, 3475, 3300, 3200, 3450, 3400, 3550, 3200, 3150, …
$ sexo            <fct> hembra, NA, NA, hembra, hembra, hembra, macho, hembra,…
$ anio            <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, …

Creación de un conjunto de entrenamiento y prueba

El paquete rsample posee funciones para crear los sets de entrenamiento y testeo

dplyr::glimpse(pinguinos_testeo)
Rows: 71
Columns: 8
$ especie         <fct> Adelia, Adelia, Adelia, Adelia, Adelia, Adelia, Adelia…
$ isla            <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgersen,…
$ largo_pico_mm   <dbl> 39.1, 40.3, NA, 39.2, 42.0, 34.4, 37.7, 38.8, 39.5, 39…
$ alto_pico_mm    <dbl> 18.7, 18.0, NA, 19.6, 20.2, 18.4, 18.7, 17.2, 17.8, 19…
$ largo_aleta_mm  <int> 181, 195, NA, 195, 190, 184, 180, 180, 188, 184, 190, …
$ masa_corporal_g <int> 3750, 3250, NA, 4675, 4250, 3325, 3600, 3800, 3300, 46…
$ sexo            <fct> macho, hembra, NA, macho, NA, hembra, macho, macho, he…
$ anio            <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, …

Decidir si y cómo preprocesar los datos para que sean apropiados para la modelación

Depende de los datos y de los tipos de modelos. Pero el paquete recipes tiene muchas opciones. Ejemplos de preprocesamiento:

  • Datos ausentes
  • Datos categóricos
  • Transformaciones (centrar, escalar, etc.)

Decidir si y cómo preprocesar los datos para que sean apropiados para la modelación

Decidir si y cómo preprocesar los datos para que sean apropiados para la modelación

receta_pinguinos <-
  recipe(masa_corporal_g ~ largo_aleta_mm + largo_pico_mm,
        data = pinguinos_entrenamiento) %>%
  step_impute_mean(masa_corporal_g,
                   largo_aleta_mm,
                   largo_pico_mm)

receta_pinguinos
Recipe

Inputs:

      role #variables
   outcome          1
 predictor          2

Operations:

Mean imputation for masa_corporal_g, largo_aleta_mm, largo_pico_mm

Creación y ajuste de modelos en un conjunto de entrenamiento

Esto lo haremos con los paquete workflows y parsnip. Debemos indicar que modelo y receta usar en el conjunto de entrenamiento

modelo_lm <- 
parsnip::linear_reg() %>%
parsnip::set_engine("lm")

modelo_lm
Linear Regression Model Specification (regression)

Computational engine: lm 

Creación y ajuste de modelos en un conjunto de entrenamiento

workflow_lm <-
workflow() %>% 
workflows::add_recipe(receta_pinguinos) %>% 
workflows::add_model(modelo_lm) 
 
ajuste_lm <- fit(workflow_lm, pinguinos_entrenamiento)

ajuste_lm
══ Workflow [trained] ══════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: linear_reg()

── Preprocessor ────────────────────────────────────────────────────────────────
1 Recipe Step

• step_impute_mean()

── Model ───────────────────────────────────────────────────────────────────────

Call:
stats::lm(formula = ..y ~ ., data = data)

Coefficients:
   (Intercept)  largo_aleta_mm   largo_pico_mm  
     -5959.415          49.304           5.647  

Evaluación de los modelos según los parámetros seleccionados

Usando el paquete broom

ajuste_lm %>% tidy()
# A tibble: 3 × 5
  term           estimate std.error statistic  p.value
  <chr>             <dbl>     <dbl>     <dbl>    <dbl>
1 (Intercept)    -5959.      327.      -18.2  5.69e-49
2 largo_aleta_mm    49.3       2.10     23.5  3.40e-67
3 largo_pico_mm      5.65      5.29      1.07 2.87e- 1

Perfeccionamiento de los modelos

  1. Realizar el ajuste de hiperparámetros
  2. Hacerlo mediante validación cruzada

Perfeccionamiento de los modelos

  1. Realizar el ajuste de hiperparámetros
  2. Hacerlo mediante validación cruzada

https://stats.stackexchange.com/questions/595512/

Perfeccionamiento de los modelos

  1. Realizar el ajuste de hiperparámetros
  2. Hacerlo mediante validación cruzada

tidymodels tiene dos paquetes para ayudarnos con estos pasos: tune y rsample

# modelo lm()
modelo_lm <- 
parsnip::linear_reg() %>%
parsnip::set_engine("lm")

modelo_lm
Linear Regression Model Specification (regression)

Computational engine: lm 
# modelo glm()
modelo_glm <- 
parsnip::linear_reg(penalty = 0.1,
                    mixture = 0.95) %>%
parsnip::set_engine("glmnet")

modelo_glm
Linear Regression Model Specification (regression)

Main Arguments:
  penalty = 0.1
  mixture = 0.95

Computational engine: glmnet 

Perfeccionamiento de los modelos

# modelo glm()
modelo_glm <- 
parsnip::linear_reg(penalty = 0.1,
                    mixture = 0.95) %>%
parsnip::set_engine("glmnet")

modelo_glm
Linear Regression Model Specification (regression)

Main Arguments:
  penalty = 0.1
  mixture = 0.95

Computational engine: glmnet 
# creamos grilla de valores a testear

penalty_tune <- 10^seq(-3, 0,
                       length.out = 10)

grilla <- crossing(penalty = penalty_tune,
                   mixture = c(0.1, 1.0))

Perfeccionamiento de los modelos

# creamos grilla de valores a testear

penalty_tune
 [1] 0.001000000 0.002154435 0.004641589 0.010000000 0.021544347 0.046415888
 [7] 0.100000000 0.215443469 0.464158883 1.000000000
grilla 
# A tibble: 20 × 2
   penalty mixture
     <dbl>   <dbl>
 1 0.001       0.1
 2 0.001       1  
 3 0.00215     0.1
 4 0.00215     1  
 5 0.00464     0.1
 6 0.00464     1  
 7 0.01        0.1
 8 0.01        1  
 9 0.0215      0.1
10 0.0215      1  
11 0.0464      0.1
12 0.0464      1  
13 0.1         0.1
14 0.1         1  
15 0.215       0.1
16 0.215       1  
17 0.464       0.1
18 0.464       1  
19 1           0.1
20 1           1  

Perfeccionamiento de los modelos

A continuación, debemos especificar cómo realizaremos la validación cruzada. Desde el paquete paquete rsample, podemos utilizar la función vfold_cv

# creamos el seteo de crossvalidation


pinguinos_folds <- vfold_cv(pinguinos_entrenamiento, v = 5)
pinguinos_folds
#  5-fold cross-validation 
# A tibble: 5 × 2
  splits           id   
  <list>           <chr>
1 <split [218/55]> Fold1
2 <split [218/55]> Fold2
3 <split [218/55]> Fold3
4 <split [219/54]> Fold4
5 <split [219/54]> Fold5

Perfeccionamiento de los modelos

Ahora creamos un modelo “tuneable”

# modelo glm() con hiperparametros fijos
modelo_glm <- 
parsnip::linear_reg(penalty = 0.1,
                    mixture = 0.95) %>%
parsnip::set_engine("glmnet")

modelo_glm
Linear Regression Model Specification (regression)

Main Arguments:
  penalty = 0.1
  mixture = 0.95

Computational engine: glmnet 
# modelo glm() con hiperparametros tuneables
modelo_glm_tune <- 
  linear_reg(penalty = tune(),
             mixture = tune()) %>% 
  set_engine("glmnet",
             path_values = penalty_tune)

modelo_glm_tune
Linear Regression Model Specification (regression)

Main Arguments:
  penalty = tune()
  mixture = tune()

Engine-Specific Arguments:
  path_values = penalty_tune

Computational engine: glmnet 

Perfeccionamiento de los modelos

# modelo glm() con hiperparametros tuneables

workflow_glm_tune <- 
workflow() %>% 
  workflows::add_recipe(receta_pinguinos) %>% 
  workflows::add_model(modelo_glm_tune) 
# tuneamos el modelo glm

glm_tuned <- tune_grid(
  # el workflow para tunear
  workflow_glm_tune, 
  # los "folds"
  resamples = pinguinos_folds,
  # La grilla de hiperparametros
  grid = grilla)

Perfeccionamiento de los modelos

glm_tuned
# Tuning results
# 5-fold cross-validation 
# A tibble: 5 × 4
  splits           id    .metrics          .notes          
  <list>           <chr> <list>            <list>          
1 <split [218/55]> Fold1 <tibble [40 × 6]> <tibble [0 × 3]>
2 <split [218/55]> Fold2 <tibble [40 × 6]> <tibble [0 × 3]>
3 <split [218/55]> Fold3 <tibble [40 × 6]> <tibble [0 × 3]>
4 <split [219/54]> Fold4 <tibble [40 × 6]> <tibble [0 × 3]>
5 <split [219/54]> Fold5 <tibble [40 × 6]> <tibble [0 × 3]>

Perfeccionamiento de los modelos

autoplot(glm_tuned)

Perfeccionamiento de los modelos

glm_best <- select_best(glm_tuned, metric = "rmse")
glm_best
# A tibble: 1 × 3
  penalty mixture .config              
    <dbl>   <dbl> <chr>                
1       1     0.1 Preprocessor1_Model10

Evaluación del modelo elegido con el conjunto de testeo

Ya tenemos nuestro mejor modelo glm_best, ahora lo utilizaremos en el conjunto de testeo

Primero hacemos nuestro último workflow

glm_best_workflow <- 
  workflow_glm_tune %>%
  finalize_workflow(tibble(penalty = 1,
                           mixture = 0.1)) %>%
  fit(data = pinguinos_entrenamiento)

glm_best_workflow 
══ Workflow [trained] ══════════════════════════════════════════════════════════
Preprocessor: Recipe
Model: linear_reg()

── Preprocessor ────────────────────────────────────────────────────────────────
1 Recipe Step

• step_impute_mean()

── Model ───────────────────────────────────────────────────────────────────────

Call:  glmnet::glmnet(x = maybe_matrix(x), y = y, family = "gaussian",      alpha = ~0.1, lambda = ~penalty_tune) 

   Df  %Dev  Lambda
1   2 78.59 1.00000
2   2 78.59 0.46420
3   2 78.59 0.21540
4   2 78.59 0.10000
5   2 78.59 0.04642
6   2 78.59 0.02154
7   2 78.59 0.01000
8   2 78.59 0.00464
9   2 78.59 0.00215
10  2 78.59 0.00100

Evaluación del modelo elegido con el conjunto de testeo

Ya tenemos nuestro mejor modelo glm_best, ahora lo utilizaremos en el conjunto de testeo

Primero hacemos nuestro último workflow

glm_best_workflow %>% tidy()
# A tibble: 3 × 3
  term           estimate penalty
  <chr>             <dbl>   <dbl>
1 (Intercept)    -5946.         1
2 largo_aleta_mm    49.2        1
3 largo_pico_mm      5.77       1

Evaluación del modelo elegido con el conjunto de testeo

Ya tenemos nuestro mejor modelo glm_best, ahora lo utilizaremos en el conjunto de testeo

Ahora usamos el conjunto de testeo

last_fit_glm <- last_fit(glm_best_workflow, pinguinos_division)

last_fit_glm %>% collect_metrics()
# A tibble: 2 × 4
  .metric .estimator .estimate .config             
  <chr>   <chr>          <dbl> <chr>               
1 rmse    standard     462.    Preprocessor1_Model1
2 rsq     standard       0.663 Preprocessor1_Model1

Evaluación del modelo elegido con el conjunto de testeo

Ya tenemos nuestro mejor modelo glm_best, ahora lo utilizaremos en el conjunto de testeo

Ahora usamos el conjunto de testeo

last_fit_glm %>% collect_predictions()
# A tibble: 71 × 5
   id               .pred  .row masa_corporal_g .config             
   <chr>            <dbl> <int>           <int> <chr>               
 1 train/test split 3187.     1            3750 Preprocessor1_Model1
 2 train/test split 3883.     3            3250 Preprocessor1_Model1
 3 train/test split 4199.     4            4193 Preprocessor1_Model1
 4 train/test split 3876.     8            4675 Preprocessor1_Model1
 5 train/test split 3646.    10            4250 Preprocessor1_Model1
 6 train/test split 3307.    19            3325 Preprocessor1_Model1
 7 train/test split 3129.    22            3600 Preprocessor1_Model1
 8 train/test split 3136.    25            3800 Preprocessor1_Model1
 9 train/test split 3534.    33            3300 Preprocessor1_Model1
10 train/test split 3338.    40            4650 Preprocessor1_Model1
# … with 61 more rows

Evaluación del modelo elegido con el conjunto de testeo

Graficamos los resultados del set de testeo

last_fit_glm %>% 
      collect_predictions() %>%
      ggplot(aes(x = masa_corporal_g,
                 y = .pred)) +
      geom_abline(lty = 2, color = "purple", size = 2) +
      geom_point(alpha = 0.3) +
      labs(
      x = "masa corporal en gramos",
      y = "masa predicha",
      title = "Predicción masa pinguinos",
      subtitle = "Análisis en el set de testeo usando modelo glm"
      ) +
  theme_bw()

Evaluación del modelo elegido con el conjunto de testeo

Este taller fue introductorio, otras capacidades de tidymodels 💻

  • Comparación entre diferentes modelos: workflows y workflowset son paquetes que facilitan este análisis.
  • Interpretación de modelos y sus variables: importancia de variables, como en modelos de random forest, redes neuronales y otros.

Muchas gracias por su atención ❤️

saryace | Saryace | saryace.github.io