knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = reticulate::py_module_available("keras") && requireNamespace("keras3", quietly = TRUE) && requireNamespace("ggplot2", quietly = TRUE) && requireNamespace("tune", quietly = TRUE) && requireNamespace("dials", quietly = TRUE) && requireNamespace("parsnip", quietly = TRUE) && requireNamespace("workflows", quietly = TRUE) && requireNamespace("recipes", quietly = TRUE) && requireNamespace("rsample", quietly = TRUE) )
When using kerasnip to define and tune Keras models within the tidymodels framework, you might encounter situations where you want to tune multiple parameters that, by default, map to the same underlying dials parameter type. A common example is tuning the number of units in multiple layer_dense blocks within the same model.
While kerasnip intelligently maps these parameters (e.g., dense1_units and dense2_units both map to dials::hidden_units()), this can lead to ambiguity when visualizing tuning results with ggplot2::autoplot(). Without a way to distinguish between these otherwise identical parameter types, autoplot() may produce errors or misleading plots.
This vignette demonstrates how to explicitly provide unique identifiers to your tuned parameters, ensuring autoplot() can correctly visualize the results for each distinct parameter.
Consider a model with two dense layers, each with a units parameter. If you were to define them for tuning without unique ids, autoplot() would encounter an issue because it cannot distinguish between the two parameters.
For example, if you were to run ggplot2::autoplot(tune_res) without unique ids, you might encounter an error similar to this:
#> Error in `dplyr::rename()`: #> ! Names must be unique. #> ✖ These names are duplicated: #> * "# Hidden Units" at locations 1 and 2.
This error clearly indicates that autoplot() is trying to rename columns for plotting, but it finds duplicate names like "# Hidden Units" because both dense1_units and dense2_units are generically identified as hidden_units by dials without further distinction. This makes it impossible for autoplot() to differentiate their tuning results.
ids with tune()The solution is to provide a unique id argument to the tune() function for each parameter you wish to distinguish.
Let's define a simple sequential Keras model with two dense layers:
library(kerasnip) library(keras3) library(parsnip) library(dials) library(workflows) library(recipes) library(rsample) library(tune) library(ggplot2) # Define a spec with multiple hidden unit parameters model_name <- "autoplot_unique_spec" # Clean up the spec if it already exists from a previous run if (exists(model_name, mode = "function")) { suppressMessages(remove_keras_spec(model_name)) } input_block <- function(model, input_shape) { keras3::keras_model_sequential(input_shape = input_shape) } dense_block <- function(model, units = 10) { model |> keras3::layer_dense(units = units) } output_block <- function(model, num_classes) { model |> keras3::layer_dense(units = num_classes, activation = "softmax") } create_keras_sequential_spec( model_name = model_name, layer_blocks = list( input = input_block, dense1 = dense_block, dense2 = dense_block, output = output_block ), mode = "classification" ) # Now, create the model specification and assign unique IDs for tuning tune_spec <- autoplot_unique_spec( dense1_units = tune(id = "dense_layer_one_units"), dense2_units = tune(id = "dense_layer_two_units") ) |> set_engine("keras") print(tune_spec)
Notice how dense1_units and dense2_units are both passed to tune(), but each is given a distinct id. This id acts as a label that autoplot() can use to differentiate the parameters.
Next, we'll set up a tidymodels workflow, define the parameter ranges, and create a tuning grid.
# Set up workflow and tuning grid rec <- recipes::recipe(Species ~ ., data = iris) tune_wf <- workflows::workflow(rec, tune_spec) params <- tune::extract_parameter_set_dials(tune_wf) # Update the parameter ranges using kerasnip::hidden_units # The `id`s provided in tune() are automatically detected and used here. params <- params |> update( dense_layer_one_units = hidden_units(range = c(4L, 8L)), dense_layer_two_units = hidden_units(range = c(4L, 8L)) ) grid <- dials::grid_regular(params, levels = 2) control <- tune::control_grid(save_pred = FALSE, verbose = FALSE) print(grid)
Now, we run tune::tune_grid to perform the actual tuning. For demonstration purposes, we'll use a small number of resamples and a simple dataset.
# Run tuning tune_res <- tune::tune_grid( tune_wf, resamples = rsample::vfold_cv(iris, v = 2), grid = grid, control = control ) print(tune_res)
autoplot()With the tuning complete, we can now use ggplot2::autoplot() to visualize the results. Because we provided unique ids, autoplot() can correctly generate separate plots for each tuned parameter.
# Assert that autoplot works without error ggplot2::autoplot(tune_res)
As you can see, autoplot() successfully generates a plot showing the performance across the different values for dense_layer_one_units and dense_layer_two_units independently.
ids are NecessaryInternally, kerasnip maps arguments like units from your layer_blocks functions to appropriate dials parameter objects (e.g., dials::hidden_units()). When multiple such arguments exist, they all point to the same type of dials parameter.
The id argument in tune() serves as a unique identifier that tune and ggplot2::autoplot() use to distinguish between different instances of these parameter types. Without it, autoplot() would see multiple parameters of type hidden_units and wouldn't know how to plot them separately, leading to errors or combining them incorrectly.
ids: When tuning multiple parameters that are conceptually similar (e.g., units in different layers, rate in different dropout layers), always provide a descriptive and unique id to the tune() function.ids: Choose ids that clearly indicate which part of the model the parameter belongs to (e.g., dense_layer_one_units, conv_filter_size). This improves readability and understanding of your tuning results.By following this practice, you ensure that your kerasnip models are robustly tunable and that their results can be clearly visualized using the tidymodels ecosystem.
suppressMessages(remove_keras_spec(model_name))
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.