---
title: "Tuning Fit and Compile Arguments"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Tuning Fit and Compile Arguments}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---



## Introduction

While `kerasnip` makes it easy to tune the architecture of a Keras model (e.g., the number of layers or the number of units in a layer), it is often just as important to tune the parameters that control the training process itself. `kerasnip` exposes these parameters through special `fit_*` and `compile_*` arguments in the model specification.

This vignette provides a comprehensive example of how to tune these arguments within a `tidymodels` workflow. We will tune:

*   **`fit_epochs`**: The number of training epochs.
*   **`fit_batch_size`**: The number of samples per gradient update.
*   **`compile_optimizer`**: The optimization algorithm (e.g., "adam", "sgd").
*   **`compile_loss`**: The loss function used for training.
*   **`learn_rate`**: The learning rate for the optimizer.

Fit arguments can be written either with the `fit_` prefix (`fit_epochs`) or
without it (`epochs`), matching the names used by other tidymodels
neural-network engines. When both are supplied, the `fit_` version takes
precedence.

## Setup

First, we load the necessary packages.


``` r
library(kerasnip)
library(tidymodels)
#> ── Attaching packages ───────────────── tidymodels 1.5.0 ──
#> ✔ broom        1.0.13     ✔ recipes      1.3.3 
#> ✔ dials        1.4.4      ✔ rsample      1.3.2 
#> ✔ dplyr        1.2.1      ✔ tailor       0.1.0 
#> ✔ ggplot2      4.0.3      ✔ tidyr        1.3.2 
#> ✔ infer        1.1.0      ✔ tune         2.1.0 
#> ✔ modeldata    1.5.1      ✔ workflows    1.3.0 
#> ✔ parsnip      1.6.0      ✔ workflowsets 1.1.1 
#> ✔ purrr        1.2.2      ✔ yardstick    1.4.0
#> ── Conflicts ──────────────────── tidymodels_conflicts() ──
#> ✖ purrr::discard()         masks scales::discard()
#> ✖ dplyr::filter()          masks stats::filter()
#> ✖ parsnip::get_model_env() masks kerasnip::get_model_env()
#> ✖ dplyr::lag()             masks stats::lag()
#> ✖ recipes::step()          masks stats::step()
library(keras3)
#> 
#> Attaching package: 'keras3'
#> The following object is masked from 'package:yardstick':
#> 
#>     get_weights
#> The following object is masked from 'package:infer':
#> 
#>     generate
```

## Data Preparation

We will use the classic `iris` dataset for this example. It's a simple, small dataset, which is ideal for demonstrating the tuning process without long training times.


``` r
# Split data into training and testing sets
set.seed(123)
iris_split <- initial_split(iris, prop = 0.8, strata = Species)
iris_train <- training(iris_split)
iris_test <- testing(iris_split)

# Create cross-validation folds for tuning
iris_folds <- vfold_cv(iris_train, v = 3, strata = Species)
```

## Define a `kerasnip` Model

We'll create a very simple sequential model with a single dense layer. This keeps the focus on tuning the `fit_*` and `compile_*` arguments rather than the model architecture.


``` r
# Define layer blocks
input_block <- function(model, input_shape) {
  keras_model_sequential(input_shape = input_shape)
}
dense_block <- function(model, units = 10) {
  model |> layer_dense(units = units, activation = "relu")
}
output_block <- function(model, num_classes) {
  model |> layer_dense(units = num_classes, activation = "softmax")
}

# Create the kerasnip model specification function
create_keras_sequential_spec(
  model_name = "iris_mlp",
  layer_blocks = list(
    input = input_block,
    dense = dense_block,
    output = output_block
  ),
  mode = "classification"
)
```

## Define the Tunable Specification

Now, we create an instance of our `iris_mlp` model. We set the arguments we want to optimize to `tune()`.


