class_docstr: Model Checkpoint to save model weights during training. 'Best' is determined by minimizing (or maximizing) the value found under monitored_log_key in the logs
  Saved checkpoints contain these keys by default:
      'run_id'
      'epoch'
      'loss_type'
      'loss_val'
      'best_epoch'
      - plus any additional key/value pairs produced by custom_func
  
  Additionally saves a .json file with statistics about the run such as:
       'run_id'
       'num_epochs'
       'best_epoch'
       'best_loss_or_gain'
       'metric_name'
       - plus any additional key/value pairs produced by custom_func
  
  :param run_id: (string):
      Uniquely identifies the run
  :param monitored_log_key: (string):
      Name of the key in the logs that will contain the value we want to minimize (and thus that will dictate whether the model is 'best')
  :param save_dir: (string):
      Path indicating where to save the checkpoint
  :param addl_k_v: (dict):
      dictionary of additional key/value pairs to save with the model. Typically these include some initialization parameters, name of the model etc.
      (e.g. from the initialization dictionary 'opt'), as well as other useful params (e.g. mean, std, proc_type: gpu/cpu etc)
  :param epoch_log_keys: (list):
      list of keys to save from the epoch log dictionary (Note: the logs dictionary is automatically provided by the learning framework)
  :param save_interval: (int):
      How often to save the model (if none then will default to every 5 iterations)
  :param save_best_only: (bool):
      Whether only to save the best result (and overwrite all previous)
      Default: False
  :param max_saves: (integer > 0 or -1):
      the max number of models to save. Older model checkpoints will be overwritten if necessary.
      Set equal to -1 to have no limit.
      Default: 5
  :param custom_func: func(k_v_dict, logs, out_dict, monitored_log_key, is_end_training):
      Custom function for performing any additional logic (to add values to the model). The function will be passed the addl_k_v dictionary,
      the event logs dictionary, an output dictionary to process, the monitored_log_key and a bool indicating whether the training is finished.
      The function is expected to modify the output dictionary in order to preserve values across epochs. The function will be called at the
      end of each epoch and at the end of the training (with is_end_traing = True)
  :param do_minimize: (bool):
      whether to minimize or maximize the 'monitored_log_key' value
  :param verbose: (bool):
      verbosity of the console output
      Default: False
code: def on_train_end(self, logs=None):
      final_epoch = self.last_epoch
      current_loss = self.last_epoch_logs[self.monitored_log_key]
  
      ## Save model if it hasn't been previously saved and it has best loss value
      if self.last_saved_ep < final_epoch and (
          (self.do_minimize and current_loss < self.best_loss)
          or (not self.do_minimize and current_loss > self.best_loss)
      ):
          # Call custom function (if set) to process things like best-N results etc
          if self.custom_func is not None:
              self.custom_func(
                  self.addl_k_v,
                  self.last_epoch_logs,
                  self.custom_func_dict,
                  False,
              )
  
          self.best_loss = current_loss
          self.best_epoch = final_epoch
          save_dict = {
              "run_id": self.run_id,
              "epoch": final_epoch + 1,
              "state_dict": self.trainer.model.state_dict(),
              "metric_type": self.monitored_log_key,
              "metric_value": current_loss,
              "best_epoch": self.best_epoch,
          }
          # add values from other dictionaries
          save_dict.update(self.addl_k_v)
          save_dict.update(self.custom_func_dict)
          for key in self.epoch_log_keys:
              save_dict[key] = self.last_epoch_logs[key]
  
          save_checkpoint(
              save_dict,
              is_best=True,
              save_path=self.save_dir,
              filename=generate_checkpoint_name(
                  self.run_id, self.addl_k_v, final_epoch, False
              ),
          )
          self.last_saved_ep = final_epoch
  
      stats = {
          "run_id": self.run_id,
          "num_epochs": final_epoch + 1,
          "best_epoch": self.best_epoch + 1,
          "best_loss_or_gain": self.best_loss,
          "metric_type": self.monitored_log_key,
      }
      stats.update(self.addl_k_v)
      stats.update(self.custom_func_dict)
      statsfile_path = generate_statsfile_name(
          self.run_id, self.save_dir
      )
      with open(statsfile_path, "a") as statsfile:
          json.dump(stats, statsfile)
  
file_path: pywick/pywick/callbacks/ModelCheckpoint.py
imports: import json
  import math
  import os
  import shutil
  import torch
  from .None import Callback
label_desc: Use image node_id 3 for calling the ModelCheckpoint obj's underlying member method code with example usage: obj.on_train_end(logs) without return types