% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/generalized-nn-fit.R, R/generalized-nn-fitds.R
\name{gen-nn-train}
\alias{gen-nn-train}
\alias{train_nn}
\alias{train_nn.matrix}
\alias{train_nn.data.frame}
\alias{train_nn.formula}
\alias{train_nn.default}
\alias{train_nn.dataset}
\title{Generalized Neural Network Trainer}
\usage{
train_nn(x, ...)

\method{train_nn}{matrix}(
  x,
  y,
  hidden_neurons = NULL,
  activations = NULL,
  output_activation = NULL,
  bias = TRUE,
  arch = NULL,
  architecture = NULL,
  early_stopping = NULL,
  epochs = 100,
  batch_size = 32,
  penalty = 0,
  mixture = 0,
  learn_rate = 0.001,
  optimizer = "adam",
  optimizer_args = list(),
  loss = "mse",
  validation_split = 0,
  device = NULL,
  verbose = FALSE,
  cache_weights = FALSE,
  ...
)

\method{train_nn}{data.frame}(
  x,
  y,
  hidden_neurons = NULL,
  activations = NULL,
  output_activation = NULL,
  bias = TRUE,
  arch = NULL,
  architecture = NULL,
  early_stopping = NULL,
  epochs = 100,
  batch_size = 32,
  penalty = 0,
  mixture = 0,
  learn_rate = 0.001,
  optimizer = "adam",
  optimizer_args = list(),
  loss = "mse",
  validation_split = 0,
  device = NULL,
  verbose = FALSE,
  cache_weights = FALSE,
  ...
)

\method{train_nn}{formula}(
  x,
  data,
  hidden_neurons = NULL,
  activations = NULL,
  output_activation = NULL,
  bias = TRUE,
  arch = NULL,
  architecture = NULL,
  early_stopping = NULL,
  epochs = 100,
  batch_size = 32,
  penalty = 0,
  mixture = 0,
  learn_rate = 0.001,
  optimizer = "adam",
  optimizer_args = list(),
  loss = "mse",
  validation_split = 0,
  device = NULL,
  verbose = FALSE,
  cache_weights = FALSE,
  ...
)

\method{train_nn}{default}(x, ...)

\method{train_nn}{dataset}(
  x,
  y = NULL,
  hidden_neurons = NULL,
  activations = NULL,
  output_activation = NULL,
  bias = TRUE,
  arch = NULL,
  architecture = NULL,
  flatten_input = NULL,
  epochs = 100,
  batch_size = 32,
  penalty = 0,
  mixture = 0,
  learn_rate = 0.001,
  optimizer = "adam",
  optimizer_args = list(),
  loss = "mse",
  validation_split = 0,
  device = NULL,
  verbose = FALSE,
  cache_weights = FALSE,
  n_classes = NULL,
  ...
)
}
\arguments{
\item{x}{Dispatch is based on its current class:
\itemize{
\item \code{matrix}: used directly, no preprocessing applied.
\item \code{data.frame}: preprocessed via \code{hardhat::mold()}. \code{y} may be a vector /
factor / matrix of outcomes, or a formula describing the outcome–predictor
relationship within \code{x}.
\item \code{formula}: combined with \code{data} and preprocessed via \code{hardhat::mold()}.
\item \code{dataset}: a \code{torch} dataset object; batched via \code{torch::dataloader()}.
This is the recommended interface for sequence/time-series and image data.
}}

\item{...}{Additional arguments passed to specific methods.}

\item{y}{Outcome data. Interpretation depends on the method:
\itemize{
\item For the \code{matrix} and \code{data.frame} methods: a numeric vector, factor, or
matrix of outcomes.
\item For the \code{data.frame} method only: alternatively a formula of the form
\code{outcome ~ predictors}, evaluated against \code{x}.
\item Ignored when \code{x} is a formula (outcome is taken from the formula) or a
\code{dataset} (labels come from the dataset itself).
}}

\item{hidden_neurons}{Integer vector specifying the number of neurons in each
hidden layer, e.g. \code{c(128, 64)} for two hidden layers. When \code{NULL} or missing,
no hidden layers are used and the model reduces to a single linear mapping
from inputs to outputs.}

\item{activations}{Activation function specification(s) for the hidden layers.
See \code{\link[=act_funs]{act_funs()}} for supported values. Recycled if a single value is given.}

\item{output_activation}{Optional activation function for the output layer.
Defaults to \code{NULL} (no activation / linear output).}

\item{bias}{Logical. Whether to include bias terms in each layer. Default \code{TRUE}.}

\item{arch}{Backward-compatible alias for \code{architecture}. If both are supplied,
they must be identical.}

\item{architecture}{An \code{\link[=nn_arch]{nn_arch()}} object specifying a custom architecture. Default
\code{NULL}, which falls back to a standard feed-forward network.}

\item{early_stopping}{An \code{\link[=early_stop]{early_stop()}} object specifying early stopping
behaviour, or \code{NULL} (default) to disable early stopping. When supplied,
training halts if the monitored metric does not improve by at least
\code{min_delta} for \code{patience} consecutive epochs.
Example: \code{early_stopping = early_stop(patience = 10)}.}

\item{epochs}{Positive integer. Number of full passes over the training data.
Default \code{100}.}

\item{batch_size}{Positive integer. Number of samples per mini-batch. Default \code{32}.}

\item{penalty}{Non-negative numeric. L1/L2 regularization strength (lambda).
Default \code{0} (no regularization).}

\item{mixture}{Numeric in [0, 1]. Elastic net mixing parameter: \code{0} = pure
ridge (L2), \code{1} = pure lasso (L1). Default \code{0}.}

\item{learn_rate}{Positive numeric. Step size for the optimizer. Default \code{0.001}.}

\item{optimizer}{Character. Optimizer algorithm. One of \code{"adam"} (default),
\code{"sgd"}, or \code{"rmsprop"}.}

\item{optimizer_args}{Named list of additional arguments forwarded to the
optimizer constructor (e.g. \code{list(momentum = 0.9)} for SGD). Default \code{list()}.}

\item{loss}{Character or function. Loss function used during training. Built-in
options: \code{"mse"} (default), \code{"mae"}, \code{"cross_entropy"}, or \code{"bce"}. For
classification tasks with a scalar label, \code{"cross_entropy"} is set
automatically. Alternatively, supply a custom function or formula with
signature \verb{function(input, target)} returning a scalar \code{torch_tensor}.}

\item{validation_split}{Numeric in [0, 1). Proportion of training data held
out for validation. Default \code{0} (no validation set).}

\item{device}{Character. Compute device: \code{"cpu"}, \code{"cuda"}, or \code{"mps"}.
Default \code{NULL}, which auto-detects the best available device.}

\item{verbose}{Logical. If \code{TRUE}, prints loss (and validation loss) at regular
intervals during training. Default \code{FALSE}.}

\item{cache_weights}{Logical. If \code{TRUE}, stores a copy of the trained weight
matrices in the returned object under \verb{$cached_weights}. Default \code{FALSE}.}

\item{data}{A data frame. Required when \code{x} is a formula.}

\item{flatten_input}{Logical or \code{NULL} (dataset method only). Controls whether
each batch/sample is flattened to 2D before entering the model. \code{NULL}
(default) auto-selects: \code{TRUE} when \code{architecture = NULL}, otherwise \code{FALSE}.}

\item{n_classes}{Positive integer. Number of output classes. Required when
\code{x} is a \code{dataset} with scalar (classification) labels; ignored otherwise.}
}
\value{
An object of class \code{"nn_fit"}, or one of its subclasses:
\itemize{
\item \code{c("nn_fit_tab", "nn_fit")} — returned by the \code{data.frame} and \code{formula} methods
\item \code{c("nn_fit_ds", "nn_fit")} — returned by the \code{dataset} method
}

All subclasses share a common structure. See \strong{Details} for the list of
components.
}
\description{
\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#experimental}{\figure{lifecycle-experimental.svg}{options: alt='[Experimental]'}}}{\strong{[Experimental]}}

\code{train_nn()} is a generic function for training neural networks with a
user-defined architecture via \code{\link[=nn_arch]{nn_arch()}}. Dispatch is based on the class
of \code{x}.

Recommended workflow:
\enumerate{
\item Define architecture with \code{\link[=nn_arch]{nn_arch()}} (optional).
\item Train with \code{train_nn()}.
\item Predict with \code{\link[=predict.nn_fit]{predict.nn_fit()}}.
}

All methods delegate to a shared implementation core after preprocessing.
When \code{architecture = NULL}, the model falls back to a plain feed-forward neural network
(\code{nn_linear}) architecture.
}
\details{
The returned \code{"nn_fit"} object is a named list with the following components:
\itemize{
\item \code{model} — the trained \code{torch::nn_module} object
\item \code{fitted} — fitted values on the training data (or \code{NULL} for dataset fits)
\item \code{loss_history} — numeric vector of per-epoch training loss, trimmed to
actual epochs run (relevant when early stopping is active)
\item \code{val_loss_history} — per-epoch validation loss, or \code{NULL} if
\code{validation_split = 0}
\item \code{n_epochs} — number of epochs actually trained
\item \code{stopped_epoch} — epoch at which early stopping triggered, or \code{NA} if
training ran to completion
\item \code{hidden_neurons}, \code{activations}, \code{output_activation} — architecture spec
\item \code{penalty}, \code{mixture} — regularization settings
\item \code{feature_names}, \code{response_name} — variable names (tabular methods only)
\item \code{no_x}, \code{no_y} — number of input features and output nodes
\item \code{is_classification} — logical flag
\item \code{y_levels}, \code{n_classes} — class labels and count (classification only)
\item \code{device} — device the model is on
\item \code{cached_weights} — list of weight matrices, or \code{NULL}
\item \code{arch} — the \code{nn_arch} object used, or \code{NULL}
}
}
\section{Supported tasks and input formats}{

\code{train_nn()} is task-agnostic by design (no explicit \code{task} argument).
Task behavior is determined by your input interface and architecture:
\itemize{
\item \strong{Tabular data}: use \code{matrix}, \code{data.frame}, or \code{formula} methods.
\item \strong{Time series}: use the \code{dataset} method with per-item tensors shaped as
\verb{[time, features]} (or your preferred convention) and a recurrent
architecture via \code{\link[=nn_arch]{nn_arch()}}.
\item \strong{Image classification}: use the \code{dataset} method with per-item tensors
shaped for your first layer (commonly \verb{[channels, height, width]} for
\code{torch::nn_conv2d}). If your source arrays are channel-last, reorder in the
dataset or via \code{input_transform}.
}
}