``` r
# Define the tunable model specification
tune_spec <- iris_mlp(
  dense_units = 16, # Keep architecture fixed for this example
  fit_epochs = tune(),
  fit_batch_size = tune(),
  compile_optimizer = tune(),
  compile_loss = tune(),
  learn_rate = tune()
) |>
  set_engine("keras")

print(tune_spec)
#> iris mlp Model Specification (classification)
#> 
#> Main Arguments:
#>   num_input = structure(list(), class = "rlang_zap")
#>   num_dense = structure(list(), class = "rlang_zap")
#>   num_output = structure(list(), class = "rlang_zap")
#>   dense_units = 16
#>   learn_rate = tune()
#>   fit_batch_size = tune()
#>   fit_epochs = tune()
#>   fit_callbacks = structure(list(), class = "rlang_zap")
#>   fit_validation_split = structure(list(), class = "rlang_zap")
#>   fit_validation_data = structure(list(), class = "rlang_zap")
#>   fit_shuffle = structure(list(), class = "rlang_zap")
#>   fit_class_weight = structure(list(), class = "rlang_zap")
#>   fit_sample_weight = structure(list(), class = "rlang_zap")
#>   fit_initial_epoch = structure(list(), class = "rlang_zap")
#>   fit_steps_per_epoch = structure(list(), class = "rlang_zap")
#>   fit_validation_steps = structure(list(), class = "rlang_zap")
#>   fit_validation_batch_size = structure(list(), class = "rlang_zap")
#>   fit_validation_freq = structure(list(), class = "rlang_zap")
#>   fit_verbose = structure(list(), class = "rlang_zap")
#>   fit_view_metrics = structure(list(), class = "rlang_zap")
#>   compile_optimizer = tune()
#>   compile_loss = tune()
#>   compile_metrics = structure(list(), class = "rlang_zap")
#>   compile_loss_weights = structure(list(), class = "rlang_zap")
#>   compile_weighted_metrics = structure(list(), class = "rlang_zap")
#>   compile_run_eagerly = structure(list(), class = "rlang_zap")
#>   compile_steps_per_execution = structure(list(), class = "rlang_zap")
#>   compile_jit_compile = structure(list(), class = "rlang_zap")
#>   compile_auto_scale_loss = structure(list(), class = "rlang_zap")
#> 
#> Computational engine: keras
```

## Create Workflow and Tuning Grid

Next, we create a `workflow` and define the search space for our hyperparameters using `dials`. `kerasnip` provides special `dials` parameter functions for `optimizer` and `loss`.


``` r
# Create a simple recipe
iris_recipe <- recipe(Species ~ ., data = iris_train) |>
  step_normalize(all_numeric_predictors())

# Create the workflow
tune_wf <- workflow() |>
  add_recipe(iris_recipe) |>
  add_model(tune_spec)

# Define the tuning grid
params <- extract_parameter_set_dials(tune_wf) |>
  update(
    fit_epochs = epochs(c(10, 30)),
    fit_batch_size = batch_size(c(16, 64), trans = NULL),
    compile_optimizer = optimizer_function(values = c("adam", "sgd", "rmsprop")),
    compile_loss = loss_function_keras(values = c("categorical_crossentropy", "kl_divergence")),
    learn_rate = learn_rate(c(0.001, 0.01), trans = NULL)
  )

set.seed(456)
tuning_grid <- grid_regular(params, levels = 2)

tuning_grid
#> # A tibble: 32 × 5
#>    learn_rate fit_batch_size fit_epochs compile_optimizer compile_loss            
#>         <dbl>          <int>      <int> <chr>             <chr>                   
#>  1      0.001             16         10 adam              categorical_crossentropy
#>  2      0.01              16         10 adam              categorical_crossentropy
#>  3      0.001             64         10 adam              categorical_crossentropy
#>  4      0.01              64         10 adam              categorical_crossentropy
#>  5      0.001             16         30 adam              categorical_crossentropy
#>  6      0.01              16         30 adam              categorical_crossentropy
#>  7      0.001             64         30 adam              categorical_crossentropy
#>  8      0.01              64         30 adam              categorical_crossentropy
#>  9      0.001             16         10 sgd               categorical_crossentropy
#> 10      0.01              16         10 sgd               categorical_crossentropy
#> # ℹ 22 more rows
```

## Tune the Model

With the workflow and grid defined, we can now run the hyperparameter tuning using `tune_grid()`.


