| ggml_trainer | R Documentation |
Builds the graph and optimizer context for a compiled model and hands them
back, so a training loop can be written in R rather than handed to
ggml_fit. The context lives until $free(), keeping
weights, Adam moments and the compute graph across as many steps as the loop
takes.
ggml_trainer(
model,
batch_size = 32L,
nbatch_logical = NULL,
loss_mask = NULL,
weight_decay = NULL
)
model |
A compiled |
batch_size |
Samples per step. The graph is built for exactly this many;
every batch handed to |
nbatch_logical |
Logical batch size for gradient accumulation. Defaults
to |
loss_mask |
|
weight_decay |
Decoupled weight decay in |
This is the level below ggml_fit_opt_gen. Reach for it when the
loop is not a pass over data: reinforcement learning, where experience comes
from a replay buffer the agent is still filling and a target network is
refreshed every so many steps; curriculum schemes that change the data
distribution as they go; data generated fresh for every step; or anything
where "epoch" is not the unit of progress.
Do not emulate this by calling ggml_fit once per batch: each
such call builds a new optimizer context, so the Adam moments are reset and
every step is the optimizer's first.
An object of class ggml_trainer: a list of functions
$step(x, y, sample_weight = NULL),
$eval(x, y, sample_weight = NULL),
$predict(x, batch_size = NULL, matmul_precision = NULL), $snapshot(),
$update_target(target, tau = 1), $model(),
$calibrate_bn(x), $set_lr(lr), $n_steps(), $free().
tr <- ggml_trainer(model, batch_size = 32L)
on.exit(tr$free())
for (i in seq_len(10000L)) {
b <- sample_batch(replay, 32L) # your own bookkeeping
loss <- tr$step(b$x, b$y)
if (i %% 500L == 0L) target <- tr$model() # refresh a target network
}
model <- tr$model() # weights back into an R model object
tr$free()
$step(x, y, sample_weight) runs one forward and backward pass and
returns that step's loss. Gradient accumulation, if nbatch_logical
asked for it, is tracked inside the context: the optimizer updates the
weights only on the period boundary, and the intervening steps only
accumulate. $eval(x, y, sample_weight) is the same without the
backward pass, for measuring on held-out data.
With loss_mask set, the loss is weighted mean squared error,
sum(w * (pred - y)^2) / (batch * outputs), and $step() and
$eval() take a sample_weight matrix:
loss_mask = 1: sample_weight is [batch, 1] (or
a vector of length batch) – one weight per sample, as in Keras.
loss_mask = <outputs>: sample_weight is
[batch, outputs] – one weight per output element. This is
not Keras' sample_weight: a 0/1 matrix is a mask that
trains only the outputs whose label is known (e.g. one action of a
Q-network), and a 0 removes that element's gradient exactly. The loss is
not renormalised by the number of active elements.
Omitting sample_weight on a weighted trainer means all ones. Only
loss = "mse" supports weights.
Sequential models and functional models (ggml_model) with
one input and one output are supported. Functional models with
several inputs or outputs are refused. For a functional model with dropout
the dropout masks are redrawn before every $step().
$predict(x, batch_size) returns predictions from the current weights,
as ggml_predict would, in eval mode (no dropout). It runs on a
scheduler of its own, created on first use: the weights are copied into a
separate buffer for the call and released after it, so neither the training
graph's buffers nor the optimizer state are touched. batch_size
defaults to the trainer's; x may have any number of rows.
$snapshot() returns an independent copy of the model: its own weight
tensors in its own buffer and its own scheduler, sharing nothing mutable with
the trainer. Later steps do not move it, ggml_predict on it
never touches the training path, and it outlives $free(). It is built
in eval mode, so dropout is off in it. Release it with
ggml_snapshot_free (a finaliser is only a backstop); after that
ggml_predict and $update_target on it are refused with an error.
$update_target(target, tau = 1) moves a snapshot towards the current
weights, \theta' \leftarrow \tau\theta + (1 - \tau)\theta':
tau = 1 is the hard copy of DQN, a small tau the Polyak
average of DDPG/SAC. Non-trainable buffers (a sequential batch-norm's
running mean and variance) are copied whole, never averaged. The target
must have the same weights, by name and shape, or it is refused. The update
goes through host memory, one read and one write per tensor.
The context holds GPU buffers and must be released with $free();
calling it twice is harmless. A trainer that goes out of scope without it is
cleaned up when garbage collected, but that moment is not predictable, so
free it yourself. After $free() every other method errors rather than
touching released memory.
The model object passed in is unchanged – R semantics being what they are,
the trainer works on its own copy of the weights. $model() returns a
model carrying the current ones.
The returned model shares the trainer's backend scheduler, and predicting
with it re-lays the scheduler's buffers for another graph. The trainer
therefore re-allocates its own graph on the first step after any
$model() call; weights and Adam moments are kept. $predict()
needs none of this. Predicting with the
original model object while the trainer is alive is not covered by
this and can crash the session on the next step – take the model from
$model() instead.
A step normalizes each batch by its own statistics and does not update the
running ones a sequential batch_norm predicts with; ggml_fit()
recalibrates them after training, a trainer does not. Call
$calibrate_bn(x) with representative inputs (up to 1024 rows are
used) before predicting, snapshotting or saving – or fix the statistics
with ggml_freeze_bn_stats. Creating a trainer for a model
with unfixed batch-norm layers warns once.
ggml_fit_opt_gen for a generator-driven epoch loop,
ggml_fit for the ordinary in-memory path.
Other optimization:
ggml_fit_opt(),
ggml_fit_opt_gen(),
ggml_fit_opt_multi(),
ggml_opt_alloc(),
ggml_opt_context_optimizer_type(),
ggml_opt_dataset_data(),
ggml_opt_dataset_free(),
ggml_opt_dataset_get_batch(),
ggml_opt_dataset_get_batch_head(),
ggml_opt_dataset_init(),
ggml_opt_dataset_labels(),
ggml_opt_dataset_ndata(),
ggml_opt_dataset_shuffle(),
ggml_opt_dataset_weights(),
ggml_opt_default_params(),
ggml_opt_epoch(),
ggml_opt_eval(),
ggml_opt_fit(),
ggml_opt_fit_multi(),
ggml_opt_free(),
ggml_opt_get_lr(),
ggml_opt_grad_acc(),
ggml_opt_init(),
ggml_opt_init_for_fit(),
ggml_opt_init_for_fit_multi(),
ggml_opt_inputs(),
ggml_opt_labels(),
ggml_opt_loss(),
ggml_opt_loss_i(),
ggml_opt_loss_type_binary_cross_entropy(),
ggml_opt_loss_type_cross_entropy(),
ggml_opt_loss_type_huber(),
ggml_opt_loss_type_mae(),
ggml_opt_loss_type_mean(),
ggml_opt_loss_type_mse(),
ggml_opt_loss_type_sum(),
ggml_opt_loss_type_weighted_mse(),
ggml_opt_n_loss(),
ggml_opt_ncorrect(),
ggml_opt_optimizer_name(),
ggml_opt_optimizer_type_adamw(),
ggml_opt_optimizer_type_sgd(),
ggml_opt_outputs(),
ggml_opt_outputs_i(),
ggml_opt_pred(),
ggml_opt_prepare_alloc(),
ggml_opt_reset(),
ggml_opt_result_accuracy(),
ggml_opt_result_free(),
ggml_opt_result_init(),
ggml_opt_result_loss(),
ggml_opt_result_loss_i(),
ggml_opt_result_n_loss(),
ggml_opt_result_ndata(),
ggml_opt_result_pred(),
ggml_opt_result_reset(),
ggml_opt_set_lr(),
ggml_opt_static_graphs(),
ggml_snapshot_free()
ggml_set_n_threads(1L) # deterministic, single OpenMP pool
n <- 64
x <- matrix(runif(n * 4), nrow = n, ncol = 4)
y <- matrix(0, nrow = n, ncol = 2)
for (i in seq_len(n)) { y[i, if (sum(x[i, ]) > 2) 1L else 2L] <- 1 }
model <- ggml_model_sequential() |>
ggml_layer_dense(8, activation = "relu") |>
ggml_layer_dense(2, activation = "softmax")
model$input_shape <- 4L
model <- ggml_compile(model, optimizer = "adam",
loss = "categorical_crossentropy")
tr <- ggml_trainer(model, batch_size = 16L)
for (i in 1:4) {
idx <- ((i - 1L) * 16L + 1L):(i * 16L)
tr$step(x[idx, , drop = FALSE], y[idx, , drop = FALSE])
}
model <- tr$model()
tr$free()
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.