class_docstr: Base class for encoder decoder CTC-based models.
class_docstr_tok: ["Base","class","for","encoder","decoder","CTC-based","models","."]
docstr: Called at the end of validation to aggregate outputs.
  :param outputs: list of individual outputs of each validation step.
docstr_tok: ["Called","at","the","end","of","validation","to","aggregate","outputs",".",":","param","outputs",":","list","of","individual","outputs","of","each","validation","step","."]
code: def multi_validation_epoch_end(
      self, outputs, dataloader_idx: int = 0, eval_mode: str = "val"
  ):
      """
      Called at the end of validation to aggregate outputs.
      :param outputs: list of individual outputs of each validation step.
      """
      if not outputs:
          return
  
      if isinstance(outputs[0], dict):
          outputs = [outputs]
  
      for output in outputs:
          eval_loss = getattr(self, "val_loss").compute()
          translations = list(
              itertools.chain(*[x["translations"] for x in output])
          )
          ground_truths = list(
              itertools.chain(*[x["ground_truths"] for x in output])
          )
  
          # Gather translations and ground truths from all workers
          tr_and_gt = [None for _ in range(self.world_size)]
          # we also need to drop pairs where ground truth is an empty string
          if self.world_size > 1:
              dist.all_gather_object(
                  tr_and_gt,
                  [
                      (t, g)
                      for (t, g) in zip(translations, ground_truths)
                      if g.strip() != ""
                  ],
              )
          else:
              tr_and_gt[0] = [
                  (t, g)
                  for (t, g) in zip(translations, ground_truths)
                  if g.strip() != ""
              ]
  
          if self.global_rank == 0:
              _translations = []
              _ground_truths = []
              for rank in range(0, self.world_size):
                  _translations += [t for (t, g) in tr_and_gt[rank]]
                  _ground_truths += [g for (t, g) in tr_and_gt[rank]]
  
              sacre_bleu = corpus_bleu(
                  _translations, [_ground_truths], tokenize="13a"
              )
              sb_score = sacre_bleu.score * self.world_size
  
              wer_scores, wer_words = 0, 0
              for h, r in zip(_translations, _ground_truths):
                  wer_words += len(r.split())
                  wer_scores += editdistance.eval(h.split(), r.split())
              wer_score = 1.0 * wer_scores * self.world_size / wer_words
  
          else:
              sb_score = 0.0
              wer_score = 0.0
  
          self.log(f"{eval_mode}_loss", eval_loss, sync_dist=True)
          self.log(f"{eval_mode}_sacreBLEU", sb_score, sync_dist=True)
          self.log(f"{eval_mode}_WER", wer_score, sync_dist=True)
          self.val_loss.reset()
  
imports: import itertools
  import json
  import os
  import tempfile
  from math import ceil
  from typing import Dict, List, Optional, Union
  import editdistance
  import torch
  import torch.distributed
  from omegaconf import DictConfig, OmegaConf
  from pytorch_lightning import Trainer
  from tqdm.auto import tqdm
  from nemo.collections.asr.data import audio_to_text_dataset
  from nemo.collections.asr.data.audio_to_text_dali import DALIOutputs
  from nemo.collections.asr.data.audio_to_text_lhotse import LhotseSpeechToTextBpeDataset
  from nemo.collections.asr.models.asr_model import ASRModel, ExportableEncDecModel
  from nemo.collections.asr.parts.mixins import ASRBPEMixin
  from nemo.collections.asr.parts.utils.audio_utils import ChannelSelectorType
  from nemo.collections.common.data.lhotse import get_lhotse_dataloader_from_config
  from nemo.collections.common.losses import SmoothedCrossEntropyLoss
  from nemo.collections.common.metrics import GlobalAverageLossMetric
  from nemo.collections.common.parts import transformer_weights_init
  from nemo.core.classes.common import typecheck
  from nemo.core.neural_types import AudioSignal, ChannelType, LabelsType, LengthsType, LogprobsType, MaskType, NeuralType, SpectrogramType
  from nemo.utils import logging
label_desc: Use image node_id 14 for calling the EncDecTransfModelBPE obj's underlying member method code with example usage: obj.multi_validation_epoch_end(outputs, dataloader_idx, eval_mode) without return types