``` r
tune_res <- tune_grid(
  tune_wf,
  resamples = iris_folds,
  grid = tuning_grid,
  metrics = metric_set(accuracy, roc_auc),
  control = control_grid(save_pred = FALSE, save_workflow = TRUE, verbose = FALSE)
)
#> 3/3 - 0s - 51ms/step
#> 2/2 - 0s - 68ms/step
#> 2/2 - 0s - 27ms/step
#> 3/3 - 0s - 38ms/step
#> 2/2 - 0s - 100ms/step
#> 2/2 - 0s - 55ms/step
#> 3/3 - 0s - 47ms/step
#> 2/2 - 0s - 69ms/step
#> 2/2 - 0s - 31ms/step
#> 3/3 - 0s - 43ms/step
#> 2/2 - 0s - 110ms/step
#> 2/2 - 0s - 72ms/step
#> 3/3 - 0s - 49ms/step
#> 2/2 - 0s - 56ms/step
#> 2/2 - 0s - 31ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 51ms/step
#> 2/2 - 0s - 22ms/step
#> 3/3 - 0s - 48ms/step
#> 2/2 - 0s - 74ms/step
#> 2/2 - 0s - 30ms/step
#> 3/3 - 0s - 100ms/step
#> 2/2 - 0s - 40ms/step
#> 2/2 - 0s - 24ms/step
#> 3/3 - 0s - 35ms/step
#> 2/2 - 0s - 48ms/step
#> 2/2 - 0s - 26ms/step
#> 3/3 - 0s - 42ms/step
#> 2/2 - 0s - 59ms/step
#> 2/2 - 0s - 31ms/step
#> 3/3 - 0s - 37ms/step
#> 2/2 - 0s - 56ms/step
#> 2/2 - 0s - 38ms/step
#> 3/3 - 0s - 37ms/step
#> 2/2 - 0s - 79ms/step
#> 2/2 - 0s - 32ms/step
#> 3/3 - 0s - 37ms/step
#> 2/2 - 0s - 62ms/step
#> 2/2 - 0s - 31ms/step
#> 3/3 - 0s - 37ms/step
#> 2/2 - 0s - 54ms/step
#> 2/2 - 0s - 26ms/step
#> 3/3 - 0s - 45ms/step
#> 2/2 - 0s - 65ms/step
#> 2/2 - 0s - 26ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 48ms/step
#> 2/2 - 0s - 25ms/step
#> 3/3 - 0s - 38ms/step
#> 2/2 - 0s - 51ms/step
#> 2/2 - 0s - 28ms/step
#> 3/3 - 0s - 55ms/step
#> 2/2 - 0s - 60ms/step
#> 2/2 - 0s - 38ms/step
#> 3/3 - 0s - 35ms/step
#> 2/2 - 0s - 55ms/step
#> 2/2 - 0s - 24ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 53ms/step
#> 2/2 - 0s - 23ms/step
#> 3/3 - 0s - 39ms/step
#> 2/2 - 0s - 56ms/step
#> 2/2 - 0s - 25ms/step
#> 3/3 - 0s - 39ms/step
#> 2/2 - 0s - 56ms/step
#> 2/2 - 0s - 25ms/step
#> 3/3 - 0s - 39ms/step
#> 2/2 - 0s - 50ms/step
#> 2/2 - 0s - 20ms/step
#> 3/3 - 0s - 35ms/step
#> 2/2 - 0s - 62ms/step
#> 2/2 - 0s - 22ms/step
#> 3/3 - 0s - 39ms/step
#> 2/2 - 0s - 53ms/step
#> 2/2 - 0s - 29ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 58ms/step
#> 2/2 - 0s - 25ms/step
#> 3/3 - 0s - 41ms/step
#> 2/2 - 0s - 66ms/step
#> 2/2 - 0s - 22ms/step
#> 3/3 - 0s - 39ms/step
#> 2/2 - 0s - 63ms/step
#> 2/2 - 0s - 24ms/step
#> 3/3 - 0s - 57ms/step
#> 2/2 - 0s - 52ms/step
#> 2/2 - 0s - 30ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 52ms/step
#> 2/2 - 0s - 24ms/step
#> 3/3 - 0s - 33ms/step
#> 2/2 - 0s - 51ms/step
#> 2/2 - 0s - 27ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 54ms/step
#> 2/2 - 0s - 22ms/step
#> 3/3 - 0s - 28ms/step
#> 2/2 - 0s - 45ms/step
#> 2/2 - 0s - 21ms/step
#> 3/3 - 0s - 44ms/step
#> 2/2 - 0s - 52ms/step
#> 2/2 - 0s - 24ms/step
#> 3/3 - 0s - 39ms/step
#> 2/2 - 0s - 57ms/step
#> 2/2 - 0s - 27ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 47ms/step
#> 2/2 - 0s - 23ms/step
#> 3/3 - 0s - 33ms/step
#> 2/2 - 0s - 57ms/step
#> 2/2 - 0s - 25ms/step
#> 3/3 - 0s - 38ms/step
#> 2/2 - 0s - 52ms/step
#> 2/2 - 0s - 25ms/step
#> 3/3 - 0s - 34ms/step
#> 2/2 - 0s - 47ms/step
#> 2/2 - 0s - 24ms/step
#> 3/3 - 0s - 45ms/step
#> 2/2 - 0s - 53ms/step
#> 2/2 - 0s - 28ms/step
#> 3/3 - 0s - 35ms/step
#> 2/2 - 0s - 58ms/step
#> 2/2 - 0s - 26ms/step
#> 3/3 - 0s - 37ms/step
#> 2/2 - 0s - 55ms/step
#> 2/2 - 0s - 24ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 56ms/step
#> 2/2 - 0s - 27ms/step
#> 3/3 - 0s - 28ms/step
#> 2/2 - 0s - 50ms/step
#> 2/2 - 0s - 21ms/step
#> 3/3 - 0s - 37ms/step
#> 2/2 - 0s - 53ms/step
#> 2/2 - 0s - 28ms/step
#> 3/3 - 0s - 35ms/step
#> 2/2 - 0s - 61ms/step
#> 2/2 - 0s - 26ms/step
#> 3/3 - 0s - 76ms/step
#> 2/2 - 0s - 88ms/step
#> 2/2 - 0s - 32ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 61ms/step
#> 2/2 - 0s - 23ms/step
#> 3/3 - 0s - 40ms/step
#> 2/2 - 0s - 49ms/step
#> 2/2 - 0s - 23ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 50ms/step
#> 2/2 - 0s - 19ms/step
#> 3/3 - 0s - 35ms/step
#> 2/2 - 0s - 51ms/step
#> 2/2 - 0s - 22ms/step
#> 3/3 - 0s - 34ms/step
#> 2/2 - 0s - 49ms/step
#> 2/2 - 0s - 29ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 52ms/step
#> 2/2 - 0s - 22ms/step
#> 3/3 - 0s - 34ms/step
#> 2/2 - 0s - 47ms/step
#> 2/2 - 0s - 28ms/step
#> 3/3 - 0s - 35ms/step
#> 2/2 - 0s - 51ms/step
#> 2/2 - 0s - 23ms/step
#> 3/3 - 0s - 35ms/step
#> 2/2 - 0s - 50ms/step
#> 2/2 - 0s - 26ms/step
#> 3/3 - 0s - 30ms/step
#> 2/2 - 0s - 53ms/step
#> 2/2 - 0s - 26ms/step
#> 3/3 - 0s - 31ms/step
#> 2/2 - 0s - 49ms/step
#> 2/2 - 0s - 20ms/step
#> 3/3 - 0s - 39ms/step
#> 2/2 - 0s - 49ms/step
#> 2/2 - 0s - 25ms/step
#> 3/3 - 0s - 30ms/step
#> 2/2 - 0s - 54ms/step
#> 2/2 - 0s - 23ms/step
#> 3/3 - 0s - 33ms/step
#> 2/2 - 0s - 51ms/step
#> 2/2 - 0s - 22ms/step
#> 3/3 - 0s - 38ms/step
#> 2/2 - 0s - 51ms/step
#> 2/2 - 0s - 19ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 50ms/step
#> 2/2 - 0s - 22ms/step
#> 3/3 - 0s - 38ms/step
#> 2/2 - 0s - 39ms/step
#> 2/2 - 0s - 26ms/step
#> 3/3 - 0s - 37ms/step
#> 2/2 - 0s - 51ms/step
#> 2/2 - 0s - 24ms/step
#> 3/3 - 0s - 42ms/step
#> 2/2 - 0s - 55ms/step
#> 2/2 - 0s - 23ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 46ms/step
#> 2/2 - 0s - 27ms/step
#> 3/3 - 0s - 37ms/step
#> 2/2 - 0s - 54ms/step
#> 2/2 - 0s - 23ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 57ms/step
#> 2/2 - 0s - 30ms/step
#> 3/3 - 0s - 39ms/step
#> 2/2 - 0s - 53ms/step
#> 2/2 - 0s - 22ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 55ms/step
#> 2/2 - 0s - 25ms/step
#> 3/3 - 0s - 38ms/step
#> 2/2 - 0s - 58ms/step
#> 2/2 - 0s - 25ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 49ms/step
#> 2/2 - 0s - 25ms/step
#> 3/3 - 0s - 38ms/step
#> 2/2 - 0s - 65ms/step
#> 2/2 - 0s - 23ms/step
#> 3/3 - 0s - 35ms/step
#> 2/2 - 0s - 54ms/step
#> 2/2 - 0s - 27ms/step
#> 3/3 - 0s - 40ms/step
#> 2/2 - 0s - 56ms/step
#> 2/2 - 0s - 20ms/step
#> 3/3 - 0s - 35ms/step
#> 2/2 - 0s - 54ms/step
#> 2/2 - 0s - 20ms/step
#> 3/3 - 0s - 35ms/step
#> 2/2 - 0s - 54ms/step
#> 2/2 - 0s - 19ms/step
#> 3/3 - 0s - 27ms/step
#> 2/2 - 0s - 50ms/step
#> 2/2 - 0s - 24ms/step
#> 3/3 - 0s - 31ms/step
#> 2/2 - 0s - 69ms/step
#> 2/2 - 0s - 23ms/step
#> 3/3 - 0s - 34ms/step
#> 2/2 - 0s - 47ms/step
#> 2/2 - 0s - 18ms/step
#> 3/3 - 0s - 33ms/step
#> 2/2 - 0s - 46ms/step
#> 2/2 - 0s - 22ms/step
#> 3/3 - 0s - 34ms/step
#> 2/2 - 0s - 45ms/step
#> 2/2 - 0s - 21ms/step
#> 3/3 - 0s - 34ms/step
#> 2/2 - 0s - 52ms/step
#> 2/2 - 0s - 19ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 62ms/step
#> 2/2 - 0s - 25ms/step
#> 3/3 - 0s - 33ms/step
#> 2/2 - 0s - 53ms/step
#> 2/2 - 0s - 22ms/step
#> 3/3 - 0s - 33ms/step
#> 2/2 - 0s - 49ms/step
#> 2/2 - 0s - 23ms/step
#> 3/3 - 0s - 39ms/step
#> 2/2 - 0s - 48ms/step
#> 2/2 - 0s - 21ms/step
#> 3/3 - 0s - 38ms/step
#> 2/2 - 0s - 51ms/step
#> 2/2 - 0s - 33ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 65ms/step
#> 2/2 - 0s - 24ms/step
#> 3/3 - 0s - 40ms/step
#> 2/2 - 0s - 54ms/step
#> 2/2 - 0s - 29ms/step
#> 3/3 - 0s - 38ms/step
#> 2/2 - 0s - 51ms/step
#> 2/2 - 0s - 32ms/step
#> 3/3 - 0s - 36ms/step
#> 2/2 - 0s - 52ms/step
#> 2/2 - 0s - 25ms/step
#> 3/3 - 0s - 59ms/step
#> 2/2 - 0s - 37ms/step
#> 2/2 - 0s - 21ms/step
#> 3/3 - 0s - 37ms/step
#> 2/2 - 0s - 55ms/step
#> 2/2 - 0s - 20ms/step
#> 3/3 - 0s - 35ms/step
#> 2/2 - 0s - 59ms/step
#> 2/2 - 0s - 22ms/step
```

