parsnip: especificar modelos agnóstico al engine
¿Por qué desacoplar modelo de implementación?
Antes de parsnip, cada paquete de ML en R tenía su propia API. Entrenar un random forest con randomForest::randomForest() requería argumentos distintos a ranger::ranger(), que a su vez eran distintos a randomForestSRC::rfsrc(). Cambiar de implementación implicaba reescribir el código.
parsnip resuelve esto con una interfaz única: tú declaras “quiero un random forest con 500 árboles” en sintaxis estándar, y parsnip traduce a la API del paquete que elijas. Cambiar de ranger a xgboost es una sola línea.
library(tidymodels)Especificar un modelo: forma básica
Una spec de parsnip tiene tres componentes:
- El tipo de modelo: qué hace conceptualmente (regresión lineal, random forest, etc.).
- El engine: qué paquete implementa el cálculo.
- El modo:
"classification"o"regression".
modelo_rf <- rand_forest(trees = 500, mtry = 4, min_n = 10) |>
set_engine("ranger") |>
set_mode("classification")Léelo: “un random forest con 500 árboles, mtry = 4 y nodos hoja con al menos 10 observaciones, implementado con ranger, en modo clasificación”.
Los argumentos del modelo se llaman main arguments, son los que tienen sentido independientemente del engine. Para random forest son trees, mtry, min_n. Para regresión lineal son penalty y mixture. Cada tipo de modelo tiene los suyos.
set_engine y set_mode
Los modelos en parsnip se separan en estas dos decisiones explícitas:
set_engine(), elige el paquete subyacente:
rand_forest() |> set_engine("ranger") # paquete ranger
rand_forest() |> set_engine("randomForest") # paquete randomForest legacy
rand_forest() |> set_engine("xgboost") # XGBoost (sí, también puede)
rand_forest() |> set_engine("spark") # H2O via sparklyrset_mode(), "classification" o "regression". No es opcional, muchos modelos admiten ambos modos y la spec se construye distinta en cada caso.
modelo <- rand_forest() # error: falta mode
modelo <- rand_forest() |> set_mode("regression") # OKIntercambiar engines: la prueba real
Donde parsnip brilla es cuando quieres comparar implementaciones. La spec no cambia. Solo set_engine:
# Mismo modelo conceptual, 3 implementaciones distintas
modelo_ranger <- rand_forest(trees = 500, mtry = 4, min_n = 10) |>
set_engine("ranger") |>
set_mode("classification")
modelo_rf_legacy <- rand_forest(trees = 500, mtry = 4, min_n = 10) |>
set_engine("randomForest") |>
set_mode("classification")
modelo_xgb <- rand_forest(trees = 500, mtry = 4, min_n = 10) |>
set_engine("xgboost") |>
set_mode("classification")El resto del pipeline (recipe, workflow, métricas) no cambia. Esto te permite tener un experimento sistemático cambiando una sola línea.
Pasar argumentos al engine
Los main arguments cubren lo común. Para argumentos específicos del engine (que no tienen sentido en todos los implementadores), se pasan dentro de set_engine():
modelo <- rand_forest(trees = 500, mtry = 4) |>
set_engine("ranger",
importance = "impurity", # específico de ranger
num.threads = 4, # específico de ranger
splitrule = "extratrees") |>
set_mode("classification")Estos argumentos pasan tal cual a ranger::ranger(). Si cambias el engine a xgboost, estos argumentos ya no aplican.
Esta separación es deliberada: parsnip no abstrae todas las opciones (sería imposible). Abstrae las comunes y deja una vía limpia para personalización específica.
Catálogo de modelos disponibles
# Ver todos los modelos disponibles
show_engines("rand_forest")
#> # A tibble: 7 × 2
#> engine mode
#> <chr> <chr>
#> 1 ranger classification
#> 2 ranger regression
#> 3 randomForest classification
#> 4 randomForest regression
#> 5 spark classification
#> 6 spark regression
#> 7 xgboost classification# Ver detalles de un modelo + engine concretos
show_model_info("rand_forest")El catálogo completo está en la web de tidymodels. Los más usados:
| Modelo | Función parsnip |
Engines típicos |
|---|---|---|
| Regresión lineal | linear_reg() |
lm, glmnet, stan |
| Regresión logística | logistic_reg() |
glm, glmnet, keras |
| Random forest | rand_forest() |
ranger, randomForest, xgboost |
| Boosted trees | boost_tree() |
xgboost, lightgbm, C5.0 |
| SVM | svm_rbf(), svm_linear() |
kernlab, liquidSVM |
| Red neuronal (simple) | mlp() |
nnet, keras, brulee |
| K-NN | nearest_neighbor() |
kknn |
| Naive Bayes | naive_Bayes() |
klaR, naivebayes |
Más allá del modelo: tune() como placeholder
Una funcionalidad clave de parsnip es declarar hiperparámetros sin valor, marcándolos como a tunear:
modelo <- rand_forest(
trees = 500,
mtry = tune(),
min_n = tune()
) |>
set_engine("ranger") |>
set_mode("classification")tune() no es un valor, es un marcador que dice “este hiperparámetro se decide más tarde, en la fase de tuning”. Lo veremos en detalle en el tutorial de tune. Por ahora basta con saber que es así como se declara la intención de optimizar.
Inspeccionar la spec
Antes de fit(), puedes ver qué se va a ejecutar:
translate(modelo_ranger)
#> Random Forest Model Specification (classification)
#>
#> Main Arguments:
#> mtry = 4
#> trees = 500
#> min_n = 10
#>
#> Computational engine: ranger
#>
#> Model fit template:
#> ranger::ranger(x = missing_arg(), y = missing_arg(),
#> mtry = min_cols(~4, x), num.trees = ~500, min.node.size = ~10,
#> num.threads = 1, verbose = FALSE, seed = sample.int(10^5, 1))translate() te muestra la llamada exacta que parsnip va a hacer al engine. Útil para depurar, si el modelo se comporta extraño, lee qué se está ejecutando realmente.
Trampas habituales
- Olvidar
set_mode(). Modelos que admiten ambos modos (random forest, boost_tree, neural net) requierenset_mode()explícito. Si lo omites,fit()falla con un error que no siempre apunta al problema real. - Engine no instalado.
parsnipno instala los paquetes subyacentes. Si declarasset_engine("xgboost")sin tenerxgboostinstalado, elfit()falla.install.packages("xgboost")antes. - Confundir
main argumentscon engine-specific.mtry = 4va enrand_forest().num.threads = 4va enset_engine()(específico de ranger). Si pones todo enrand_forest(),parsnipse queja. - Cambiar
trees = 500enparsnipesperando quentree = 500también cambie enrandomForest. La traducción la haceparsnip, tú usastrees, siempre. Si pasasntreedirectamente,parsnipno lo reconoce.
En la siguiente entrega
Tienes recipe (preprocesado) y model spec (modelo). El siguiente paquete los combina en un único objeto entrenable y serializable: workflows. Es lo que hace que el pipeline sea un objeto coherente en lugar de dos piezas sueltas. Lo siguiente.