Fit (calibrate) a FINN model end-to-end by gradient-descent optimization. All mechanistic process parameters, environmental-response coefficients and, if present, the weights of any hybrid (DNN-based) processes are estimated jointly in a single optimization, rather than calibrating each process in isolation.
Usage
fit(
model,
data = NULL,
env,
disturbance = NULL,
patches = 100L,
patch_size = 0.1,
init_cohort = NULL,
epochs = 20L,
lr = 0.01,
lr_scheduler = "none",
lr_scheduler_params = list(),
loss = c(dbh = "mse", ba = "mse", trees = "poisson", growth = "mse", mortality =
"binomial", regeneration = "nbinom"),
weights = "auto",
optimizer = optim_ignite_adam,
batchsize = NULL,
device = c("cpu", "gpu"),
update_step = 1L,
start_time = 1L,
plot_progress = TRUE,
folder = NULL,
checkpoints = 100L,
shuffle = TRUE,
record_gradients = FALSE,
env_autoscale = TRUE,
clip_norm = 2,
...
)Arguments
- model
(
finn_class)
Object of classfinn_classcreated by finn.- data
(
data.table|data.frame)
Data about demographic rates and stand variables must be passed asdata.tableordata.frame.Optional columns that change how the responses are scored:
period_length— how many simulated timesteps each observation's inventory interval spans. The rate responses (growth,mortality) are then compared against the model's mean over that interval, andregenerationagainst its sum (recruits accumulate). Absent orNAmeans each observation is compared against a single timestep. NOTE: this must currently be the SAME for every site — a per-site interval is not supported yet, so inventories with varying remeasurement gaps need filtering to a constant interval first.n_at_risk— trees behind each observedmortalityproportion; used to weight the binomial term (seeweights).growth_n— trees behind each observedgrowthmean; used to weight the squared-error term, since the variance of a mean is \(\sigma^2/n\).
- env
(
data.table|data.frame)
Data with environmental covariates must be passed asdata.tableordata.frame.- disturbance
(
data.table|data.frame)
Data with disturbance rates must be passed asdata.tableordata.frame.- patches
(
integer(1))
Number of patches.- patch_size
(
numeric(1))
Patch size.- init_cohort
(
CohortMat)
Initial cohort matrix of classCohortMat, created by CohortMat- epochs
(
integer(1))
Number of iteration steps.- lr
(
numeric(1))
Learning rate of the optimizer.- lr_scheduler
(
character(1)|function)
Learning-rate schedule applied on top of the constantlrabove."none"(default) keepslrconstant, reproducing the previous behavior exactly. Built-ins:"step"(decay bygammaeverystep_sizeepochs),"exponential"(multiply bygammaevery epoch),"cosine"(cosine-anneal toeta_minoverT_maxepochs),"plateau"(reduce byfactorafterpatienceepochs without improvement in the total loss). Advanced: pass afunction(optimizer)returning atorchlr scheduler object (must implement$step()).- lr_scheduler_params
(
list())
Tuning overrides for the scheduler chosen vialr_scheduler, e.g.list(step_size = 50, gamma = 0.5)for"step",list(T_max = 200)for"cosine",list(factor = 0.5, patience = 20)for"plateau". Unset entries fall back to defaults scaled offepochs/lr.- loss
(
character(6))
Named vector of the different losses. Names should bedbh,ba,trees,growth,mortality, andregeneration. Supported losses aremse,poisson,nbinom,gaussian, andbinomial.binomialis a Bernoulli/binomial negative log-likelihood intended formortality; it expects both the prediction and the observation to be proportions in [0, 1].- weights
(
"auto"ornumeric(6))
Weights of the six losses, in the orderdbh,ba,trees,growth,mortality,regeneration.Weights must account for the raw scale of each loss, not just its importance. The six terms are summed, and their raw magnitudes differ by orders of magnitude: on the bundled FIA data
dbhis an MSE in cm^2 (~430 at the start of training) whilegrowthis an MSE on a ratio (~0.03) — a factor of ~1e4. Weights chosen by importance alone are therefore dominated by whichever response happens to have the largest units, and the rest receive too little gradient to learn from. With equal weights,dbhtakes ~87% of the FIA objective andgrowth~1%;growththen never improves."auto"(the default) fixes this without needing to know the units. Each loss is divided by its intercept-only baseline — the loss you would get by predicting the single best constant (the null model). Every term then measures the same thing on the same scale: the fraction of its own null deviance. For a squared-error term the baseline is exactly \(var(y)\), so this reduces to scaling by \(1/sd(y)^2\); but it generalises to the Poisson, negative-binomial and binomial terms, where a standard deviation is not the right scale. It is the same idea asenv_autoscale = TRUE, applied to the responses rather than the predictors.A useful side effect: the per-response values in
m$historybecome directly interpretable as "how much better than the mean" —1is no better than the intercept, below1is better, above1is worse. The baselines are stored inm$loss_baseline, the resulting weights inm$loss_weights, and both are reported once when fitting starts.Passing a
numeric(6)uses those weights as-is and disables the scaling. On the FIA example, balancing the objective is worth ~+0.24 Spearman on held-outgrowth— far more than tuninglrorepochs. See the "Fitting FINN to forest inventory data" vignette.- optimizer
(
torch_optimizer_generator)
Optimizer from thetorchpackage.- batchsize
(
integer(1))
Batch size, model will be trained in random batch sizes of the data to preserve memory and improve convergence.- device
(
character(1))
Should the model be fitted on the CPU or the GPU (Graphic card). Support is only for NVIDIA GPUs available.- update_step
(
integer(1))
Number of steps for which the gradient should be calculated. Automatic differentation becomes slow for larger update steps and the risk of vanishing gradients increases.- start_time
(
integer(1))
Starting from which year should the model be fitted. Can be used to use on burn-in.- plot_progress
(
logical(1))
Plot fitting progress (losses) or not.- folder
(
character(1))
Path to folder for saving checkpoint models. IfNULL, no models will be saved during the training.- checkpoints
(
integer(1))
Interval size in epochs for saving checkpoint models.- shuffle
(
logical(1))
Shuffle data or not.- record_gradients
(
logical(1))
Record the gradients of all parameters or not. Can get large for many epochs.- env_autoscale
(
logical(1))
IfTRUE, FINN z-standardizes the environmental predictors inenvinternally: the per-variable mean and standard deviation are learned from the trainingenvand stored on the model, then re-applied automatically at everypredict()/simulate()call. This lets you pass raw (untransformed)envfor both calibration and prediction; FINN guarantees an identical transformation at both stages. Recommended (and the default) for numerical stability when predictors are on different scales. SetFALSEto useenvexactly as supplied (e.g. if you have already standardized it yourself). The learned constants are available asmodel$env_scaling.- clip_norm
(
numeric(1)|list())
Gradient-norm budget passed totorch::nn_utils_clip_grad_norm_(), applied separately to each of three parameter groups (mechanistic per-species rates, env-effect networks, loss-distribution nuisance parameters) rather than once globally across all parameters. A single number (default2.0) applies the same budget to every group; a named list/vector keyed by"mechanistic"/"nn"/"loss"overrides individual groups, e.g.clip_norm = list(loss = 5, nn = 1).- ...
Additional arguments passed to
optimizer.
Value
The fitted model, invisibly. fit() trains the model in place, so
the returned object is the one passed in, now carrying the training results
(e.g. $history, $loss_weights, $loss_baseline).
Details
Calibration relies on the fact that FINN is implemented in torch for R and is
therefore fully differentiable: at each simulated time step the predicted stand
variables and demographic rates are compared to the observed data through a
joint loss function, and gradients of this loss with respect to every model
parameter are obtained via automatic differentiation (backpropagation) and used
to update the parameters with a torch optimizer (Adam, torch::optim_ignite_adam,
by default).
The joint loss is the sum of per-variable losses (akin to negative
log-likelihoods), one for each of dbh, ba (basal area), trees (number of
trees), growth, mortality and regeneration. Following Pichler & Käber
(2026), reasonable choices are mean squared error ("mse", equivalent to a
Gaussian likelihood) for dbh and ba, Poisson likelihood for trees,
negative binomial ("nbinom") for regeneration, and "mse" for growth as
a continuous rate. mortality is an observed proportion of trees that died
and the model predicts it through a sigmoid, so it defaults to "binomial" —
a Bernoulli/binomial likelihood (binary cross-entropy, which admits fractional
targets). This respects the [0, 1] support and the mean-variance link of a
proportion, both of which "mse" ignores (the model also supports
"gaussian" and "poisson" as alternatives via the loss argument; see
Appendix B of the paper for details). Each loss can be weighted individually via weights, and
missing values in the observed data are masked out of the corresponding loss
term. The model is trained for epochs iterations over the (optionally
batched and shuffled) data using optimizer with learning rate lr.
Backpropagating gradients through a long simulated time series is prone to
vanishing gradients and is computationally expensive. FINN therefore uses
truncated backpropagation through time: the computational graph is detached
every update_step simulated years, gradients are accumulated and the
parameters are updated, before the simulation continues. Smaller update_step
values are faster and avoid vanishing gradients but provide a more short-sighted
learning signal; larger values let the loss integrate over a longer trajectory
at increased computational cost. start_time allows discarding an initial
burn-in period of the simulation from the loss, and checkpoints/folder
allow periodically saving the model state during training.
growth is compared as a relative rate (dbh/dbh_before - 1), which is
the model's native parameter (dbh_new = dbh * (1 + g)). Training against the
absolute diameter increment (dbh * g) instead was tested over three seeds and
was worse on every one — even when scored on the absolute scale — and about
four times more seed-variable, because it couples the growth parameter to
whichever trees happen to be present.