## Inspect the Results

Let's examine the results to see how the different combinations of fitting and compilation parameters performed.


``` r
# Show the best performing models based on accuracy
show_best(tune_res, metric = "accuracy")
#> # A tibble: 5 × 11
#>   learn_rate fit_batch_size fit_epochs compile_optimizer compile_loss     .metric .estimator  mean     n std_err .config
#>        <dbl>          <int>      <int> <chr>             <chr>            <chr>   <chr>      <dbl> <int>   <dbl> <chr>  
#> 1       0.01             16         30 adam              categorical_cro… accura… multiclass 0.951     3 0.0132  pre0_m…
#> 2       0.01             16         30 adam              kl_divergence    accura… multiclass 0.951     3 0.0132  pre0_m…
#> 3       0.01             64         30 adam              categorical_cro… accura… multiclass 0.942     3 0.00672 pre0_m…
#> 4       0.01             64         30 adam              kl_divergence    accura… multiclass 0.918     3 0.0305  pre0_m…
#> 5       0.01             16         10 adam              kl_divergence    accura… multiclass 0.901     3 0.0220  pre0_m…

# Plot the results
autoplot(tune_res) + theme_minimal()
```

![plot of chunk inspect-results](figure/inspect-results-1.png)

``` r

# Select the best hyperparameters
best_params <- select_best(tune_res, metric = "accuracy")
print(best_params)
#> # A tibble: 1 × 6
#>   learn_rate fit_batch_size fit_epochs compile_optimizer compile_loss             .config         
#>        <dbl>          <int>      <int> <chr>             <chr>                    <chr>           
#> 1       0.01             16         30 adam              categorical_crossentropy pre0_mod21_post0
```

