From 74b1296d23a31583286894853a8529bf1c860e37 Mon Sep 17 00:00:00 2001 From: davidrsch Date: Thu, 4 Sep 2025 11:58:19 +0200 Subject: [PATCH 1/4] Updating docs to fix issue with checks --- NAMESPACE | 1 + R/compile_keras_grid.R | 150 ++++++++++++++++++++++++---- R/create_keras_functional_spec.R | 1 + R/create_keras_sequential_spec.R | 5 +- R/generic_functional_fit.R | 9 +- R/generic_sequential_fit.R | 9 +- R/keras_tools.R | 32 ++++-- R/remove_keras_spec.R | 2 +- R/utils.R | 27 +++++ man/compile_keras_grid.Rd | 35 +++++-- man/create_keras_functional_spec.Rd | 1 + man/create_keras_sequential_spec.Rd | 5 +- man/extract_valid_grid.Rd | 58 +++++++++-- man/generic_functional_fit.Rd | 9 +- man/generic_sequential_fit.Rd | 9 +- man/inform_errors.Rd | 57 ++++++++++- man/keras_evaluate.Rd | 32 ++++-- man/model_exists.Rd | 33 ++++++ man/remove_keras_spec.Rd | 2 +- 19 files changed, 401 insertions(+), 76 deletions(-) create mode 100644 man/model_exists.Rd diff --git a/NAMESPACE b/NAMESPACE index 385f59f..46d821b 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -19,6 +19,7 @@ export(keras_losses) export(keras_metrics) export(keras_optimizers) export(loss_function_keras) +export(model_exists) export(optimizer_function) export(process_x_functional) export(process_x_sequential) diff --git a/R/compile_keras_grid.R b/R/compile_keras_grid.R index 259db24..a1d7457 100644 --- a/R/compile_keras_grid.R +++ b/R/compile_keras_grid.R @@ -42,35 +42,49 @@ #' @examples #' \donttest{ #' if (requireNamespace("keras3", quietly = TRUE)) { +#' library(keras3) +#' library(parsnip) +#' library(dials) #' -#' # 1. Define a kerasnip model specification +#' # 1. Define layer blocks +#' input_block <- function(model, input_shape) { +#' keras_model_sequential(input_shape = input_shape) +#' } +#' hidden_block <- function(model, units = 32) { +#' model |> layer_dense(units = units, activation = "relu") +#' } +#' output_block <- function(model, num_classes) { +#' model |> layer_dense(units = num_classes, activation = "softmax") +#' } +#' +#' # 2. Define a kerasnip model specification #' create_keras_sequential_spec( -#' model_name = "my_mlp", +#' model_name = "my_mlp_grid", #' layer_blocks = list( -#' input_block, -#' hidden_block, -#' output_block +#' input = input_block, +#' hidden = hidden_block, +#' output = output_block #' ), #' mode = "classification" #' ) #' -#' mlp_spec <- my_mlp( +#' mlp_spec <- my_mlp_grid( #' hidden_units = tune(), #' compile_loss = "categorical_crossentropy", #' compile_optimizer = "adam" #' ) #' -#' # 2. Create a hyperparameter grid +#' # 3. Create a hyperparameter grid #' # Include an invalid value (-10) to demonstrate error handling #' param_grid <- tibble::tibble( #' hidden_units = c(32, 64, -10) #' ) #' -#' # 3. Prepare dummy data +#' # 4. Prepare dummy data #' x_train <- matrix(rnorm(100 * 10), ncol = 10) #' y_train <- factor(sample(0:1, 100, replace = TRUE)) #' -#' # 4. Compile models over the grid +#' # 5. Compile models over the grid #' compiled_grid <- compile_keras_grid( #' spec = mlp_spec, #' grid = param_grid, @@ -79,8 +93,9 @@ #' ) #' #' print(compiled_grid) +#' remove_keras_spec("my_mlp_grid") #' -#' # 5. Inspect the results +#' # 6. Inspect the results #' # The row with `hidden_units = -10` will show an error. #' } #' } @@ -194,15 +209,61 @@ compile_keras_grid <- function(spec, grid, x, y) { #' #' @examples #' \donttest{ -#' # Continuing the example from `compile_keras_grid`: +#' if (requireNamespace("keras3", quietly = TRUE)) { +#' library(keras3) +#' library(parsnip) +#' library(dials) +#' +#' # 1. Define layer blocks +#' input_block <- function(model, input_shape) { +#' keras_model_sequential(input_shape = input_shape) +#' } +#' hidden_block <- function(model, units = 32) { +#' model |> layer_dense(units = units, activation = "relu") +#' } +#' output_block <- function(model, num_classes) { +#' model |> layer_dense(units = num_classes, activation = "softmax") +#' } +#' +#' # 2. Define a kerasnip model specification +#' create_keras_sequential_spec( +#' model_name = "my_mlp_grid_2", +#' layer_blocks = list( +#' input = input_block, +#' hidden = hidden_block, +#' output = output_block +#' ), +#' mode = "classification" +#' ) +#' +#' mlp_spec <- my_mlp_grid_2( +#' hidden_units = tune(), +#' compile_loss = "categorical_crossentropy", +#' compile_optimizer = "adam" +#' ) +#' +#' # 3. Create a hyperparameter grid +#' param_grid <- tibble::tibble( +#' hidden_units = c(32, 64, -10) +#' ) #' -#' # `compiled_grid` contains one row with an error. -#' valid_grid <- extract_valid_grid(compiled_grid) +#' # 4. Prepare dummy data +#' x_train <- matrix(rnorm(100 * 10), ncol = 10) +#' y_train <- factor(sample(0:1, 100, replace = TRUE)) #' -#' # `valid_grid` now only contains the rows that compiled successfully. -#' print(valid_grid) +#' # 5. Compile models over the grid +#' compiled_grid <- compile_keras_grid( +#' spec = mlp_spec, +#' grid = param_grid, +#' x = x_train, +#' y = y_train +#' ) #' -#' # This clean grid can now be passed to tune::tune_grid(). +#' # 6. Extract the valid grid +#' valid_grid <- extract_valid_grid(compiled_grid) +#' print(valid_grid) +#' remove_keras_spec("my_mlp_grid_2") +#' } #' } #' @export extract_valid_grid <- function(compiled_grid) { @@ -242,11 +303,60 @@ extract_valid_grid <- function(compiled_grid) { #' #' @examples #' \donttest{ -#' # Continuing the example from `compile_keras_grid`: +#' if (requireNamespace("keras3", quietly = TRUE)) { +#' library(keras3) +#' library(parsnip) +#' library(dials) +#' +#' # 1. Define layer blocks +#' input_block <- function(model, input_shape) { +#' keras_model_sequential(input_shape = input_shape) +#' } +#' hidden_block <- function(model, units = 32) { +#' model |> layer_dense(units = units, activation = "relu") +#' } +#' output_block <- function(model, num_classes) { +#' model |> layer_dense(units = num_classes, activation = "softmax") +#' } #' -#' # `compiled_grid` contains one row with an error. -#' # This will print a formatted summary of that error. -#' inform_errors(compiled_grid) +#' # 2. Define a kerasnip model specification +#' create_keras_sequential_spec( +#' model_name = "my_mlp_grid_3", +#' layer_blocks = list( +#' input = input_block, +#' hidden = hidden_block, +#' output = output_block +#' ), +#' mode = "classification" +#' ) +#' +#' mlp_spec <- my_mlp_grid_3( +#' hidden_units = tune(), +#' compile_loss = "categorical_crossentropy", +#' compile_optimizer = "adam" +#' ) +#' +#' # 3. Create a hyperparameter grid +#' param_grid <- tibble::tibble( +#' hidden_units = c(32, 64, -10) +#' ) +#' +#' # 4. Prepare dummy data +#' x_train <- matrix(rnorm(100 * 10), ncol = 10) +#' y_train <- factor(sample(0:1, 100, replace = TRUE)) +#' +#' # 5. Compile models over the grid +#' compiled_grid <- compile_keras_grid( +#' spec = mlp_spec, +#' grid = param_grid, +#' x = x_train, +#' y = y_train +#' ) +#' +#' # 6. Inform about errors +#' inform_errors(compiled_grid) +#' remove_keras_spec("my_mlp_grid_3") +#' } #' } #' @export inform_errors <- function(compiled_grid, n = 10) { diff --git a/R/create_keras_functional_spec.R b/R/create_keras_functional_spec.R index cbf476c..5103f70 100644 --- a/R/create_keras_functional_spec.R +++ b/R/create_keras_functional_spec.R @@ -119,6 +119,7 @@ #' # model_spec <- my_resnet_spec(num_dense_path = 2, dense_path_units = 32) #' #' print(model_spec) +#' remove_keras_spec("my_resnet_spec") #' # tune::tunable(model_spec) #' } #' } diff --git a/R/create_keras_sequential_spec.R b/R/create_keras_sequential_spec.R index de1bbce..0369a7d 100644 --- a/R/create_keras_sequential_spec.R +++ b/R/create_keras_sequential_spec.R @@ -84,7 +84,7 @@ #' #' # 2. Create the spec, providing blocks in the correct order. #' create_keras_sequential_spec( -#' model_name = "my_mlp", +#' model_name = "my_mlp_seq_spec", #' layer_blocks = list( #' input = input_block, #' hidden = hidden_block, @@ -95,7 +95,7 @@ #' #' # 3. Use the newly created specification function! #' # Note the new arguments `num_hidden` and `hidden_units`. -#' model_spec <- my_mlp( +#' model_spec <- my_mlp_seq_spec( #' num_hidden = 2, #' hidden_units = 64, #' epochs = 10, @@ -103,6 +103,7 @@ #' ) #' #' print(model_spec) +#' remove_keras_spec("my_mlp_seq_spec") #' } #' } create_keras_sequential_spec <- function( diff --git a/R/generic_functional_fit.R b/R/generic_functional_fit.R index 35864d9..7befb9f 100644 --- a/R/generic_functional_fit.R +++ b/R/generic_functional_fit.R @@ -57,13 +57,14 @@ #' # It is called internally by `parsnip::fit()`. #' # For example: #' \donttest{ +#' library(parsnip) #' # create_keras_functional_spec(...) defines my_functional_model #' -#' spec <- my_functional_model(hidden_units = 128, fit_epochs = 10) |> -#' set_engine("keras") +#' # spec <- my_functional_model(hidden_units = 128, fit_epochs = 10) |> +#' # set_engine("keras") #' -#' # This call to fit() would invoke generic_functional_fit() internally -#' fitted_model <- fit(spec, y ~ x, data = training_data) +#' # # This call to fit() would invoke generic_functional_fit() internally +#' # fitted_model <- fit(spec, y ~ x, data = training_data) #' } #' @keywords internal #' @export diff --git a/R/generic_sequential_fit.R b/R/generic_sequential_fit.R index be5da61..3f49ef7 100644 --- a/R/generic_sequential_fit.R +++ b/R/generic_sequential_fit.R @@ -57,13 +57,14 @@ #' # It is called internally by `parsnip::fit()`. #' # For example: #' \donttest{ +#' library(parsnip) #' # create_keras_sequential_spec(...) defines my_sequential_model #' -#' spec <- my_sequential_model(hidden_1_units = 128, fit_epochs = 10) |> -#' set_engine("keras") +#' # spec <- my_sequential_model(hidden_1_units = 128, fit_epochs = 10) |> +#' # set_engine("keras") #' -#' # This call to fit() would invoke generic_sequential_fit() internally -#' fitted_model <- fit(spec, y ~ x, data = training_data) +#' # # This call to fit() would invoke generic_sequential_fit() internally +#' # fitted_model <- fit(spec, y ~ x, data = training_data) #' } #' @keywords internal #' @export diff --git a/R/keras_tools.R b/R/keras_tools.R index f5cd5f0..2313ee8 100644 --- a/R/keras_tools.R +++ b/R/keras_tools.R @@ -20,15 +20,32 @@ #' @examples #' \donttest{ #' if (requireNamespace("keras3", quietly = TRUE)) { +#' library(keras3) +#' library(parsnip) #' -#' # 1. Define and fit a model ---- +#' # 1. Define layer blocks +#' input_block <- function(model, input_shape) { +#' keras_model_sequential(input_shape = input_shape) +#' } +#' hidden_block <- function(model, units = 32) { +#' model |> layer_dense(units = units, activation = "relu") +#' } +#' output_block <- function(model, num_classes) { +#' model |> layer_dense(units = num_classes, activation = "softmax") +#' } +#' +#' # 2. Define and fit a model ---- #' create_keras_sequential_spec( -#' model_name = "my_mlp", -#' layer_blocks = list(input_block, hidden_block, output_block), +#' model_name = "my_mlp_tools", +#' layer_blocks = list( +#' input = input_block, +#' hidden = hidden_block, +#' output = output_block +#' ), #' mode = "classification" #' ) #' -#' mlp_spec <- my_mlp( +#' mlp_spec <- my_mlp_tools( #' hidden_units = 32, #' compile_loss = "categorical_crossentropy", #' compile_optimizer = "adam", @@ -42,20 +59,21 @@ #' #' fitted_mlp <- fit(mlp_spec, y ~ x, data = train_df) #' -#' # 2. Evaluate the model on new data ---- +#' # 3. Evaluate the model on new data ---- #' x_test <- matrix(rnorm(50 * 10), ncol = 10) #' y_test <- factor(sample(0:1, 50, replace = TRUE)) #' #' eval_metrics <- keras_evaluate(fitted_mlp, x_test, y_test) #' print(eval_metrics) #' -#' # 3. Extract the Keras model object ---- +#' # 4. Extract the Keras model object ---- #' keras_model <- extract_keras_model(fitted_mlp) #' summary(keras_model) #' -#' # 4. Extract the training history ---- +#' # 5. Extract the training history ---- #' history <- extract_keras_history(fitted_mlp) #' plot(history) +#' remove_keras_spec("my_mlp_tools") #' } #' } #' @export diff --git a/R/remove_keras_spec.R b/R/remove_keras_spec.R index 54518c1..f0091bf 100644 --- a/R/remove_keras_spec.R +++ b/R/remove_keras_spec.R @@ -55,7 +55,7 @@ #' #' # Check it's gone #' !exists("my_temp_model") -#' !"my_temp_model" %in% parsnip::show_engines(NULL)$model +#' !model_exists("my_temp_model") #' } #' } remove_keras_spec <- function(model_name, env = parent.frame()) { diff --git a/R/utils.R b/R/utils.R index c3cd94d..c814a19 100644 --- a/R/utils.R +++ b/R/utils.R @@ -416,3 +416,30 @@ get_model_env <- function() { current <- utils::getFromNamespace("parsnip", ns = "parsnip") current } + +#' Check if a Kerasnip Model Specification Exists +#' +#' @description +#' This is an internal helper function to check if a model specification has been +#' registered in the `parsnip` model environment. +#' +#' @param model_name A character string giving the name of the model +#' specification function to check (e.g., "my_mlp"). +#' @return A logical value, `TRUE` if the model exists, `FALSE` otherwise. +#' @examples +#' \donttest{ +#' if (requireNamespace("parsnip", quietly = TRUE)) { +#' library(parsnip) +#' +#' # Check for a model that exists in parsnip +#' model_exists("mlp") +#' +#' # Check for a model that does not exist +#' model_exists("non_existent_model") +#' } +#' } +#' @keywords internal +#' @export +model_exists <- function(model_name) { + model_name %in% ls(get_model_env()) +} diff --git a/man/compile_keras_grid.Rd b/man/compile_keras_grid.Rd index 04f649c..57115ba 100644 --- a/man/compile_keras_grid.Rd +++ b/man/compile_keras_grid.Rd @@ -54,35 +54,49 @@ inspect which architectures are valid. \examples{ \donttest{ if (requireNamespace("keras3", quietly = TRUE)) { +library(keras3) +library(parsnip) +library(dials) -# 1. Define a kerasnip model specification +# 1. Define layer blocks +input_block <- function(model, input_shape) { + keras_model_sequential(input_shape = input_shape) +} +hidden_block <- function(model, units = 32) { + model |> layer_dense(units = units, activation = "relu") +} +output_block <- function(model, num_classes) { + model |> layer_dense(units = num_classes, activation = "softmax") +} + +# 2. Define a kerasnip model specification create_keras_sequential_spec( - model_name = "my_mlp", + model_name = "my_mlp_grid", layer_blocks = list( - input_block, - hidden_block, - output_block + input = input_block, + hidden = hidden_block, + output = output_block ), mode = "classification" ) -mlp_spec <- my_mlp( +mlp_spec <- my_mlp_grid( hidden_units = tune(), compile_loss = "categorical_crossentropy", compile_optimizer = "adam" ) -# 2. Create a hyperparameter grid +# 3. Create a hyperparameter grid # Include an invalid value (-10) to demonstrate error handling param_grid <- tibble::tibble( hidden_units = c(32, 64, -10) ) -# 3. Prepare dummy data +# 4. Prepare dummy data x_train <- matrix(rnorm(100 * 10), ncol = 10) y_train <- factor(sample(0:1, 100, replace = TRUE)) -# 4. Compile models over the grid +# 5. Compile models over the grid compiled_grid <- compile_keras_grid( spec = mlp_spec, grid = param_grid, @@ -91,8 +105,9 @@ compiled_grid <- compile_keras_grid( ) print(compiled_grid) +remove_keras_spec("my_mlp_grid") -# 5. Inspect the results +# 6. Inspect the results # The row with `hidden_units = -10` will show an error. } } diff --git a/man/create_keras_functional_spec.Rd b/man/create_keras_functional_spec.Rd index 5a1c629..ba41f23 100644 --- a/man/create_keras_functional_spec.Rd +++ b/man/create_keras_functional_spec.Rd @@ -136,6 +136,7 @@ if (requireNamespace("keras3", quietly = TRUE)) { # model_spec <- my_resnet_spec(num_dense_path = 2, dense_path_units = 32) print(model_spec) + remove_keras_spec("my_resnet_spec") # tune::tunable(model_spec) } } diff --git a/man/create_keras_sequential_spec.Rd b/man/create_keras_sequential_spec.Rd index b7c061c..54e1228 100644 --- a/man/create_keras_sequential_spec.Rd +++ b/man/create_keras_sequential_spec.Rd @@ -98,7 +98,7 @@ output_block <- function(model, num_classes) { # 2. Create the spec, providing blocks in the correct order. create_keras_sequential_spec( -model_name = "my_mlp", +model_name = "my_mlp_seq_spec", layer_blocks = list( input = input_block, hidden = hidden_block, @@ -109,7 +109,7 @@ model_name = "my_mlp", # 3. Use the newly created specification function! # Note the new arguments `num_hidden` and `hidden_units`. -model_spec <- my_mlp( +model_spec <- my_mlp_seq_spec( num_hidden = 2, hidden_units = 64, epochs = 10, @@ -117,6 +117,7 @@ model_spec <- my_mlp( ) print(model_spec) +remove_keras_spec("my_mlp_seq_spec") } } } diff --git a/man/extract_valid_grid.Rd b/man/extract_valid_grid.Rd index ff12a80..1befbec 100644 --- a/man/extract_valid_grid.Rd +++ b/man/extract_valid_grid.Rd @@ -28,14 +28,60 @@ problematic hyperparameter combinations before proceeding to the full } \examples{ \donttest{ -# Continuing the example from `compile_keras_grid`: +if (requireNamespace("keras3", quietly = TRUE)) { + library(keras3) + library(parsnip) + library(dials) -# `compiled_grid` contains one row with an error. -valid_grid <- extract_valid_grid(compiled_grid) + # 1. Define layer blocks + input_block <- function(model, input_shape) { + keras_model_sequential(input_shape = input_shape) + } + hidden_block <- function(model, units = 32) { + model |> layer_dense(units = units, activation = "relu") + } + output_block <- function(model, num_classes) { + model |> layer_dense(units = num_classes, activation = "softmax") + } -# `valid_grid` now only contains the rows that compiled successfully. -print(valid_grid) + # 2. Define a kerasnip model specification + create_keras_sequential_spec( + model_name = "my_mlp_grid_2", + layer_blocks = list( + input = input_block, + hidden = hidden_block, + output = output_block + ), + mode = "classification" + ) -# This clean grid can now be passed to tune::tune_grid(). + mlp_spec <- my_mlp_grid_2( + hidden_units = tune(), + compile_loss = "categorical_crossentropy", + compile_optimizer = "adam" + ) + + # 3. Create a hyperparameter grid + param_grid <- tibble::tibble( + hidden_units = c(32, 64, -10) + ) + + # 4. Prepare dummy data + x_train <- matrix(rnorm(100 * 10), ncol = 10) + y_train <- factor(sample(0:1, 100, replace = TRUE)) + + # 5. Compile models over the grid + compiled_grid <- compile_keras_grid( + spec = mlp_spec, + grid = param_grid, + x = x_train, + y = y_train + ) + + # 6. Extract the valid grid + valid_grid <- extract_valid_grid(compiled_grid) + print(valid_grid) + remove_keras_spec("my_mlp_grid_2") +} } } diff --git a/man/generic_functional_fit.Rd b/man/generic_functional_fit.Rd index 333d558..2bb9388 100644 --- a/man/generic_functional_fit.Rd +++ b/man/generic_functional_fit.Rd @@ -69,13 +69,14 @@ and processed data, passing along any fitting-specific arguments (e.g., # It is called internally by `parsnip::fit()`. # For example: \donttest{ +library(parsnip) # create_keras_functional_spec(...) defines my_functional_model -spec <- my_functional_model(hidden_units = 128, fit_epochs = 10) |> - set_engine("keras") +# spec <- my_functional_model(hidden_units = 128, fit_epochs = 10) |> +# set_engine("keras") -# This call to fit() would invoke generic_functional_fit() internally -fitted_model <- fit(spec, y ~ x, data = training_data) +# # This call to fit() would invoke generic_functional_fit() internally +# fitted_model <- fit(spec, y ~ x, data = training_data) } } \keyword{internal} diff --git a/man/generic_sequential_fit.Rd b/man/generic_sequential_fit.Rd index d35d6f9..523a533 100644 --- a/man/generic_sequential_fit.Rd +++ b/man/generic_sequential_fit.Rd @@ -69,13 +69,14 @@ and processed data, passing along any fitting-specific arguments (e.g., # It is called internally by `parsnip::fit()`. # For example: \donttest{ +library(parsnip) # create_keras_sequential_spec(...) defines my_sequential_model -spec <- my_sequential_model(hidden_1_units = 128, fit_epochs = 10) |> - set_engine("keras") +# spec <- my_sequential_model(hidden_1_units = 128, fit_epochs = 10) |> +# set_engine("keras") -# This call to fit() would invoke generic_sequential_fit() internally -fitted_model <- fit(spec, y ~ x, data = training_data) +# # This call to fit() would invoke generic_sequential_fit() internally +# fitted_model <- fit(spec, y ~ x, data = training_data) } } \keyword{internal} diff --git a/man/inform_errors.Rd b/man/inform_errors.Rd index c892bbd..cd23035 100644 --- a/man/inform_errors.Rd +++ b/man/inform_errors.Rd @@ -29,10 +29,59 @@ some hyperparameter combinations may lead to invalid Keras models. } \examples{ \donttest{ -# Continuing the example from `compile_keras_grid`: +if (requireNamespace("keras3", quietly = TRUE)) { + library(keras3) + library(parsnip) + library(dials) -# `compiled_grid` contains one row with an error. -# This will print a formatted summary of that error. -inform_errors(compiled_grid) + # 1. Define layer blocks + input_block <- function(model, input_shape) { + keras_model_sequential(input_shape = input_shape) + } + hidden_block <- function(model, units = 32) { + model |> layer_dense(units = units, activation = "relu") + } + output_block <- function(model, num_classes) { + model |> layer_dense(units = num_classes, activation = "softmax") + } + + # 2. Define a kerasnip model specification + create_keras_sequential_spec( + model_name = "my_mlp_grid_3", + layer_blocks = list( + input = input_block, + hidden = hidden_block, + output = output_block + ), + mode = "classification" + ) + + mlp_spec <- my_mlp_grid_3( + hidden_units = tune(), + compile_loss = "categorical_crossentropy", + compile_optimizer = "adam" + ) + + # 3. Create a hyperparameter grid + param_grid <- tibble::tibble( + hidden_units = c(32, 64, -10) + ) + + # 4. Prepare dummy data + x_train <- matrix(rnorm(100 * 10), ncol = 10) + y_train <- factor(sample(0:1, 100, replace = TRUE)) + + # 5. Compile models over the grid + compiled_grid <- compile_keras_grid( + spec = mlp_spec, + grid = param_grid, + x = x_train, + y = y_train + ) + + # 6. Inform about errors + inform_errors(compiled_grid) + remove_keras_spec("my_mlp_grid_3") +} } } diff --git a/man/keras_evaluate.Rd b/man/keras_evaluate.Rd index 1970e43..9df1aea 100644 --- a/man/keras_evaluate.Rd +++ b/man/keras_evaluate.Rd @@ -33,15 +33,32 @@ Evaluate a Fitted Kerasnip Model on New Data \examples{ \donttest{ if (requireNamespace("keras3", quietly = TRUE)) { +library(keras3) +library(parsnip) -# 1. Define and fit a model ---- +# 1. Define layer blocks +input_block <- function(model, input_shape) { + keras_model_sequential(input_shape = input_shape) +} +hidden_block <- function(model, units = 32) { + model |> layer_dense(units = units, activation = "relu") +} +output_block <- function(model, num_classes) { + model |> layer_dense(units = num_classes, activation = "softmax") +} + +# 2. Define and fit a model ---- create_keras_sequential_spec( - model_name = "my_mlp", - layer_blocks = list(input_block, hidden_block, output_block), + model_name = "my_mlp_tools", + layer_blocks = list( + input = input_block, + hidden = hidden_block, + output = output_block + ), mode = "classification" ) -mlp_spec <- my_mlp( +mlp_spec <- my_mlp_tools( hidden_units = 32, compile_loss = "categorical_crossentropy", compile_optimizer = "adam", @@ -55,20 +72,21 @@ train_df <- data.frame(x = I(x_train), y = y_train) fitted_mlp <- fit(mlp_spec, y ~ x, data = train_df) -# 2. Evaluate the model on new data ---- +# 3. Evaluate the model on new data ---- x_test <- matrix(rnorm(50 * 10), ncol = 10) y_test <- factor(sample(0:1, 50, replace = TRUE)) eval_metrics <- keras_evaluate(fitted_mlp, x_test, y_test) print(eval_metrics) -# 3. Extract the Keras model object ---- +# 4. Extract the Keras model object ---- keras_model <- extract_keras_model(fitted_mlp) summary(keras_model) -# 4. Extract the training history ---- +# 5. Extract the training history ---- history <- extract_keras_history(fitted_mlp) plot(history) +remove_keras_spec("my_mlp_tools") } } } diff --git a/man/model_exists.Rd b/man/model_exists.Rd new file mode 100644 index 0000000..17bfc93 --- /dev/null +++ b/man/model_exists.Rd @@ -0,0 +1,33 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/utils.R +\name{model_exists} +\alias{model_exists} +\title{Check if a Kerasnip Model Specification Exists} +\usage{ +model_exists(model_name) +} +\arguments{ +\item{model_name}{A character string giving the name of the model +specification function to check (e.g., "my_mlp").} +} +\value{ +A logical value, \code{TRUE} if the model exists, \code{FALSE} otherwise. +} +\description{ +This is an internal helper function to check if a model specification has been +registered in the \code{parsnip} model environment. +} +\examples{ +\donttest{ +if (requireNamespace("parsnip", quietly = TRUE)) { + library(parsnip) + + # Check for a model that exists in parsnip + model_exists("mlp") + + # Check for a model that does not exist + model_exists("non_existent_model") +} +} +} +\keyword{internal} diff --git a/man/remove_keras_spec.Rd b/man/remove_keras_spec.Rd index 6e5fa36..1062faf 100644 --- a/man/remove_keras_spec.Rd +++ b/man/remove_keras_spec.Rd @@ -64,7 +64,7 @@ if (requireNamespace("keras3", quietly = TRUE)) { # Check it's gone !exists("my_temp_model") - !"my_temp_model" \%in\% parsnip::show_engines(NULL)$model + !model_exists("my_temp_model") } } } From eff9d5bc996d2d3305160628d3a96a2f1be81095 Mon Sep 17 00:00:00 2001 From: davidrsch Date: Thu, 4 Sep 2025 15:45:32 +0200 Subject: [PATCH 2/4] Fix for macos check error? --- .github/workflows/R-CMD-check.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index a46eb86..6a023ca 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -41,6 +41,7 @@ jobs: env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} R_KEEP_PKG_SOURCE: yes + OMP_NUM_THREADS: 1 steps: - uses: actions/checkout@v4 From b7ac2de5d53f459dba50362960a5e5a2cdbfbf7d Mon Sep 17 00:00:00 2001 From: davidrsch Date: Thu, 4 Sep 2025 16:01:09 +0200 Subject: [PATCH 3/4] Adding more Badges to readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 33b7036..8226fc7 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://lifecycle.r-lib.org/articles/stages.html#experimental) [![R-CMD-check](https://github.com/davidrsch/kerasnip/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/davidrsch/kerasnip/actions/workflows/R-CMD-check.yaml) +[![Codecov test +coverage](https://codecov.io/gh/davidrsch/kerasnip/graph/badge.svg)](https://app.codecov.io/gh/davidrsch/kerasnip) +[![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/kerasnip)](https://cran.r-project.org/package=kerasnip) +[![Downloads](https://cranlogs.r-pkg.org/badges/kerasnip)](https://cran.r-project.org/package=kerasnip) The goal of `kerasnip` is to provide a seamless bridge between the `keras` and `tidymodels` frameworks. It allows for the dynamic creation of `parsnip` model specifications for Keras models, making them fully compatible with `tidymodels` workflows. From a0314fd06e2e1f42092674a4bf2935014bbd1c1a Mon Sep 17 00:00:00 2001 From: davidrsch Date: Thu, 4 Sep 2025 16:01:27 +0200 Subject: [PATCH 4/4] Trying to fix macOS checks --- .github/workflows/R-CMD-check.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 6a023ca..e0b85a8 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -41,7 +41,6 @@ jobs: env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} R_KEEP_PKG_SOURCE: yes - OMP_NUM_THREADS: 1 steps: - uses: actions/checkout@v4 @@ -81,7 +80,7 @@ jobs: - name: Install Keras backend # The keras3 package is installed by the previous step (as a dependency) # This step installs the required Python libraries (e.g., tensorflow). - run: keras3::install_keras() + run: keras3::install_keras(envname = "r-reticulate") shell: Rscript {0} - uses: r-lib/actions/check-r-package@v2