This base class provides the basic functionality for training and prediction of a neural network. All torch learners should inherit from this class.
Validation
To specify the validation data, you can set the $validate field of the Learner, which can be set to:
NULL: no validationratio: only proportion1 - ratioof the task is used for training andratiois used for validation."test"means that the"test"task of a resampling is used and is not possible when calling$train()manually."predefined": This will use the predefined$internal_valid_taskof amlr3::Task.
This validation data can also be used for early stopping, see the description of the Learner's parameters.
Saving a Learner
In order to save a LearnerTorch for later usage, it is necessary to call the $marshal() method on the Learner
before writing it to disk, as the object will otherwise not be saved correctly.
After loading a marshaled LearnerTorch into R again, you then need to call $unmarshal() to transform it
into a useable state.
Early Stopping and Internal Tuning
In order to prevent overfitting, the LearnerTorch class allows to use early stopping via the patience
and min_delta parameters, see the Learner's parameters.
When tuning a LearnerTorch it is also possible to combine the explicit tuning via mlr3tuning
and the LearnerTorch's internal tuning of the epochs via early stopping.
To do so, you just need to include epochs = to_tune(upper = <upper>, internal = TRUE) in the search space,
where <upper> is the maximally allowed number of epochs, and configure the early stopping.
Network Head and Target Encoding
Torch learners are expected to have the following output:
binary classification:
(batch_size, 1), representing the logits for the positive class.multiclass classification:
(batch_size, n_classes), representing the logits for all classes.regression:
(batch_size, 1)representing the response prediction.
A network may return more than one prediction during training, which is what networks with
auxiliary classifiers such as Inception v3 do.
In this case the network returns a list() of tensors, each with the shape given above, and
the following convention applies:
The first element is the primary prediction. It is the one that is scored by
measures_trainand the one that the network is expected to return when it is in evaluation mode, i.e. when predicting and when calculating the validation scores.The remaining elements are the predictions of the auxiliary classifiers. They only exist to contribute to the loss during training and are never scored.
During training, ContextTorch makes both available: ctx$y_hats is the complete output of
the network, i.e. what the loss is applied to, and ctx$y_hat is always the primary
prediction. For a network that returns a single tensor the two are identical.
Because the configured loss is applied to a single tensor, a learner whose network returns a
list has to wrap it by overloading .loss_fn(), see the list of methods below.
Furthermore, the target encoding is expected to be as follows:
regression: The
numerictarget variable of aTaskRegris encoded as atorch_floatwith shapec(batch_size, 1).binary classification: The
factortarget variable of aTaskClassifis encoded as atorch_floatwith shape(batch_size, 1)where the positive class (Task$positive, which is also ensured to be the first factor level) is1and the negative class is0.multi-class classification: The
factortarget variable of aTaskClassifis a label-encodedtorch_longwith shape(batch_size)where the label-encoding goes from1ton_classes.
Important Runtime Considerations
There are a few hyperparameters settings that can have a considerable impact on the runtime of the learner. These include:
device: Use a GPU if possible.num_threads: Set this to the number of CPU cores available if training on CPU. When resampling, benchmarking or tuning in parallel, each worker usesnum_threadsthreads, so divide the available cores among the workers instead to avoid oversubscribing the machine.tensor_dataset: Set this toTRUE(or"device"if on a GPU) if the dataset fits into memory.batch_size: Especially for very small models, choose a larger batch size.
Also, see the Early Stopping and Internal Tuning section for how to terminate training early.
Model
The Model is a list of class "learner_torch_model" with the following elements:
network:: The trained network.optimizer:: The$state_dict()optimizer used to train the network.loss_fn:: The$state_dict()of the loss used to train the network.callbacks:: The callbacks used to train the network.seed:: The seed that was / is used for training and prediction.epochs:: How many epochs the model was trained for (early stopping).task_col_info:: Adata.table()containing information about the train-task.
Parameters
General:
The parameters of the optimizer, loss and callbacks,
prefixed with "opt.", "loss." and "cb.<callback id>." respectively, as well as:
epochs::integer(1)
The number of epochs.device::character(1)
The device. One of"auto","cpu", or"cuda"or other values defined inmlr_reflections$torch$devices. The value is initialized to"auto", which will select"cuda"if possible, then try"mps"and otherwise fall back to"cpu".num_threads::integer(1)
The number of threads for intraop parallelization (ifdeviceis"cpu"). This value is initialized to 1. When resampling, benchmarking or tuning in parallel, each worker uses this many threads, so divide the available cores among the workers instead of setting this to the number of cores.num_interop_threads::integer(1)
The number of threads for interop parallelization (ifdeviceis"cpu"). Note that this can only be set once per session, so setting this for one learner also changes the behavior of other learners, and a later learner asking for a different value errors.NULL(default) uses whatever is set. In order to use different values for this parameter, use encapsulation to train the learners in separate R sessions.seed::integer(1)or"random"orNULL
The torch seed that is used during training and prediction. This value is initialized to"random", which means that a random seed will be sampled at the beginning of the training phase. This seed (either set or randomly sampled) is available via$model$seedafter training and used during prediction. Note that by setting the seed during the training phase this will mean that by default (i.e. whenseedis"random"), clones of the learner will use a different seed. If set toNULL, no seeding will be done. This parameter only seeds torch's random number generator, it does not seed R's. Anything that is drawn from R's RNG is therefore unaffected by it, so to make those parts reproducible you need to seed R's RNG as well, e.g. viaset.seed().tensor_dataset::logical(1)|"device"
Whether to load all batches at once at the beginning of training and stack them. This is initialized toFALSE. If set to"device", the device of the tensors will be set to the value ofdevice, which can avoid unnecessary moving of tensors between devices. When your dataset fits into memory this will make the loading of batches faster. Note that this should not be set for datasets that containlazy_tensors with random data augmentation, as this augmentation will only be applied once at the beginning of training.
Evaluation:
measures_train::Measureorlist()ofMeasures
Measures to be evaluated during training.measures_valid::Measureorlist()ofMeasures
Measures to be evaluated during validation.eval_freq::integer(1)
How often the train / validation predictions are evaluated usingmeasures_train/measures_valid. This is initialized to1. Note that the final model is always evaluated.
Early Stopping:
patience::integer(1)
This activates early stopping using the validation scores. If the performance of a model does not improve forpatienceevaluation steps, training is ended. Note that this counts evaluation steps, not epochs: wheneval_freqis greater than1,patienceevaluation steps correspond topatience * eval_freqepochs. Note that the final model is stored in the learner, not the best model. This is initialized to0, which means no early stopping. The first entry frommeasures_validis used as the metric. This also requires to specify the$validatefield of the Learner, as well asmeasures_valid. If this is set, the epoch after which no improvement was observed, can be accessed via the$internal_tuned_valuesfield of the learner.min_delta::double(1)
The minimum improvement threshold for early stopping. Is initialized to 0.restore_best_weights::logical(1)
Whether to restore the weights of the best epoch when training ends, instead of keeping those of the last epoch that was trained. Is initialized toFALSE, i.e. the network of the last epoch is stored. Setting this toTRUEmakes the stored network the one of the epoch that$internal_tuned_valuesreports, and costs one additional copy of the network's parameters in memory. Checkpoints written byt_clbk("checkpoint")are unaffected: they always hold the network as training left it.
Dataloader:
batch_size::integer(1)
The batch size used by the training and prediction dataloader. It is required for training (unless abatch_sampleris provided, which already determines the batches) and it is required for prediction (unlessbatch_size_predictis set).batch_size_predict::integer(1)
The batch size used by the prediction dataloader (this includes the validation data during training). When set, it overridesbatch_sizefor prediction. The batch size does not change the predictions, but smaller batches take longer and require less memory.shuffle::logical(1)
Whether to shuffle the instances in the dataset. This is initialized toTRUE, which differs from the default (FALSE). It is ignored when asamplerorbatch_sampleris provided.sampler::torch::sampler
Object that defines how the dataloader draws samples, i.e. the order in which the observations are drawn. This must be the sampler generator (as returned bytorch::sampler()), not an instance, as it is instantiated with the training dataset internally.batch_sampler::torch::sampler
Object that defines how the dataloader draws batches. As forsampler, this must be the generator. When it is provided, the parametersbatch_size,shuffleanddrop_lastare ignored during training, because the batch sampler already determines the batches.num_workers::integer(1)
The number of workers for data loading (batches are loaded in parallel). The default is0, which means that data will be loaded in the main process.collate_fn::function
How to merge a list of samples to form a batch.pin_memory::logical(1)
Whether the dataloader copies tensors into CUDA pinned memory before returning them.drop_last::logical(1)
Whether to drop the last training batch in each epoch during training. Default isFALSE. It is ignored when abatch_sampleris provided.timeout::numeric(1)
The timeout value for collecting a batch from workers. Negative values mean no timeout and the default is-1.worker_init_fn::function(id)
A function that receives the worker id (in[1, num_workers]) and is executed after seeding on the worker but before data loading.worker_globals::list()|character()
When loading data in parallel, this allows to export globals to the workers. If this is a character vector, the objects in the global environment with those names are copied to the workers.worker_packages::character()
Which packages to load on the workers.
Also see torch::dataloader for more information.
Inheriting
There are no separate classes for classification and regression to inherit from.
Instead, the task_type must be specified as a construction argument.
Currently, only classification and regression are supported.
When inheriting from this class, one should overload the following methods:
.network(task, param_vals)
(Task,list()) ->nn_module
Construct atorch::nn_moduleobject for the given task and parameter values, i.e. the neural network that is trained by the learner. Note that a specific output shape is expected from the returned network, see section Network Head and Target Encoding. That section also describes how a network can return more than one prediction during training. You can useoutput_dim_for()to obtain the correct output dimension for a given task..loss_fn(task, param_vals)
(Task,list()) ->nn_module
Construct the loss that is applied to the output of the network. The default implementation generates the loss that was configured by the user, i.e.self$loss$generate(task). Overload this if the network returns more than one prediction and the configured loss has to be wrapped, see theaux_logitsparameter ofclassif.inception_v3..ingress_tokens(task, param_vals)
(Task,list()) -> namedlist()withTorchIngressTokens
Create theTorchIngressTokens that are passed to thetask_datasetconstructor. The number of ingress tokens must correspond to the number of input parameters of the network. If there is more than one input, the names must correspond to the inputs of the network. Seeingress_num,ingress_categ, andingress_ltnsron how to easily create the correct tokens. For more flexibility, you can also directly implement the.dataset(task, param_vals)method, see below..dataset(task, param_vals)
(Task,list()) ->torch::dataset
Create the dataset for the task. Don't implement this if the.ingress_tokens()method is defined. The dataset must return a named list where:xis a list of torch tensors that are the input to the network. For networks with more than one input, the names must correspond to the inputs of the network.yis the target tensor..indexare the indices of the batch (integer()or atorch_int()).
For information on the expected target encoding of
y, see section Network Head and Target Encoding. Moreover, one needs to pay attention respect the row ids of the provided task. It is recommended to relu ontask_datasetfor creating thedataset.
It is also possible to overwrite the private .dataloader() method.
This must respect the dataloader parameters from the ParamSet.
.dataloader(dataset, param_vals)
(dataset,list()) ->torch::dataloader
Create a dataloader from the dataset. Needs to respect at leastbatch_sizeandshuffle(otherwise predictions will be incorrectly ordered). Useget_batch_size(param_vals, "train")to obtain the batch size for the respective phase, which takes thebatch_size_predictparameter into account.
To change the predict types, it is possible to overwrite the method below:
.encode_prediction(predict_tensor, task)
(torch_tensor,Task) ->list()
Take in the raw predictions fromself$network(predict_tensor) and encode them into a format that can be converted to validmlr3predictions usingmlr3::as_prediction_data(). This method must takeself$predict_typeinto account.
While it is possible to add parameters by specifying the param_set construction argument, it is currently
not possible to remove existing parameters, i.e. those listed in section Parameters.
None of the parameters provided in param_set can have an id that starts with "loss.", "opt.",
or "cb.", as these are preserved for the dynamically constructed parameters of the optimizer, the loss function,
and the callbacks.
To perform additional input checks on the task, the private .check_train_task(task, param_vals) and
.check_predict_task(task, param_vals) can be overwritten.
These should return TRUE if the input task is valid and otherwise a string with an error message.
For learners that have other construction arguments that should change the hash of a learner, it is required
to implement the private $.additional_phash_input().
Super class
mlr3::Learner -> LearnerTorch
Active bindings
validateHow to construct the internal validation data. This parameter can be either
NULL, a ratio in $(0, 1)$,"test", or"predefined".loss(
TorchLoss)
The torch loss.optimizer(
TorchOptimizer)
The torch optimizer.callbacks(
list()ofTorchCallbacks)
List of torch callbacks. The ids will be set as the names.internal_valid_scoresRetrieves the internal validation scores as a named
list(). Specify the$validatefield and themeasures_validparameter to configure this. ReturnsNULLif learner is not trained yet.internal_tuned_valuesWhen early stopping is active, this returns a named list with the early-stopped epochs, otherwise an empty list is returned. Returns
NULLif learner is not trained yet.marshaled(
logical(1))
Whether the learner is marshaled.network(
nn_module())
Shortcut forlearner$model$network.param_set(
ParamSet)
The parameter sethash(
character(1))
Hash (unique identifier) for this object.phash(
character(1))
Hash (unique identifier) for this partial object, excluding some components which are varied systematically during tuning (parameter values).
Methods
LearnerTorch$new()
Creates a new instance of this R6 class.
Usage
LearnerTorch$new(
id,
task_type,
param_set,
properties = character(),
man,
label,
feature_types,
optimizer = NULL,
loss = NULL,
packages = character(),
predict_types = NULL,
callbacks = list(),
jittable = FALSE
)Arguments
id(
character(1))
The id for of the new object.task_type(
character(1))
The task type.param_set(
ParamSetoralist())
Either a parameter set, or analist()containing different values of self, e.g.alist(private$.param_set1, private$.param_set2), from which aParamSetcollection should be created.properties(
character())
The properties of the object. Seemlr_reflections$learner_propertiesfor available values.man(
character(1))
String in the format[pkg]::[topic]pointing to a manual page for this object. The referenced help package can be opened via method$help().label(
character(1))
Label for the new instance.feature_types(
character())
The feature types. Seemlr_reflections$task_feature_typesfor available values, Additionally,"lazy_tensor"is supported.optimizer(
NULLorTorchOptimizer)
The optimizer to use for training. Defaults to adam.loss(
NULLorTorchLoss)
The loss to use for training. Defaults to MSE for regression and cross entropy for classification.packages(
character())
The R packages this object depends on.predict_types(
character())
The predict types. Seemlr_reflections$learner_predict_typesfor available values. For regression, the default is"response". For classification, this defaults to"response"and"prob". To deviate from the defaults, it is necessary to overwrite the private$.encode_prediction()method, see section Inheriting.callbacks(
list()ofTorchCallbacks)
The callbacks to use for training. Defaults to an emptylist(), i.e. no callbacks. Within a stage they are called in the order in which they are provided, unless a callback requests otherwise via its$weight, see section Ordering ofCallbackSet.jittable(
logical(1))
Whether the model can be jit-traced. Default isFALSE.