docstr: Called at the end of validation to aggregate outputs.
  outputs: list of individual outputs of each validation step.
docstr_tok: ["Called","at","the","end","of","validation","to","aggregate","outputs",".","outputs",":","list","of","individual","outputs","of","each","validation","step","."]
code: def multi_validation_epoch_end(
      self, outputs, dataloader_idx: int = 0
  ):
      """
      Called at the end of validation to aggregate outputs.
      outputs: list of individual outputs of each validation step.
      """
      avg_loss = torch.stack(
          [x["val_loss"] for x in self.validation_step_outputs]
      ).mean()
      preds = torch.cat(
          [
              x["eval_tensors"]["preds"]
              for x in self.validation_step_outputs
          ]
      )
      labels = torch.cat(
          [
              x["eval_tensors"]["labels"]
              for x in self.validation_step_outputs
          ]
      )
  
      all_preds = []
      all_labels = []
      if torch.distributed.is_initialized():
          world_size = torch.distributed.get_world_size()
          for ind in range(world_size):
              all_preds.append(torch.empty_like(preds))
              all_labels.append(torch.empty_like(labels))
          torch.distributed.all_gather(all_preds, preds)
          torch.distributed.all_gather(all_labels, labels)
      else:
          all_preds.append(preds)
          all_labels.append(labels)
  
      if (
          not torch.distributed.is_initialized()
          or torch.distributed.get_rank() == 0
      ):
          preds = []
          labels = []
          for p in all_preds:
              preds.extend(tensor2list(p))
          for l in all_labels:
              labels.extend(tensor2list(l))
  
          results = compute_metrics(
              self.task_name, np.array(preds), np.array(labels)
          )
          val_name = self._validation_names[dataloader_idx].upper()
          logging.info(f"{val_name} evaluation: {results}")
  
          # writing labels and predictions to a file in output_dir is specified in the config
          output_dir = self._cfg.output_dir
          if output_dir:
              os.makedirs(output_dir, exist_ok=True)
              filename = os.path.join(
                  output_dir, f"{self.task_name}_{val_name}.txt"
              )
              logging.info(
                  f"Saving labels and predictions to {filename}"
              )
              with open(filename, "w") as f:
                  f.write("labels\t" + list2str(labels) + "\n")
                  f.write("preds\t" + list2str(preds) + "\n")
  
      self.log("val_loss", avg_loss)
      if self.trainer.is_global_zero:
          for k, v in results.items():
              self.log(f"{val_name}_{k}", v, rank_zero_only=True)
  
imports: import os
  from typing import Dict, Optional, Union
  import numpy
  import torch
  from omegaconf import DictConfig
  from pytorch_lightning import Trainer
  from nemo.collections.common.losses import CrossEntropyLoss, MSELoss
  from nemo.collections.nlp.data.glue_benchmark.glue_benchmark_dataset import GLUE_TASKS_NUM_LABELS, GLUEDataset
  from nemo.collections.nlp.models.glue_benchmark.metrics_for_glue import compute_metrics
  from nemo.collections.nlp.models.nlp_model import NLPModel
  from nemo.collections.nlp.modules.common import SequenceClassifier, SequenceRegression
  from nemo.collections.nlp.parts.utils_funcs import list2str, tensor2list
  from nemo.core.classes import typecheck
  from nemo.core.neural_types import NeuralType
  from nemo.utils import logging
label_desc: Use image node_id 9 for calling the GLUEModel obj's underlying member method code with example usage: obj.multi_validation_epoch_end(outputs, dataloader_idx) without return types