class_docstr: This class defines the training and validation code for training models with CVNets
code: def run(self, train_sampler=None):
      if train_sampler is None and self.is_master_node:
          logger.error("Train sampler cannot be None")
  
      copy_at_epoch = getattr(self.opts, "ema.copy_at_epoch", -1)
      train_start_time = time.time()
  
      cfg_file = getattr(self.opts, "common.config_file", None)
      if cfg_file is not None and self.is_master_node:
          dst_cfg_file = "{}/config.yaml".format(self.save_location)
          shutil.copy(src=cfg_file, dst=dst_cfg_file)
          logger.info(
              "Configuration file is stored here: {}".format(
                  logger.color_text(dst_cfg_file)
              )
          )
  
      keep_k_best_ckpts = getattr(
          self.opts, "common.k_best_checkpoints", 5
      )
      ema_best_metric = self.best_metric
      is_ema_best = False
  
      try:
          max_epochs = getattr(
              self.opts, "scheduler.max_epochs", DEFAULT_EPOCHS
          )
          max_checkpoint_metric = getattr(
              self.opts, "stats.checkpoint_metric_max", False
          )
          for epoch in range(self.start_epoch, max_epochs):
              # Note that we are using our owm implementations of data samplers
              # and we have defined this function for both distributed and non-distributed cases
              train_sampler.set_epoch(epoch)
              train_sampler.update_scales(
                  epoch=epoch, is_master_node=self.is_master_node
              )
  
              train_loss, train_ckpt_metric = self.train_epoch(epoch)
  
              val_loss, val_ckpt_metric = self.val_epoch(
                  epoch=epoch, model=self.model
              )
  
              if epoch == copy_at_epoch and self.model_ema is not None:
                  if self.is_master_node:
                      logger.log("Copying EMA weights")
                  # copy model_src weights to model_tgt
                  self.model = copy_weights(
                      model_tgt=self.model, model_src=self.model_ema
                  )
                  if self.is_master_node:
                      logger.log("EMA weights copied")
                      logger.log(
                          "Running validation after Copying EMA model weights"
                      )
                  self.val_epoch(epoch=epoch, model=self.model)
  
              if max_checkpoint_metric:
                  is_best = val_ckpt_metric >= self.best_metric
                  self.best_metric = max(
                      val_ckpt_metric, self.best_metric
                  )
              else:
                  is_best = val_ckpt_metric <= self.best_metric
                  self.best_metric = min(
                      val_ckpt_metric, self.best_metric
                  )
  
              val_ema_loss = None
              val_ema_ckpt_metric = None
              if self.model_ema is not None:
                  val_ema_loss, val_ema_ckpt_metric = self.val_epoch(
                      epoch=epoch,
                      model=self.model_ema.ema_model,
                      extra_str=" (EMA)",
                  )
                  if max_checkpoint_metric:
                      is_ema_best = (
                          val_ema_ckpt_metric >= ema_best_metric
                      )
                      ema_best_metric = max(
                          val_ema_ckpt_metric, ema_best_metric
                      )
                  else:
                      is_ema_best = (
                          val_ema_ckpt_metric <= ema_best_metric
                      )
                      ema_best_metric = min(
                          val_ema_ckpt_metric, ema_best_metric
                      )
  
              # sample efficient training
              if (
                  self.sample_efficient_training
                  and (epoch + 1) % self.find_easy_samples_every_k_epoch
                  == 0
              ):
                  self.find_easy_samples(
                      epoch=epoch,
                      model=self.model
                      if self.model_ema is not None
                      else self.model_ema.ema_model,
                  )
  
              gc.collect()
  
              if self.is_master_node:
                  save_checkpoint(
                      iterations=self.train_iterations,
                      epoch=epoch,
                      model=self.model,
                      optimizer=self.optimizer,
                      best_metric=self.best_metric,
                      is_best=is_best,
                      save_dir=self.save_location,
                      model_ema=self.model_ema,
                      is_ema_best=is_ema_best,
                      ema_best_metric=ema_best_metric,
                      gradient_scaler=self.gradient_scaler,
                      max_ckpt_metric=max_checkpoint_metric,
                      k_best_checkpoints=keep_k_best_ckpts,
                      save_all_checkpoints=self.save_all_checkpoints,
                  )
                  logger.info(
                      "Checkpoints saved at: {}".format(
                          self.save_location
                      ),
                      print_line=True,
                  )
  
              if self.is_master_node:
                  lr_list = self.scheduler.retrieve_lr(self.optimizer)
  
                  for log_writer in self.log_writers:
                      log_metrics(
                          lrs=lr_list,
                          log_writer=log_writer,
                          train_loss=train_loss,
                          val_loss=val_loss,
                          epoch=epoch,
                          best_metric=self.best_metric,
                          val_ema_loss=val_ema_loss,
                          ckpt_metric_name=self.ckpt_metric,
                          train_ckpt_metric=train_ckpt_metric,
                          val_ckpt_metric=val_ckpt_metric,
                          val_ema_ckpt_metric=val_ema_ckpt_metric,
                      )
  
              if self.max_iterations_reached:
                  if self.use_distributed:
                      dist_barrier()
  
                  if self.is_master_node:
                      logger.info(
                          "Max. iterations for training reached"
                      )
                  break
      except KeyboardInterrupt as e:
          if self.is_master_node:
              logger.log(
                  "Keyboard interruption. Exiting from early training"
              )
              raise e
      except Exception as e:
          if "out of memory" in str(e):
              logger.log("OOM exception occured")
              n_gpus = getattr(self.opts, "dev.num_gpus", 1)
              for dev_id in range(n_gpus):
                  mem_summary = torch.cuda.memory_summary(
                      device=torch.device("cuda:{}".format(dev_id)),
                      abbreviated=True,
                  )
                  logger.log(
                      "Memory summary for device id: {}".format(dev_id)
                  )
                  print(mem_summary)
  
          logger.log(
              f"Exception occurred that interrupted the training:\n{traceback.format_exc()}"
          )
          raise e
      finally:
          use_distributed = getattr(
              self.opts, "ddp.use_distributed", False
          )
          if use_distributed:
              torch.distributed.destroy_process_group()
  
          torch.cuda.empty_cache()
  
          for log_writer in self.log_writers:
              log_writer.close()
  
          if self.is_master_node:
              train_end_time = time.time()
              hours, rem = divmod(
                  train_end_time - train_start_time, 3600
              )
              minutes, seconds = divmod(rem, 60)
              train_time_str = "{:0>2}:{:0>2}:{:05.2f}".format(
                  int(hours), int(minutes), seconds
              )
              logger.log("Training took {}".format(train_time_str))
  
imports: import copy
  import gc
  import shutil
  import time
  import traceback
  from itertools import product
  from typing import Dict, Union
  import numpy
  import torch
  from torch import Tensor
  from torch.nn import functional
  from common import DEFAULT_EPOCHS, DEFAULT_ITERATIONS, DEFAULT_LOG_FREQ, if_test_env
  from data.transforms.image_torch import apply_mixing_transforms
  from engine.utils import autocast_fn, get_batch_size, get_log_writers, log_metrics
  from loss_landscape import landscape_utils
  from metrics.stats import Statistics
  from options.parse_args import parse_validation_metric_names
  from utils import logger
  from utils.checkpoint_utils import copy_weights, save_checkpoint, save_interval_checkpoint
  from utils.common_utils import move_to_device, unwrap_model_fn
  from utils.ddp_utils import dist_barrier, is_master
  from utils.tensor_utils import reduce_tensor_sum
label_desc: Use image node_id 7 for calling the Trainer obj's underlying member method code with example usage: obj.run(train_sampler) without return types