\section{Matrix method}{

When \code{x} is supplied as a raw numeric matrix, no preprocessing is applied.
Data is passed directly to the shared \code{train_nn_impl} core.
}

\section{Data frame method}{

When \code{x} is a data frame, \code{y} can be either a vector / factor / matrix of
outcomes, or a formula of the form \code{outcome ~ predictors} evaluated against
\code{x}. Preprocessing is handled by \code{hardhat::mold()}.
}

\section{Formula method}{

When \code{x} is a formula, \code{data} must be supplied as the data frame against
which the formula is evaluated. Preprocessing is handled by \code{hardhat::mold()}.
}

\section{Dataset method (\code{train_nn.dataset()})}{

Trains a neural network directly on a \code{torch} dataset object. Batching and
lazy loading are handled by \code{torch::dataloader()}, making this method
well-suited for large datasets that do not fit entirely in memory.

Architecture configuration follows the same contract as other \code{train_nn()}
methods via \code{architecture = nn_arch(...)} (or legacy \code{arch = ...}).
For non-tabular inputs (time series, images), set \code{flatten_input = FALSE} to
preserve tensor dimensions expected by recurrent or convolutional layers.

Labels are taken from the second element of each dataset item (i.e.
\code{dataset[[i]][[2]]}), so \code{y} is ignored. When the label is a scalar tensor,
a classification task is assumed and \code{n_classes} must be supplied. The loss
is automatically switched to \code{"cross_entropy"} in that case.

Fitted values are \strong{not} cached in the returned object. Use
\code{\link[=predict.nn_fit_ds]{predict.nn_fit_ds()}} with \code{newdata} to obtain predictions after training.
}

