code: def run_eval(
      cfg,
      model: torch.nn.Module,
      val_dataloader: DataLoader,
      val_df: pd.DataFrame,
      mode: str = "validation",
  ) -> Tuple:
      """Runs the evaluation loop.
  
      Args:
          cfg: config object
          model: trained model
          val_dataloader: validation Dataloader
          val_df: validation DataFrame
          mode: validation
  
      Returns:
          Validation loss
      """
  
      with torch.no_grad():
          model.eval()
          val_data: Dict[str, Any] = run_inference(
              cfg, model, val_dataloader, mode
          )  # type: ignore
  
      # Sync validation predictions across GPUs
      if (
          cfg.environment._distributed
          and cfg.environment._distributed_inference
      ):
          for key, value in val_data.items():
              val_data[key] = sync_across_processes(
                  value,
                  cfg.environment._world_size,
                  group=cfg.environment._cpu_comm,
              )
  
      torch.inference_mode(mode=True)
      # Drop any extra observations
      for k, v in val_data.items():
          val_data[k] = v[: len(val_dataloader.dataset)]  # type: ignore
  
      if cfg.environment._local_rank == 0:
          val_data = val_dataloader.dataset.postprocess_output(  # type: ignore
              cfg=cfg, df=val_df, output=val_data
          )
  
      val_loss = 0.0
      val_metric = 0.0
      if cfg.environment._local_rank == 0:
          # Calculate validation loss
          if "loss" in val_data:
              assert isinstance(val_data["loss"], torch.Tensor)
              val_losses = val_data["loss"].float().cpu().numpy()
              val_loss = np.mean(val_losses)
              logger.info(f"Mean {mode} loss: {val_loss:.5f}")
              cfg.logging._logger.log(
                  mode,
                  "loss",
                  val_loss,
                  step=cfg.environment._curr_step,
              )
  
          # Calculate reduced validation metric
          _, _, reduce = cfg.prediction.metric_class.get(
              cfg.prediction.metric
          )
          val_metric = reduce_metric(val_data, reduce=reduce)
  
          logger.info(
              f"{mode.capitalize()} {cfg.prediction.metric}: {val_metric:.5f}"
          )
          cfg.logging._logger.log(
              mode,
              cfg.prediction.metric,
              val_metric,
              step=cfg.environment._curr_step,
          )
  
          # Log plots
          if val_df is not None:
              plot = (
                  cfg.logging.plots_class.plot_validation_predictions(
                      val_outputs=val_data,
                      cfg=cfg,
                      val_df=val_df,
                      mode="validation",
                  )
              )
              log_plot(cfg, plot, "validation_predictions")
  
          save_predictions(cfg, val_data, val_dataloader, val_df, mode)
  
      if cfg.environment._distributed:
          torch.distributed.barrier()
  
      torch.inference_mode(mode=False)
  
      return val_loss, val_metric
  
imports: import os
  import argparse
  import gc
  import logging
  import sys
  import time
  from distutils import util
  from typing import Any, Callable, Dict, Tuple
  import deepspeed
  import numpy
  import pandas
  import torch
  from torch.cuda.amp import GradScaler, autocast
  from torch.utils.data import DataLoader
  from tqdm import tqdm
  from transformers.deepspeed import HfDeepSpeedConfig
  from llm_studio.src.datasets.text_utils import get_tokenizer
  from llm_studio.src.loggers import MainLogger
  from llm_studio.src.trl.trainer import PPOTrainer
  from llm_studio.src.utils.config_utils import load_config_py, load_config_yaml, save_config_yaml
  from llm_studio.src.utils.data_utils import get_data, get_inference_batch_size, get_train_dataloader, get_train_dataset, get_val_dataloader, get_val_dataset
  from llm_studio.src.utils.exceptions import LLMTrainingException
  from llm_studio.src.utils.export_utils import save_prediction_outputs
  from llm_studio.src.utils.gpu_utils import sync_across_processes
  from llm_studio.src.utils.logging_utils import TqdmToLogger, initialize_logging, log_plot, write_flag
  from llm_studio.src.utils.modeling_utils import activate_neftune, check_disk_space, get_ds_config, get_number_of_validation_epochs, get_optimizer, get_scheduler, load_checkpoint, reduce_metric, run_inference, save_checkpoint, save_predictions, unwrap_model, wrap_model_distributed
  from llm_studio.src.utils.utils import kill_ddp_processes, set_environment, set_seed
label_desc: Use image node_id 1 for calling a global function with example usage: run_eval(cfg, model, val_dataloader, val_df, mode) and returns: val_loss,  val_metric