The results show that `tune` has successfully explored different optimizers, loss functions, learning rates, epochs, and batch sizes, identifying the combination that yields the best accuracy.

## Finalize and Fit

Finally, we finalize our workflow with the best-performing hyperparameters and fit the model one last time on the full training dataset.


``` r
# Finalize the workflow
final_wf <- finalize_workflow(tune_wf, best_params)

# Fit the final model
final_fit <- fit(final_wf, data = iris_train)
#> 4/4 - 0s - 27ms/step

print(final_fit)
#> ══ Workflow [trained] ═════════════════════════════════════
#> Preprocessor: Recipe
#> Model: iris_mlp()
#> 
#> ── Preprocessor ───────────────────────────────────────────
#> 1 Recipe Step
#> 
#> • step_normalize()
#> 
#> ── Model ──────────────────────────────────────────────────
#> $fit
#> Model: "sequential_98"
#> ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┓
#> ┃ Layer (type)                                        ┃ Output Shape                           ┃               Param # ┃
#> ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━┩
#> │ dense_195 (Dense)                                   │ (None, 16)                             │                    80 │
#> ├─────────────────────────────────────────────────────┼────────────────────────────────────────┼───────────────────────┤
#> │ dense_196 (Dense)                                   │ (None, 3)                              │                    51 │
#> └─────────────────────────────────────────────────────┴────────────────────────────────────────┴───────────────────────┘
#>  Total params: 395 (1.55 KB)
#>  Trainable params: 131 (524.00 B)
#>  Non-trainable params: 0 (0.00 B)
#>  Optimizer params: 264 (1.04 KB)
#> 
#> $keras_bytes
#>     [1] 50 4b 03 04 14 00 00 00 00 00 00 00 21 00 10 9a 9e 5c 40 00 00 00 40 00 00 00 0d 00 00 00 6d 65 74 61 64 61 74
#>    [38] 61 2e 6a 73 6f 6e 7b 22 6b 65 72 61 73 5f 76 65 72 73 69 6f 6e 22 3a 20 22 33 2e 31 35 2e 31 22 2c 20 22 64 61
#>    [75] 74 65 5f 73 61 76 65 64 22 3a 20 22 32 30 32 36 2d 30 39 2d 30 33 40 31 37 3a 32 30 3a 33 35 22 7d 50 4b 03 04
#>   [112] 14 00 00 00 00 00 00 00 21 00 28 a1 52 af d0 0b 00 00 d0 0b 00 00 0b 00 00 00 63 6f 6e 66 69 67 2e 6a 73 6f 6e
#>   [149] 7b 22 6d 6f 64 75 6c 65 22 3a 20 22 6b 65 72 61 73 22 2c 20 22 63 6c 61 73 73 5f 6e 61 6d 65 22 3a 20 22 53 65
#>   [186] 71 75 65 6e 74 69 61 6c 22 2c 20 22 63 6f 6e 66 69 67 22 3a 20 7b 22 6e 61 6d 65 22 3a 20 22 73 65 71 75 65 6e
#>   [223] 74 69 61 6c 5f 39 38 22 2c 20 22 74 72 61 69 6e 61 62 6c 65 22 3a 20 74 72 75 65 2c 20 22 64 74 79 70 65 22 3a
#>   [260] 20 7b 22 6d 6f 64 75 6c 65 22 3a 20 22 6b 65 72 61 73 22 2c 20 22 63 6c 61 73 73 5f 6e 61 6d 65 22 3a 20 22 44
#>   [297] 54 79 70 65 50 6f 6c 69 63 79 22 2c 20 22 63 6f 6e 66 69 67 22 3a 20 7b 22 6e 61 6d 65 22 3a 20 22 66 6c 6f 61
#>   [334] 74 33 32 22 7d 2c 20 22 72 65 67 69 73 74 65 72 65 64 5f 6e 61 6d 65 22 3a 20 6e 75 6c 6c 2c 20 22 73 68 61 72
#>   [371] 65 64 5f 6f 62 6a 65 63 74 5f 69 64 22 3a 20 31 33 31 34 30 35 33 32 34 31 37 31 36 30 30 7d 2c 20 22 6c 61 79
#>   [408] 65 72 73 22 3a 20 5b 7b 22 6d 6f 64 75 6c 65 22 3a 20 22 6b 65 72 61 73 2e 6c 61 79 65 72 73 22 2c 20 22 63 6c
#>   [445] 61 73 73 5f 6e 61 6d 65 22 3a 20 22 49 6e 70 75 74 4c 61 79 65 72 22 2c 20 22 63 6f 6e 66 69 67 22 3a 20 7b 22
#>   [482] 62 61 74 63 68 5f 73 68 61 70 65 22 3a 20 5b 6e 75 6c 6c 2c 20 34 5d 2c 20 22 64 74 79 70 65 22 3a 20 22 66 6c
#>   [519] 6f 61 74 33 32 22 2c 20 22 73 70 61 72 73 65 22 3a 20 66 61 6c 73 65 2c 20 22 72 61 67 67 65 64 22 3a 20 66 61
#>   [556] 6c 73 65 2c 20 22 6e 61 6d 65 22 3a 20 22 69 6e 70 75 74 5f 6c 61 79 65 72 5f 39 38 22 2c 20 22 6f 70 74 69 6f
#>   [593] 6e 61 6c 22 3a 20 66 61 6c 73 65 7d 2c 20 22 72 65 67 69 73 74 65 72 65 64 5f 6e 61 6d 65 22 3a 20 6e 75 6c 6c
#>   [630] 7d 2c 20 7b 22 6d 6f 64 75 6c 65 22 3a 20 22 6b 65 72 61 73 2e 6c 61 79 65 72 73 22 2c 20 22 63 6c 61 73 73 5f
#>   [667] 6e 61 6d 65 22 3a 20 22 44 65 6e 73 65 22 2c 20 22 63 6f 6e 66 69 67 22 3a 20 7b 22 6e 61 6d 65 22 3a 20 22 64
#>   [704] 65 6e 73 65 5f 31 39 35 22 2c 20 22 74 72 61 69 6e 61 62 6c 65 22 3a 20 74 72 75 65 2c 20 22 64 74 79 70 65 22
#>   [741] 3a 20 7b 22 6d 6f 64 75 6c 65 22 3a 20 22 6b 65 72 61 73 22 2c 20 22 63 6c 61 73 73 5f 6e 61 6d 65 22 3a 20 22
#>   [778] 44 54 79 70 65 50 6f 6c 69 63 79 22 2c 20 22 63 6f 6e 66 69 67 22 3a 20 7b 22 6e 61 6d 65 22 3a 20 22 66 6c 6f
#>   [815] 61 74 33 32 22 7d 2c 20 22 72 65 67 69 73 74 65 72 65 64 5f 6e 61 6d 65 22 3a 20 6e 75 6c 6c 7d 2c 20 22 75 6e
#>   [852] 69 74 73 22 3a 20 31 36 2c 20 22 61 63 74 69 76 61 74 69 6f 6e 22 3a 20 22 72 65 6c 75 22 2c 20 22 75 73 65 5f
#>   [889] 62 69 61 73 22 3a 20 74 72 75 65 2c 20 22 6b 65 72 6e 65 6c 5f 69 6e 69 74 69 61 6c 69 7a 65 72 22 3a 20 7b 22
#>   [926] 6d 6f 64 75 6c 65 22 3a 20 22 6b 65 72 61 73 2e 69 6e 69 74 69 61 6c 69 7a 65 72 73 22 2c 20 22 63 6c 61 73 73
#>   [963] 5f 6e 61 6d 65 22 3a 20 22 47 6c 6f 72 6f 74 55 6e 69 66 6f 72 6d 22 2c 20 22 63 6f 6e 66 69 67 22 3a 20 7b 22
#>  [1000] 73 65 65 64 22 3a 20 6e 75 6c 6c 2c 20 22 69 6e 70 75 74 5f 61 78 65 73 22 3a 20 6e 75 6c 6c 2c 20 22 6f 75 74
#>  [1037] 70 75 74 5f 61 78 65 73 22 3a 20 6e 75 6c 6c 7d 2c 20 22 72 65 67 69 73 74 65 72 65 64 5f 6e 61 6d 65 22 3a 20
#>  [1074] 6e 75 6c 6c 7d 2c 20 22 62 69 61 73 5f 69 6e 69 74 69 61 6c 69 7a 65 72 22 3a 20 7b 22 6d 6f 64 75 6c 65 22 3a
#>  [1111] 20 22 6b 65 72 61 73 2e 69 6e 69 74 69 61 6c 69 7a 65 72 73 22 2c 20 22 63 6c 61 73 73 5f 6e 61 6d 65 22 3a 20
#>  [1148] 22 5a 65 72 6f 73 22 2c 20 22 63 6f 6e 66 69 67 22 3a 20 7b 7d 2c 20 22 72 65 67 69 73 74 65 72 65 64 5f 6e 61
#>  [1185] 6d 65 22 3a 20 6e 75 6c 6c 7d 2c 20 22 6b 65 72 6e 65 6c 5f 72 65 67 75 6c 61 72 69 7a 65 72 22 3a 20 6e 75 6c
#>  [1222] 6c 2c 20 22 62 69 61 73 5f 72 65 67 75 6c 61 72 69 7a 65 72 22 3a 20 6e 75 6c 6c 2c 20 22 6b 65 72 6e 65 6c 5f
#>  [1259] 63 6f 6e 73 74 72 61 69 6e 74 22 3a 20 6e 75 6c 6c 2c 20 22 62 69 61 73 5f 63 6f 6e 73 74 72 61 69 6e 74 22 3a
#> 
#> ...
#> and 1106 more lines.
```

We can now use this `final_fit` object to make predictions on the test set.


``` r
# Make predictions
predictions <- predict(final_fit, new_data = iris_test)
#> 1/1 - 0s - 57ms/step

# Evaluate performance
bind_cols(predictions, iris_test) |>
  accuracy(truth = Species, estimate = .pred_class)
#> # A tibble: 1 × 3
#>   .metric  .estimator .estimate
#>   <chr>    <chr>          <dbl>
#> 1 accuracy multiclass     0.967
```

## Conclusion

This vignette demonstrated how to tune the crucial `fit_*` and `compile_*` arguments of a Keras model within the `tidymodels` framework using `kerasnip`. By exposing these as tunable parameters, `kerasnip` gives you full control over the training process, allowing you to optimize not just the model's architecture, but also how it learns.