\examples{
\donttest{
if (torch::torch_is_installed()) {
    # Matrix method — no preprocessing
    model = train_nn(
        x = as.matrix(iris[, 2:4]),
        y = iris$Sepal.Length,
        hidden_neurons = c(64, 32),
        activations = "relu",
        epochs = 50
    )

    # Data frame method — y as a vector
    model = train_nn(
        x = iris[, 2:4],
        y = iris$Sepal.Length,
        hidden_neurons = c(64, 32),
        activations = "relu",
        epochs = 50
    )

    # Data frame method — y as a formula evaluated against x
    model = train_nn(
        x = iris,
        y = Sepal.Length ~ . - Species,
        hidden_neurons = c(64, 32),
        activations = "relu",
        epochs = 50
    )

    # Formula method — outcome derived from formula
    model = train_nn(
        x = Sepal.Length ~ .,
        data = iris[, 1:4],
        hidden_neurons = c(64, 32),
        activations = "relu",
        epochs = 50
    )

    # No hidden layers — linear model
    model = train_nn(
        x = Sepal.Length ~ .,
        data = iris[, 1:4],
        epochs = 50
    )

    # Architecture object (nn_arch -> train_nn)
    mlp_arch = nn_arch(nn_name = "mlp_model")
    model = train_nn(
        x = Sepal.Length ~ .,
        data = iris[, 1:4],
        hidden_neurons = c(64, 32),
        activations = "relu",
        architecture = mlp_arch,
        epochs = 50
    )

    # Custom layer architecture
    custom_linear = torch::nn_module(
        "CustomLinear",
        initialize = function(in_features, out_features, bias = TRUE) {
            self$layer = torch::nn_linear(in_features, out_features, bias = bias)
        },
        forward = function(x) self$layer(x)
    )
    custom_arch = nn_arch(
        nn_name = "custom_linear_mlp",
        nn_layer = ~ custom_linear
    )
    model = train_nn(
        x = Sepal.Length ~ .,
        data = iris[, 1:4],
        hidden_neurons = c(16, 8),
        activations = "relu",
        architecture = custom_arch,
        epochs = 50
    )

    # With early stopping
    model = train_nn(
        x = Sepal.Length ~ .,
        data = iris[, 1:4],
        hidden_neurons = c(64, 32),
        activations = "relu",
        epochs = 200,
        validation_split = 0.2,
        early_stopping = early_stop(patience = 10)
    )
}
}

\donttest{
if (torch::torch_is_installed()) {
    # torch dataset method — labels come from the dataset itself
    iris_cls_dataset = torch::dataset(
        name = "iris_cls_dataset",
        
        initialize = function(data = iris) {
            self$x = torch::torch_tensor(
                as.matrix(data[, 1:4]),
                dtype = torch::torch_float32()
            )
            # Species is a factor; convert to integer (1-indexed -> keep as-is for cross_entropy)
            self$y = torch::torch_tensor(
                as.integer(data$Species),
                dtype = torch::torch_long()
            )
        },
        
        .getitem = function(i) {
            list(self$x[i, ], self$y[i])
        },
        
        .length = function() {
            self$x$size(1)
        }
    )()
    
    model_nn_ds = train_nn(
        x = iris_cls_dataset,
        hidden_neurons = c(32, 10),
        activations = "relu",
        epochs = 80,
        batch_size = 16,
        learn_rate = 0.01,
        n_classes = 3, # Iris dataset has only 3 species
        validation_split = 0.2,
        verbose = TRUE
    )
    
    pred_nn = predict(model_nn_ds, iris_cls_dataset)
    class_preds = c("Setosa", "Versicolor", "Virginica")[predict(model_nn_ds, iris_cls_dataset)]
    
    # Confusion Matrix
    table(actual = iris$Species, pred = class_preds)
}
}

}
\seealso{
\code{\link[=predict.nn_fit]{predict.nn_fit()}}, \code{\link[=nn_arch]{nn_arch()}}, \code{\link[=act_funs]{act_funs()}}, \code{\link[=early_stop]{early_stop()}}
}
