Package: pl-crossvalidate
PL CrossvalidateCross validation in pytorch lightning made easy :]Just import the specialized trainer frompl_crossvalidateinstead ofpytorch_lightningand you are set# To distinguish from the original trainer the new trainer is called KFoldTrainer by defaultfrompl_crossvalidateimportKFoldTrainerasTrainer# Normal Lightning modulemodel=MyModel(...)# Use a Lightning datamodule or training dataloaderdatamodule=MyDatamodule(...)# New trainer takes all original arguments + three new for controling the cross validationtrainer=Trainer(num_folds=5,# number of folds to doshuffle=False,# if samples should be shuffled before splittingstratified=False,# if splitting should be done in a stratified manneraccelerator=...,callbacks=...,...)# Returns a dict of stats over the different splitscross_val_stats=trainer.cross_validate(model,datamodule=datamodule)# Additionally, we can construct an ensemble from the K trained modelsensemble_model=trainer.create_ensemble(model)💻 Installationpipinstallpl-crossvalidateOr latest version from githubpipinstallhttps://github.com/SkafteNicki/pl_crossvalidate/archive/master.zipRequirestorch>=2.0,lightning>=2.0andscikit-learn>=1.0.🤔 Cross-validation: why?The core functionality of machine learning algorithms is that they are able tolearnfrom data. Therefore, it is very
interesting to ask the question: howwelldoes our algorithms actually learn?. This is in abstract question, because
it requires us to define whatwellmeans. One interpretation of this question is an algorithms ability togeneralizee.g. a model that generalizes well have actually learned something meaningfull.The mathematical definition of the generalization error/expected loss/risk is given bywhereis some functiondenotes the loss function andis
the joint probability distribution betweenand. This is the theoretical error an algorithm will do
on some unobserved dataset. The problem with this definition is that we cannot compute it, due tobeing unknown and even if we knew it
the integral is intractable. The best we therefore can do is anapproximationof the generalization error:which measures the error that our functiondoes ondatapoints measured by loss
function. This function we can
compute (just think of this as your normal loss function) and we even know thatNamely that approximation of the generalization error will become the true generalization error if we just evaluate it
on enough data. But how does all this related to cross-validation you may ask? The problem with the above is thatis not a fixed function, but
data-dependent function i.e..
Thus, the above approximation will only converge ifandrefers to different sets of
data points. This is where cross-validation strategies comes into play.Hold outK-foldIn general we consider two viable strategies for selecting the(validation) and(training) set: hold-out validation
and K-fold cross validation. In hold out we create a separate independent set of data to evaluate our training on. This
is easily done in native pytorch-lightning by implementing thevalidation_stepmethod. For K-fold we cut our data
into K equally large chunks and then we iteratively train on K-1 folds and evaluate on the remaining 1 fold, repeating
this K times. In general K-fold gives a better approximation of the generalization error than hold-out, but at the
expense of requiring you to train K models.🗒️ Some notesFor the.cross_validatemethod to work, we in addition to the standard set of method in lightning that need
to be implemented (training_stepandconfigure_optimizers) we also requires thetest_stepmethod to be
implemented, as we use this method evaluating the hold out set. We do not rely on thevalidation_stepmethod
as your models training may be dependent on the validation set (for example if you use early stopping) and your
validation set will therefore not be truly separated from the training.To do the splitting in cross validation we need the total number of data points in your dataset. For this reason,
we require that your dataset implements the__len__method.Cross validation is always done sequentially, even if the device you are training on in principal could
fit parallel training on multiple folds at the same time. We try to figure out in the future if we can
parallelize the process.Logging can be a bit weird. Logging of training progress is essentially not important to cross-validation,
but that does not mean that it is interesting to track. The cross-validation method will hijack theversionattribute of any logger attached to the trainer and set the logging directory tof"{version}/fold_{fold_index}".Stratified splitting assume that we can extract a 1D label vector from your dataset.If your dataset has anlabelsattribute, we will use that as the labelsIf the attribute does not exist, we manually iterate over your dataset trying to extract the labels when creating
the splits (this is done as part of.setupphase of the datamodule). By default we assume that given abatchthe labels can be found as the second argument e.g.batch[1]. You can adjust this by importing the specializedKFoldDataModuleand changing thelabel_extractorattribute. For example, if your batches are dictionaries
instead you can do something like this:frompl_crossvalidateimportKFoldDataModule,KFoldTrainermodel=...trainer=KFoldTrainer(...)datamodule=KFoldDataModule(num_folds,shuffle,stratified,# these should match how the trainer is initializedtrain_dataloader=my_train_dataloader,)# change the label extractor function, such that it will return the labels for a given batchdatamodule.label_extractor=lambdabatch:batch['y']trainer.cross_validate(model,datamodule=datamodule)😃 BibtexIf you want to cite the framework feel free to use this:@article{software:pl_crossvalidate,title={PL Crossvalidate},author={Nicki S. Detlefsen},journal={GitHub. Note: https://github.com/SkafteNicki/pl_crossvalidate},year={2023}}