--- title: "Getting Started with densemlp" output: pdf_document: toc: true number_sections: true vignette: > %\VignetteIndexEntry{Getting Started with densemlp} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} can_use_torch <- requireNamespace("torch", quietly = TRUE) && isTRUE(torch::torch_is_installed()) knitr::opts_chunk$set( echo = TRUE, eval = can_use_torch, collapse = TRUE, comment = "#>", fig.width = 6, fig.height = 4 ) ``` ```{r} library(densemlp) ``` `densemlp` trains dense feed-forward multilayer perceptrons for tabular data. It accepts a formula and data frame, preprocesses numeric and categorical predictors, and returns a `densemlp_fit` object with methods for prediction, plotting, metrics, tuning, and variable importance. The models implemented in `densemlp` are dense feed-forward multilayer perceptrons. Optional components such as dropout, batch normalization, residual connections, gated blocks, and input projection extend the basic dense multilayer perceptron architecture but do not change the model class into a convolutional, recurrent, transformer, or tree-based model. `task` is optional in the main API. When it is set to `"auto"` or omitted, the task is inferred from the outcome. Use it only when you need to override that inference. This vignette documents version `0.5.0`. This vignette assumes the package is installed or loaded with `pkgload::load_all(".")`. Do not source individual files such as `R/densemlp.R` because exported functions rely on helpers loaded through the package namespace. `densemlp` implements dense feed-forward multilayer perceptrons for tabular data. Each hidden block is centered on a fully connected transformation, optionally combined with activation functions, dropout, batch normalization, residual connections, gated mechanisms, and input projection. ## Model ### Architecture Let $x \in \mathbb{R}^{p}$ be a predictor row after preprocessing. If an input projection of width $d_0$ is requested, it is applied first: $$ h_0 = W_{\text{in}} x + b_{\text{in}}, \qquad W_{\text{in}} \in \mathbb{R}^{d_0 \times p}. $$ Otherwise $h_0 = x$. Each of the $L$ hidden blocks then applies, in order, a linear map, batch normalization, an activation, an optional gate, and dropout. For block $l = 1, \dots, L$ with input $h_{l-1} \in \mathbb{R}^{d_{l-1}}$ and output width $d_l$: $$ z_l = W_l h_{l-1} + b_l, \qquad W_l \in \mathbb{R}^{d_l \times d_{l-1}}, $$ $$ u_l = \mathrm{BN}(z_l) \quad \text{(identity if batch normalization is off)}, $$ $$ a_l = \phi(u_l), \qquad \phi \in \{\mathrm{ReLU}, \tanh, \mathrm{GELU}\}. $$ If the block is gated, $a_l$ is rescaled by a sigmoid gate computed from itself: $$ g_l = \sigma(W_l^{g} a_l + b_l^{g}), \qquad a_l \leftarrow a_l \odot g_l. $$ Dropout is then applied, $\tilde a_l = \mathrm{Dropout}(a_l)$. If the block is residual, the block input is added back, with a learned linear projection $P_l$ when $d_{l-1} \neq d_l$ and the identity otherwise: $$ h_l = \tilde a_l + P_l(h_{l-1}), \qquad P_l(h_{l-1}) = \begin{cases} h_{l-1} & d_{l-1} = d_l \\ W_l^{p} h_{l-1} + b_l^{p} & d_{l-1} \neq d_l. \end{cases} $$ After the $L$ blocks, a final linear layer maps to the output dimension $d_{\text{out}}$ (1 for regression and binary classification, the number of classes for multiclass classification): $$ \hat y = W_{\text{out}} h_L + b_{\text{out}}. $$ Every linear layer is initialized independently: Xavier uniform when the block's activation is $\tanh$, Kaiming (He) uniform otherwise (used for both ReLU and GELU blocks, with the ReLU gain), and all biases start at zero. ### Loss functions Training minimizes a task-specific criterion over mini-batches of size $B$. Regression uses mean squared error on the raw network output $\hat y_i$: $$ \mathcal{L}_{\text{reg}} = \frac{1}{B} \sum_{i=1}^{B} (\hat y_i - y_i)^2. $$ Binary classification uses binary cross-entropy computed directly from the logit $\hat y_i$: $$ \mathcal{L}_{\text{bin}} = -\frac{1}{B} \sum_{i=1}^{B} \left[ y_i \log \sigma(\hat y_i) + (1 - y_i) \log(1 - \sigma(\hat y_i)) \right]. $$ With label smoothing $\varepsilon > 0$, targets are shrunk toward $0.5$ before this loss is computed, $y_i \leftarrow y_i(1-\varepsilon) + 0.5\varepsilon$. As an alternative for imbalanced binary outcomes, focal loss reweights each example by $(1-p_t)^\gamma$ with class weight $\alpha$: $$ \mathcal{L}_{\text{focal}} = -\frac{1}{B}\sum_{i=1}^{B} \alpha_i (1-p_{t,i})^{\gamma} \left[ y_i \log \sigma(\hat y_i) + (1-y_i)\log(1-\sigma(\hat y_i)) \right], \qquad p_{t,i} = \sigma(\hat y_i)^{y_i}(1-\sigma(\hat y_i))^{1-y_i}. $$ Multiclass classification uses softmax cross-entropy over the $K$ output logits $\hat y_i \in \mathbb{R}^K$: $$ \mathcal{L}_{\text{multi}} = -\frac{1}{B} \sum_{i=1}^{B} \log \frac{\exp(\hat y_{i, c_i})}{\sum_{k=1}^{K} \exp(\hat y_{i,k})}, \qquad c_i \text{ the true class of example } i, $$ with the same label-smoothing option applied to the target distribution when requested. ### Optimization Parameters are updated with Adam or SGD with momentum $0.9$, both supporting an $L_2$ weight decay $\lambda$ applied to the parameter vector $\theta$: $$ \theta \leftarrow \theta - \eta \left( \widehat{\nabla_\theta \mathcal{L}} + \lambda \theta \right), $$ where $\eta$ is the learning rate and $\widehat{\nabla_\theta \mathcal{L}}$ is the Adam or SGD update direction computed from the mini-batch gradient. Gradients are clipped to a maximum global norm of 5 before each step. The learning rate itself can follow a fixed schedule: none, cosine annealing over the training horizon, $$ \eta_t = \eta_{\min} + \tfrac{1}{2}(\eta_0 - \eta_{\min})\left(1 + \cos\left(\tfrac{t}{T_{\max}}\pi\right)\right), $$ or a step decay that halves $\eta$ every $\max(5, \lfloor \text{epochs}/3 \rfloor)$ epochs. Training holds out a validation fraction of the data and stops early once the validation loss fails to improve by at least `min_delta` for `patience` consecutive epochs (after `min_epochs` epochs have elapsed); the model weights from the best validation epoch are restored at the end of training. ## Classification For classification, use a factor outcome. The task is inferred from the outcome type, so `task` can usually be omitted. ```{r classification-fit} fit <- densemlp( Species ~ ., data = iris, epochs = 10, patience = 3, verbose = FALSE, seed = 1 ) fit ``` Class predictions are returned as factors with the same levels as the training outcome. ```{r classification-predictions} predict(fit, iris[1:5, ], type = "class") round(predict(fit, iris[1:5, ], type = "prob"), 3) ``` Use task-aware metrics to summarize predictions. ```{r classification-metrics} pred <- predict(fit, iris, type = "class") densemlp_metrics(iris$Species, pred) ``` ## Regression For regression, use a numeric outcome. The task is inferred automatically for numeric outcomes. ```{r regression-fit} fit_reg <- densemlp( mpg ~ disp + hp + wt, data = mtcars, epochs = 10, patience = 3, verbose = FALSE, seed = 2 ) fit_reg ``` ```{r regression-predictions} pred_reg <- predict(fit_reg, mtcars, type = "response") head(round(pred_reg, 2)) densemlp_metrics(mtcars$mpg, pred_reg) ``` ## Training Diagnostics The fitted object stores training history, which can be displayed directly or through the `ggplot2::autoplot()` method. ```{r plot-history} plot_history(fit) ``` ```{r autoplot-history} ggplot2::autoplot(fit) ``` ## Tuning `tune_densemlp()` evaluates a grid of hyperparameters and, by default, refits the best configuration on the supplied data. ```{r tuning} tuned <- tune_densemlp( Species ~ ., data = iris, grid = list( hidden_units = list(c(8), c(16, 8)), activation = c("relu"), dropout = c(0), batch_size = c(8), lr = c(1e-3), epochs = c(10) ), patience = 3, seed = 3, verbose = FALSE ) tuned$results ``` ## Permutation Importance Permutation importance estimates how much a metric changes after shuffling each predictor. ```{r permutation-importance} importance <- perm_importance(fit, iris[, -5], iris$Species) importance$data plot(importance) ```