class_docstr: The :class:`~poutyne.ModelBundle` class provides a straightforward experimentation tool for efficient and entirely
  customizable finetuning of the whole neural network training procedure with PyTorch. The
  :class:`~poutyne.ModelBundle` object takes care of the training and testing processes while also managing to keep
  traces of all pertinent information via the automatic logging option.
  
  Use ``ModelBundle.from_*`` methods to instanciate a :class:`~poutyne.ModelBundle`.
class_docstr_tok: ["The",":","class",":","`","~poutyne.ModelBundle","`","class","provides","a","straightforward","experimentation","tool","for","efficient","and","entirely","customizable","finetuning","of","the","whole","neural","network","training","procedure","with","PyTorch",".","The",":","class",":","`","~poutyne.ModelBundle","`","object","takes","care","of","the","training","and","testing","processes","while","also","managing","to","keep","traces","of","all","pertinent","information","via","the","automatic","logging","option",".","Use","``","ModelBundle.from_","*","``","methods","to","instanciate","a",":","class",":","`","~poutyne.ModelBundle","`","."]
docstr: Instanciate a :class:`~poutyne.ModelBundle` from a PyTorch :class:`~torch.nn.Module` instance.
  
  Args:
      directory (str): Path to the model bundle's working directory. Will be used for automatic logging.
      network (torch.nn.Module): A PyTorch network.
      device (Union[torch.torch.device, List[torch.torch.device], str, None]): The device to which the model is
          sent or for multi-GPUs, the list of devices to which the model is to be sent. When using a string for a
          multiple GPUs, the option is "all", for "take them all." By default, the current device is used as the
          main one. If None, the model will be kept on its current device.
          (Default value = None)
      logging (bool): Whether or not to log the model bundle's progress. If true, various logging
          callbacks will be inserted to output training and testing stats as well as to save model checkpoints,
          for example, automatically. See :func:`~ModelBundle.train()` and :func:`~ModelBundle.test()` for more
          details. (Default value = True)
      optimizer (Union[torch.optim.Optimizer, str]): If Pytorch Optimizer, must already be initialized.
          If str, should be the optimizer's name in Pytorch (i.e. 'Adam' for torch.optim.Adam).
          (Default value = 'sgd')
      loss_function(Union[Callable, str], optional) It can be any PyTorch
          loss layer or custom loss function. It can also be a string with the same name as a PyTorch
          loss function (either the functional or object name). The loss function must have the signature
          ``loss_function(input, target)`` where ``input`` is the prediction of the network and ``target``
          is the ground truth. If ``None``, will default to, in priority order, either the model's own
          loss function or the default loss function associated with the ``task``.
          (Default value = None)
      batch_metrics (list): List of functions with the same signature as a loss function or objects with the same
          signature as either :class:`~poutyne.Metric` or :class:`torchmetrics.Metric <torchmetrics.Metric>`. It can
          also be a string with the same name as a PyTorch loss function (either the functional or object name).
          Some metrics, such as  'accuracy' (or just 'acc'), are also available as strings. See :ref:`metrics` and
          the `TorchMetrics documentation <https://torchmetrics.readthedocs.io/en/latest/references/modules.html>`__
          for available metrics.
  
          Batch metric are computed on computed for each batch.
          (Default value = None)
  
          .. warning:: When using this argument, the metrics are computed for each batch. This can significantly slow
              down the compuations depending on the metrics used. This mostly happens on non-decomposable metrics
              such as :class:`torchmetrics.AUROC <torchmetrics.AUROC>` where an ordering of the elements is necessary
              to compute the metric. In such case, we advise to use them as epoch metrics instead.
      epoch_metrics (list): List of functions with the same signature as a loss function or objects with the same
          signature as either :class:`~poutyne.Metric` or :class:`torchmetrics.Metric <torchmetrics.Metric>`. It can
          also be a string with the same name as a PyTorch loss function (either the functional or object name).
          Some metrics, such as  'accuracy' (or just 'acc'), are also available as strings. See :ref:`metrics` and
          the `TorchMetrics documentation <https://torchmetrics.readthedocs.io/en/latest/references/modules.html>`__
          for available metrics.
  
          Epoch metrics are computed only at the end of the epoch.
          (Default value = None)
      monitoring (bool): Whether or not to monitor the training. If True will track the best epoch.
          If False, ``monitor_metric`` and ``monitor_mode`` are not used, and when testing, the last epoch is used
          to test the model instead of the best epoch.
          (Default value = True)
      monitor_metric (str, optional): Which metric to consider for best model performance calculation. Should be
          in the format '{metric_name}' or 'val_{metric_name}' (i.e. 'val_loss'). If None, will follow the value
          suggested by ``task`` or default to 'val_loss'. If ``monitoring`` is set to False, will be ignore.
  
          .. warning:: If you do not plan on using a validation set, you must set the monitor metric to another
              value.
      monitor_mode (str, optional): Which mode, either 'min' or 'max', should be used when considering the
          ``monitor_metric`` value. If None, will follow the value suggested by ``task`` or default to 'min'.
          If ``monitoring`` is set to False, will be ignore.
      task (str, optional): Any str beginning with either 'classif' or 'reg'. Specifying a ``task``
          can assign default values to the ``loss_function``, ``batch_metrics``, ``monitor_mode`` and
          ``monitor_mode``. For ``task`` that begins with 'reg', the only default value is the loss function
          that is the mean squared error. When beginning with 'classif', the default loss function is the
          cross-entropy loss. The default batch metrics will be the accuracy, the default epoch metrics will be
          the F1 score and the default monitoring will be set on 'val_acc' with a 'max' mode.
          (Default value = None)
  
  Examples:
      Using a PyTorch DataLoader, on classification task with SGD optimizer::
  
          import torch
          from torch.utils.data import DataLoader, TensorDataset
          from poutyne import ModelBundle
  
          num_features = 20
          num_classes = 5
  
          # Our training dataset with 800 samples.
          num_train_samples = 800
          train_x = torch.rand(num_train_samples, num_features)
          train_y = torch.randint(num_classes, (num_train_samples, ), dtype=torch.long)
          train_dataset = TensorDataset(train_x, train_y)
          train_generator = DataLoader(train_dataset, batch_size=32)
  
          # Our validation dataset with 200 samples.
          num_valid_samples = 200
          valid_x = torch.rand(num_valid_samples, num_features)
          valid_y = torch.randint(num_classes, (num_valid_samples, ), dtype=torch.long)
          valid_dataset = TensorDataset(valid_x, valid_y)
          valid_generator = DataLoader(valid_dataset, batch_size=32)
  
          # Our network
          pytorch_network = torch.nn.Linear(num_features, num_train_samples)
  
          # Initialization of our experimentation and network training
          exp = ModelBundle.from_network('./simple_example',
                                         pytorch_network,
                                         optimizer='sgd',
                                         task='classif')
          exp.train(train_generator, valid_generator, epochs=5)
  
  The above code will yield an output similar to the below lines. Note the automatic checkpoint saving
  in the model bundle directory when the monitored metric improved.
  
  .. code-block:: none
  
          Epoch 1/5 0.09s Step 25/25: loss: 6.351375, acc: 1.375000, val_loss: 6.236106, val_acc: 5.000000
          Epoch 1: val_acc improved from -inf to 5.00000, saving file to ./simple_example/checkpoint_epoch_1.ckpt
          Epoch 2/5 0.10s Step 25/25: loss: 6.054254, acc: 14.000000, val_loss: 5.944495, val_acc: 19.500000
          Epoch 2: val_acc improved from 5.00000 to 19.50000, saving file to ./simple_example/checkpoint_epoch_2.ckpt
          Epoch 3/5 0.09s Step 25/25: loss: 5.759377, acc: 22.875000, val_loss: 5.655412, val_acc: 21.000000
          Epoch 3: val_acc improved from 19.50000 to 21.00000, saving file to ./simple_example/checkpoint_epoch_3.ckpt
          ...
  
  Training can now easily be resumed from the best checkpoint::
  
          exp.train(train_generator, valid_generator, epochs=10)
  
  .. code-block:: none
  
          Restoring model from ./simple_example/checkpoint_epoch_3.ckpt
          Loading weights from ./simple_example/checkpoint.ckpt and starting at epoch 6.
          Loading optimizer state from ./simple_example/checkpoint.optim and starting at epoch 6.
          Epoch 6/10 0.16s Step 25/25: loss: 4.897135, acc: 22.875000, val_loss: 4.813141, val_acc: 20.500000
          Epoch 7/10 0.10s Step 25/25: loss: 4.621514, acc: 22.625000, val_loss: 4.545359, val_acc: 20.500000
          Epoch 8/10 0.24s Step 25/25: loss: 4.354721, acc: 23.625000, val_loss: 4.287117, val_acc: 20.500000
          ...
  
  Testing is also very intuitive::
  
          exp.test(test_generator)
  
  .. code-block:: none
  
          Restoring model from ./simple_example/checkpoint_epoch_9.ckpt
          Found best checkpoint at epoch: 9
          lr: 0.01, loss: 4.09892, acc: 23.625, val_loss: 4.04057, val_acc: 21.5
          On best model: test_loss: 4.06664, test_acc: 17.5
  
  
  Finally, all the pertinent metrics specified to the ModelBundle at each epoch are stored in a specific logging
  file, found here at './simple_example/log.tsv'.
  
  .. code-block:: none
  
          epoch   time                lr      loss                    acc     val_loss            val_acc
          1           0.0721172170015052  0.01    6.351375141143799       1.375   6.23610631942749        5.0
          2           0.0298177790245972  0.01    6.054253826141357       14.000  5.94449516296386        19.5
          3           0.0637106419890187  0.01    5.759376544952392       22.875  5.65541223526001        21.0
          ...
  
  Also, we could use more than one GPU (on a single node) by using the device argument
  
  .. code-block:: none
  
          # Initialization of our experimentation and network training
          exp = ModelBundle.from_network('./simple_example',
                                         pytorch_network,
                                         optimizer='sgd',
                                         task='classif',
                                         device="all")
          exp.train(train_generator, valid_generator, epochs=5)
