% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/create_keras_functional_spec.R
\name{create_keras_functional_spec}
\alias{create_keras_functional_spec}
\title{Create a Custom Keras Functional API Model Specification for Tidymodels}
\usage{
create_keras_functional_spec(
  model_name,
  layer_blocks,
  mode = c("regression", "classification"),
  ...,
  env = parent.frame()
)
}
\arguments{
\item{model_name}{A character string for the name of the new model
specification function (e.g., "custom_resnet"). This should be a valid R
function name.}

\item{layer_blocks}{A named list of functions where each function defines a
"block" (a node) in the model graph. The list names are crucial as they
define the names of the nodes. The arguments of each function define how
the nodes are connected. See the "Model Graph Connectivity" section for
details.}

\item{mode}{A character string, either "regression" or "classification".}

\item{...}{Reserved for future use. Currently not used.}

\item{env}{The environment in which to create the new model specification
function and its associated \code{update()} method. Defaults to the calling
environment (\code{parent.frame()}).}
}
\value{
Invisibly returns \code{NULL}. Its primary side effect is to create a
new model specification function (e.g., \code{custom_resnet()}) in the
specified environment and register the model with \code{parsnip} so it can be
used within the \code{tidymodels} framework.
}
\description{
This function acts as a factory to generate a new \code{parsnip} model
specification based on user-defined blocks of Keras layers using the
Functional API. This allows for creating complex, tunable architectures
with non-linear topologies that integrate seamlessly with the \code{tidymodels}
ecosystem.
}
\details{
This function generates all the boilerplate needed to create a custom,
tunable \code{parsnip} model specification that uses the Keras Functional API.
This is ideal for models with complex, non-linear topologies, such as
networks with multiple inputs/outputs or residual connections.

The function inspects the arguments of your \code{layer_blocks} functions and
makes them available as tunable parameters in the generated model
specification, prefixed with the block's name (e.g., \code{dense_units}).
Common training parameters such as \code{epochs} and \code{learn_rate} are also added.
}
\section{Model Graph Connectivity}{

\code{kerasnip} builds the model's directed acyclic graph by inspecting the
arguments of each function in the \code{layer_blocks} list. The connection logic
is as follows:
\enumerate{
\item The \strong{names of the elements} in the \code{layer_blocks} list define the names
of the nodes in your graph (e.g., \code{main_input}, \code{dense_path}, \code{output}).
\item The \strong{names of the arguments} in each block function specify its inputs.
A block function like \verb{my_block <- function(input_a, input_b, ...)}
declares that it needs input from the nodes named \code{input_a} and
\code{input_b}. \code{kerasnip} will automatically supply the output tensors from
those nodes when calling \code{my_block}.
}

There are two special requirements:
\itemize{
\item \strong{Input Block}: The first block in the list is treated as the input
node. Its function should not take other blocks as input, but it can have
an \code{input_shape} argument, which is supplied automatically during
fitting.
\item \strong{Output Block}: Exactly one block must be named \code{"output"}. The tensor
returned by this block is used as the final output of the Keras model.
}

A key feature is the automatic creation of \verb{num_\{block_name\}} arguments
(e.g., \code{num_dense_path}). This allows you to control how many times a block
is repeated, making it easy to tune the depth of your network. A block can
only be repeated if it has exactly one input from another block in the graph.

The new model specification function and its \code{update()} method are created
in the environment specified by the \code{env} argument.
}

\section{Saving and Reloading Models}{

To save a fitted workflow and reload it in a new R session, use
\code{bundle::bundle()} before saving — this is required to preserve the Keras
model weights:

\if{html}{\out{<div class="sourceCode r">}}\preformatted{library(bundle)
bundled <- bundle(fitted_workflow)
saveRDS(bundled, "model.rds")

# New session:
library(kerasnip); library(bundle)
fitted_workflow <- unbundle(readRDS("model.rds"))
predict(fitted_workflow, new_data = test_data)  # works
}\if{html}{\out{</div>}}

Plain \code{saveRDS()} without \code{bundle()} does not preserve Keras weights, but
\code{predict()} will still auto-register the parsnip model type from metadata
stored on the spec.
}

\examples{
\donttest{
if (requireNamespace("keras3", quietly = TRUE)) {
  library(keras3)
  library(parsnip)

  # 1. Define block functions. These are the building blocks of our model.
  # An input block that receives the data's shape automatically.
  input_block <- function(input_shape) layer_input(shape = input_shape)

  # A dense block with a tunable `units` parameter.
  dense_block <- function(tensor, units) {
    tensor |> layer_dense(units = units, activation = "relu")
  }

  # A block that adds two tensors together (for the residual connection).
  add_block <- function(input_a, input_b) layer_add(list(input_a, input_b))

  # An output block for regression.
  output_block_reg <- function(tensor) layer_dense(tensor, units = 1)

  # 2. Create the spec. The `layer_blocks` list defines the graph.
  create_keras_functional_spec(
    model_name = "my_resnet_spec",
    layer_blocks = list(
      # The names of list elements are the node names.
      main_input = input_block,

      # The argument `main_input` connects this block to the input node.
      dense_path = function(main_input, units = 32) {
        dense_block(main_input, units)
      },

      # This block's arguments connect it to the original input AND the dense
      # layer.
      add_residual = function(main_input, dense_path) {
        add_block(main_input, dense_path)
      },

      # This block must be named 'output'. It connects to the residual add
      # layer.
      output = function(add_residual) output_block_reg(add_residual)
    ),
    mode = "regression"
  )

  # 3. Use the newly created specification function!
  # The `dense_path_units` argument was created automatically.
  model_spec <- my_resnet_spec(dense_path_units = 64, epochs = 10)

  # You could also tune the number of dense layers since it has a single
  # input:
  # 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)
}
}
}
\seealso{
\code{\link[=remove_keras_spec]{remove_keras_spec()}}, \code{\link[parsnip:add_on_exports]{parsnip::new_model_spec()}},
\code{\link[=create_keras_sequential_spec]{create_keras_sequential_spec()}}
}