docstr_tok: ["Instanciate","a",":","class",":","`","~poutyne.ModelBundle","`","from","a","PyTorch",":","class",":","`","~torch.nn.Module","`","instance",".","Args",":","directory","(","str",")",":","Path","to","the","model","bundle","'s","working","directory",".","Will","be","used","for","automatic","logging",".","network","(","torch.nn.Module",")",":","A","PyTorch","network",".","device","(","Union","[","torch.torch.device",",","List","[","torch.torch.device","]",",","str",",","None","]",")",":","The","device","to","which","the","model","is","sent","or","for","multi-GPUs",",","the","list","of","devices","to","which","the","model","is","to","be","sent",".","When","using","a","string","for","a","multiple","GPUs",",","the","option","is","``","all","''",",","for","``","take","them","all",".","''","By","default",",","the","current","device","is","used","as","the","main","one",".","If","None",",","the","model","will","be","kept","on","its","current","device",".","(","Default","value","=","None",")","logging","(","bool",")",":","Whether","or","not","to","log","the","model","bundle","'s","progress",".","If","true",",","various","logging","callbacks","will","be","inserted","to","output","training","and","testing","stats","as","well","as","to","save","model","checkpoints",",","for","example",",","automatically",".","See",":","func",":","`","~ModelBundle.train","(",")","`","and",":","func",":","`","~ModelBundle.test","(",")","`","for","more","details",".","(","Default","value","=","True",")","optimizer","(","Union","[","torch.optim.Optimizer",",","str","]",")",":","If","Pytorch","Optimizer",",","must","already","be","initialized",".","If","str",",","should","be","the","optimizer","'s","name","in","Pytorch","(","i.e",".","'Adam","'","for","torch.optim.Adam",")",".","(","Default","value","=","'sgd","'",")","loss_function","(","Union","[","Callable",",","str","]",",","optional",")","It","can","be","any","PyTorch","loss","layer","or","custom","loss","function",".","It","can","also","be","a","string","with","the","same","name","as","a","PyTorch","loss","function","(","either","the","functional","or","object","name",")",".","The","loss","function","must","have","the","signature","``","loss_function","(","input",",","target",")","``","where","``","input","``","is","the","prediction","of","the","network","and","``","target","``","is","the","ground","truth",".","If","``","None","``",",","will","default","to",",","in","priority","order",",","either","the","model","'s","own","loss","function","or","the","default","loss","function","associated","with","the","``","task","``",".","(","Default","value","=","None",")","batch_metrics","(","list",")",":","List","of","functions","with","the","same","signature","as","a","loss","function","or","objects","with","the","same","signature","as","either",":","class",":","`","~poutyne.Metric","`","or",":","class",":","`","torchmetrics.Metric","<","torchmetrics.Metric",">","`",".","It","can","also","be","a","string","with","the","same","name","as","a","PyTorch","loss","function","(","either","the","functional","or","object","name",")",".","Some","metrics",",","such","as","'accuracy","'","(","or","just","'acc","'",")",",","are","also","available","as","strings",".","See",":","ref",":","`","metrics","`","and","the","`","TorchMetrics","documentation","<","https",":","\/\/torchmetrics.readthedocs.io\/en\/latest\/references\/modules.html",">","`","__","for","available","metrics",".","Batch","metric","are","computed","on","computed","for","each","batch",".","(","Default","value","=","None",")","..","warning",":",":","When","using","this","argument",",","the","metrics","are","computed","for","each","batch",".","This","can","significantly","slow","down","the","compuations","depending","on","the","metrics","used",".","This","mostly","happens","on","non-decomposable","metrics","such","as",":","class",":","`","torchmetrics.AUROC","<","torchmetrics.AUROC",">","`","where","an","ordering","of","the","elements","is","necessary","to","compute","the","metric",".","In","such","case",",","we","advise","to","use","them","as","epoch","metrics","instead",".","epoch_metrics","(","list",")",":","List","of","functions","with","the","same","signature","as","a","loss","function","or","objects","with","the","same","signature","as","either",":","class",":","`","~poutyne.Metric","`","or",":","class",":","`","torchmetrics.Metric","<","torchmetrics.Metric",">","`",".","It","can","also","be","a","string","with","the","same","name","as","a","PyTorch","loss","function","(","either","the","functional","or","object","name",")",".","Some","metrics",",","such","as","'accuracy","'","(","or","just","'acc","'",")",",","are","also","available","as","strings",".","See",":","ref",":","`","metrics","`","and","the","`","TorchMetrics","documentation","<","https",":","\/\/torchmetrics.readthedocs.io\/en\/latest\/references\/modules.html",">","`","__","for","available","metrics",".","Epoch","metrics","are","computed","only","at","the","end","of","the","epoch",".","(","Default","value","=","None",")","monitoring","(","bool",")",":","Whether","or","not","to","monitor","the","training",".","If","True","will","track","the","best","epoch",".","If","False",",","``","monitor_metric","``","and","``","monitor_mode","``","are","not","used",",","and","when","testing",",","the","last","epoch","is","used","to","test","the","model","instead","of","the","best","epoch",".","(","Default","value","=","True",")","monitor_metric","(","str",",","optional",")",":","Which","metric","to","consider","for","best","model","performance","calculation",".","Should","be","in","the","format","'","{","metric_name","}","'","or","'val_","{","metric_name","}","'","(","i.e",".","'val_loss","'",")",".","If","None",",","will","follow","the","value","suggested","by","``","task","``","or","default","to","'val_loss","'",".","If","``","monitoring","``","is","set","to","False",",","will","be","ignore",".","..","warning",":",":","If","you","do","not","plan","on","using","a","validation","set",",","you","must","set","the","monitor","metric","to","another","value",".","monitor_mode","(","str",",","optional",")",":","Which","mode",",","either","'min","'","or","'max","'",",","should","be","used","when","considering","the","``","monitor_metric","``","value",".","If","None",",","will","follow","the","value","suggested","by","``","task","``","or","default","to","'min","'",".","If","``","monitoring","``","is","set","to","False",",","will","be","ignore",".","task","(","str",",","optional",")",":","Any","str","beginning","with","either","'classif","'","or","'reg","'",".","Specifying","a","``","task","``","can","assign","default","values","to","the","``","loss_function","``",",","``","batch_metrics","``",",","``","monitor_mode","``","and","``","monitor_mode","``",".","For","``","task","``","that","begins","with","'reg","'",",","the","only","default","value","is","the","loss","function","that","is","the","mean","squared","error",".","When","beginning","with","'classif","'",",","the","default","loss","function","is","the","cross-entropy","loss",".","The","default","batch","metrics","will","be","the","accuracy",",","the","default","epoch","metrics","will","be","the","F1","score","and","the","default","monitoring","will","be","set","on","'val_acc","'","with","a","'max","'","mode",".","(","Default","value","=","None",")","Examples",":","Using","a","PyTorch","DataLoader",",","on","classification","task","with","SGD","optimizer",":",":","import","torch","from","torch.utils.data","import","DataLoader",",","TensorDataset","from","poutyne","import","ModelBundle","num_features","=","20","num_classes","=","5","#","Our","training","dataset","with","800","samples",".","num_train_samples","=","800","train_x","=","torch.rand","(","num_train_samples",",","num_features",")","train_y","=","torch.randint","(","num_classes",",","(","num_train_samples",",",")",",","dtype=torch.long",")","train_dataset","=","TensorDataset","(","train_x",",","train_y",")","train_generator","=","DataLoader","(","train_dataset",",","batch_size=32",")","#","Our","validation","dataset","with","200","samples",".","num_valid_samples","=","200","valid_x","=","torch.rand","(","num_valid_samples",",","num_features",")","valid_y","=","torch.randint","(","num_classes",",","(","num_valid_samples",",",")",",","dtype=torch.long",")","valid_dataset","=","TensorDataset","(","valid_x",",","valid_y",")","valid_generator","=","DataLoader","(","valid_dataset",",","batch_size=32",")","#","Our","network","pytorch_network","=","torch.nn.Linear","(","num_features",",","num_train_samples",")","#","Initialization","of","our","experimentation","and","network","training","exp","=","ModelBundle.from_network","(","'.\/simple_example","'",",","pytorch_network",",","optimizer='sgd","'",",","task='classif","'",")","exp.train","(","train_generator",",","valid_generator",",","epochs=5",")","The","above","code","will","yield","an","output","similar","to","the","below","lines",".","Note","the","automatic","checkpoint","saving","in","the","model","bundle","directory","when","the","monitored","metric","improved",".","..","code-block",":",":","none","Epoch","1\/5","0.09s","Step","25\/25",":","loss",":","6.351375",",","acc",":","1.375000",",","val_loss",":","6.236106",",","val_acc",":","5.000000","Epoch","1",":","val_acc","improved","from","-inf","to","5.00000",",","saving","file","to",".\/simple_example\/checkpoint_epoch_1.ckpt","Epoch","2\/5","0.10s","Step","25\/25",":","loss",":","6.054254",",","acc",":","14.000000",",","val_loss",":","5.944495",",","val_acc",":","19.500000","Epoch","2",":","val_acc","improved","from","5.00000","to","19.50000",",","saving","file","to",".\/simple_example\/checkpoint_epoch_2.ckpt","Epoch","3\/5","0.09s","Step","25\/25",":","loss",":","5.759377",",","acc",":","22.875000",",","val_loss",":","5.655412",",","val_acc",":","21.000000","Epoch","3",":","val_acc","improved","from","19.50000","to","21.00000",",","saving","file","to",".\/simple_example\/checkpoint_epoch_3.ckpt","...","Training","can","now","easily","be","resumed","from","the","best","checkpoint",":",":","exp.train","(","train_generator",",","valid_generator",",","epochs=10",")","..","code-block",":",":","none","Restoring","model","from",".\/simple_example\/checkpoint_epoch_3.ckpt","Loading","weights","from",".\/simple_example\/checkpoint.ckpt","and","starting","at","epoch","6",".","Loading","optimizer","state","from",".\/simple_example\/checkpoint.optim","and","starting","at","epoch","6",".","Epoch","6\/10","0.16s","Step","25\/25",":","loss",":","4.897135",",","acc",":","22.875000",",","val_loss",":","4.813141",",","val_acc",":","20.500000","Epoch","7\/10","0.10s","Step","25\/25",":","loss",":","4.621514",",","acc",":","22.625000",",","val_loss",":","4.545359",",","val_acc",":","20.500000","Epoch","8\/10","0.24s","Step","25\/25",":","loss",":","4.354721",",","acc",":","23.625000",",","val_loss",":","4.287117",",","val_acc",":","20.500000","...","Testing","is","also","very","intuitive",":",":","exp.test","(","test_generator",")","..","code-block",":",":","none","Restoring","model","from",".\/simple_example\/checkpoint_epoch_9.ckpt","Found","best","checkpoint","at","epoch",":","9","lr",":","0.01",",","loss",":","4.09892",",","acc",":","23.625",",","val_loss",":","4.04057",",","val_acc",":","21.5","On","best","model",":","test_loss",":","4.06664",",","test_acc",":","17.5","Finally",",","all","the","pertinent","metrics","specified","to","the","ModelBundle","at","each","epoch","are","stored","in","a","specific","logging","file",",","found","here","at","'.\/simple_example\/log.tsv","'",".","..","code-block",":",":","none","epoch","time","lr","loss","acc","val_loss","val_acc","1","0.0721172170015052","0.01","6.351375141143799","1.375","6.23610631942749","5.0","2","0.0298177790245972","0.01","6.054253826141357","14.000","5.94449516296386","19.5","3","0.0637106419890187","0.01","5.759376544952392","22.875","5.65541223526001","21.0","...","Also",",","we","could","use","more","than","one","GPU","(","on","a","single","node",")","by","using","the","device","argument","..","code-block",":",":","none","#","Initialization","of","our","experimentation","and","network","training","exp","=","ModelBundle.from_network","(","'.\/simple_example","'",",","pytorch_network",",","optimizer='sgd","'",",","task='classif","'",",","device=","''","all","''",")","exp.train","(","train_generator",",","valid_generator",",","epochs=5",")"]
code: def from_network(
      cls,
      directory: str,
      network: torch.nn.Module,
      *,
      device: Union[
          torch.device, List[torch.device], List[str], None, str
      ] = None,
      logging: bool = True,
      optimizer: Union[torch.optim.Optimizer, str] = "sgd",
      loss_function: Union[Callable, str] = None,
      batch_metrics: Union[List, None] = None,
      epoch_metrics: Union[List, None] = None,
      monitoring: bool = True,
      monitor_metric: Union[str, None] = None,
      monitor_mode: Union[str, None] = None,
      task: Union[str, None] = None,
  ):
      # pylint: disable=line-too-long
      """
      Instanciate a :class:`~poutyne.ModelBundle` from a PyTorch :class:`~torch.nn.Module` instance.
  
      Args:
          directory (str): Path to the model bundle's working directory. Will be used for automatic logging.
          network (torch.nn.Module): A PyTorch network.
          device (Union[torch.torch.device, List[torch.torch.device], str, None]): The device to which the model is
              sent or for multi-GPUs, the list of devices to which the model is to be sent. When using a string for a
              multiple GPUs, the option is "all", for "take them all." By default, the current device is used as the
              main one. If None, the model will be kept on its current device.
              (Default value = None)
          logging (bool): Whether or not to log the model bundle's progress. If true, various logging
              callbacks will be inserted to output training and testing stats as well as to save model checkpoints,
              for example, automatically. See :func:`~ModelBundle.train()` and :func:`~ModelBundle.test()` for more
              details. (Default value = True)
          optimizer (Union[torch.optim.Optimizer, str]): If Pytorch Optimizer, must already be initialized.
              If str, should be the optimizer's name in Pytorch (i.e. 'Adam' for torch.optim.Adam).
              (Default value = 'sgd')
          loss_function(Union[Callable, str], optional) It can be any PyTorch
              loss layer or custom loss function. It can also be a string with the same name as a PyTorch
              loss function (either the functional or object name). The loss function must have the signature
              ``loss_function(input, target)`` where ``input`` is the prediction of the network and ``target``
              is the ground truth. If ``None``, will default to, in priority order, either the model's own
              loss function or the default loss function associated with the ``task``.
              (Default value = None)
          batch_metrics (list): List of functions with the same signature as a loss function or objects with the same
              signature as either :class:`~poutyne.Metric` or :class:`torchmetrics.Metric <torchmetrics.Metric>`. It can
              also be a string with the same name as a PyTorch loss function (either the functional or object name).
              Some metrics, such as  'accuracy' (or just 'acc'), are also available as strings. See :ref:`metrics` and
              the `TorchMetrics documentation <https://torchmetrics.readthedocs.io/en/latest/references/modules.html>`__
              for available metrics.
  
              Batch metric are computed on computed for each batch.
              (Default value = None)
  
              .. warning:: When using this argument, the metrics are computed for each batch. This can significantly slow
                  down the compuations depending on the metrics used. This mostly happens on non-decomposable metrics
                  such as :class:`torchmetrics.AUROC <torchmetrics.AUROC>` where an ordering of the elements is necessary
                  to compute the metric. In such case, we advise to use them as epoch metrics instead.
          epoch_metrics (list): List of functions with the same signature as a loss function or objects with the same
              signature as either :class:`~poutyne.Metric` or :class:`torchmetrics.Metric <torchmetrics.Metric>`. It can
              also be a string with the same name as a PyTorch loss function (either the functional or object name).
              Some metrics, such as  'accuracy' (or just 'acc'), are also available as strings. See :ref:`metrics` and
              the `TorchMetrics documentation <https://torchmetrics.readthedocs.io/en/latest/references/modules.html>`__
              for available metrics.
  
              Epoch metrics are computed only at the end of the epoch.
              (Default value = None)
          monitoring (bool): Whether or not to monitor the training. If True will track the best epoch.
              If False, ``monitor_metric`` and ``monitor_mode`` are not used, and when testing, the last epoch is used
              to test the model instead of the best epoch.
              (Default value = True)
          monitor_metric (str, optional): Which metric to consider for best model performance calculation. Should be
              in the format '{metric_name}' or 'val_{metric_name}' (i.e. 'val_loss'). If None, will follow the value
              suggested by ``task`` or default to 'val_loss'. If ``monitoring`` is set to False, will be ignore.
  
              .. warning:: If you do not plan on using a validation set, you must set the monitor metric to another
                  value.
          monitor_mode (str, optional): Which mode, either 'min' or 'max', should be used when considering the
              ``monitor_metric`` value. If None, will follow the value suggested by ``task`` or default to 'min'.
              If ``monitoring`` is set to False, will be ignore.
          task (str, optional): Any str beginning with either 'classif' or 'reg'. Specifying a ``task``
              can assign default values to the ``loss_function``, ``batch_metrics``, ``monitor_mode`` and
              ``monitor_mode``. For ``task`` that begins with 'reg', the only default value is the loss function
              that is the mean squared error. When beginning with 'classif', the default loss function is the
              cross-entropy loss. The default batch metrics will be the accuracy, the default epoch metrics will be
              the F1 score and the default monitoring will be set on 'val_acc' with a 'max' mode.
              (Default value = None)
  
      Examples:
          Using a PyTorch DataLoader, on classification task with SGD optimizer::
  
              import torch
              from torch.utils.data import DataLoader, TensorDataset
              from poutyne import ModelBundle
  
              num_features = 20
              num_classes = 5
  
              # Our training dataset with 800 samples.
              num_train_samples = 800
              train_x = torch.rand(num_train_samples, num_features)
              train_y = torch.randint(num_classes, (num_train_samples, ), dtype=torch.long)
              train_dataset = TensorDataset(train_x, train_y)
              train_generator = DataLoader(train_dataset, batch_size=32)
  
              # Our validation dataset with 200 samples.
              num_valid_samples = 200
              valid_x = torch.rand(num_valid_samples, num_features)
              valid_y = torch.randint(num_classes, (num_valid_samples, ), dtype=torch.long)
              valid_dataset = TensorDataset(valid_x, valid_y)
              valid_generator = DataLoader(valid_dataset, batch_size=32)
  
              # Our network
              pytorch_network = torch.nn.Linear(num_features, num_train_samples)
  
              # Initialization of our experimentation and network training
              exp = ModelBundle.from_network('./simple_example',
                                             pytorch_network,
                                             optimizer='sgd',
                                             task='classif')
              exp.train(train_generator, valid_generator, epochs=5)
  
      The above code will yield an output similar to the below lines. Note the automatic checkpoint saving
      in the model bundle directory when the monitored metric improved.
  
      .. code-block:: none
  
              Epoch 1/5 0.09s Step 25/25: loss: 6.351375, acc: 1.375000, val_loss: 6.236106, val_acc: 5.000000
              Epoch 1: val_acc improved from -inf to 5.00000, saving file to ./simple_example/checkpoint_epoch_1.ckpt
              Epoch 2/5 0.10s Step 25/25: loss: 6.054254, acc: 14.000000, val_loss: 5.944495, val_acc: 19.500000
              Epoch 2: val_acc improved from 5.00000 to 19.50000, saving file to ./simple_example/checkpoint_epoch_2.ckpt
              Epoch 3/5 0.09s Step 25/25: loss: 5.759377, acc: 22.875000, val_loss: 5.655412, val_acc: 21.000000
              Epoch 3: val_acc improved from 19.50000 to 21.00000, saving file to ./simple_example/checkpoint_epoch_3.ckpt
              ...
  
      Training can now easily be resumed from the best checkpoint::
  
              exp.train(train_generator, valid_generator, epochs=10)
  
      .. code-block:: none
  
              Restoring model from ./simple_example/checkpoint_epoch_3.ckpt
              Loading weights from ./simple_example/checkpoint.ckpt and starting at epoch 6.
              Loading optimizer state from ./simple_example/checkpoint.optim and starting at epoch 6.
              Epoch 6/10 0.16s Step 25/25: loss: 4.897135, acc: 22.875000, val_loss: 4.813141, val_acc: 20.500000
              Epoch 7/10 0.10s Step 25/25: loss: 4.621514, acc: 22.625000, val_loss: 4.545359, val_acc: 20.500000
              Epoch 8/10 0.24s Step 25/25: loss: 4.354721, acc: 23.625000, val_loss: 4.287117, val_acc: 20.500000
              ...
  
      Testing is also very intuitive::
  
              exp.test(test_generator)
  
      .. code-block:: none
  
              Restoring model from ./simple_example/checkpoint_epoch_9.ckpt
              Found best checkpoint at epoch: 9
              lr: 0.01, loss: 4.09892, acc: 23.625, val_loss: 4.04057, val_acc: 21.5
              On best model: test_loss: 4.06664, test_acc: 17.5
  
  
      Finally, all the pertinent metrics specified to the ModelBundle at each epoch are stored in a specific logging
      file, found here at './simple_example/log.tsv'.
  
      .. code-block:: none
  
              epoch	time	            lr	    loss	            acc	    val_loss	        val_acc
              1	    0.0721172170015052	0.01	6.351375141143799	1.375	6.23610631942749	5.0
              2	    0.0298177790245972	0.01	6.054253826141357	14.000	5.94449516296386	19.5
              3	    0.0637106419890187	0.01	5.759376544952392	22.875	5.65541223526001	21.0
              ...
  
      Also, we could use more than one GPU (on a single node) by using the device argument
  
      .. code-block:: none
  
              # Initialization of our experimentation and network training
              exp = ModelBundle.from_network('./simple_example',
                                             pytorch_network,
                                             optimizer='sgd',
                                             task='classif',
                                             device="all")
              exp.train(train_generator, valid_generator, epochs=5)
  
      """
      if (
          task is not None
          and not task.startswith("classif")
          and not task.startswith("reg")
      ):
          raise ValueError(f"Invalid task '{task}'")
  
      batch_metrics = [] if batch_metrics is None else batch_metrics
      epoch_metrics = [] if epoch_metrics is None else epoch_metrics
  
      loss_function = cls._get_loss_function(
          loss_function, network, task
      )
      batch_metrics = cls._get_batch_metrics(
          batch_metrics, network, task
      )
      epoch_metrics = cls._get_epoch_metrics(
          epoch_metrics, network, task
      )
  
      (
          monitoring,
          monitor_metric,
          monitor_mode,
      ) = cls._get_monitoring_config(
          monitoring, monitor_metric, monitor_mode, task
      )
  
      model = Model(
          network,
          optimizer,
          loss_function,
          batch_metrics=batch_metrics,
          epoch_metrics=epoch_metrics,
          device=device,
      )
  
      return ModelBundle(
          directory,
          model,
          logging=logging,
          monitoring=monitoring,
          monitor_metric=monitor_metric,
          monitor_mode=monitor_mode,
          _is_direct=False,
      )
  
imports: import os
  import warnings
  from typing import Any, Callable, Dict, List, Tuple, Union
  import torch
  from poutyne.framework.callbacks import AtomicCSVLogger, BestModelRestore, LRSchedulerCheckpoint, ModelCheckpoint, OptimizerCheckpoint, PeriodicSaveLambda, RandomStatesCheckpoint, TensorBoardLogger
  from poutyne.framework.model import Model
  from poutyne.plotting import plot_history
  from poutyne.utils import load_random_states, set_seeds
label_desc: Use image node_id 2 for calling the ModelBundle obj's underlying member method code with example usage: obj.from_network(cls, directory, network) and returns: ModelBundle