class_docstr: Hybrid CTC/Masked LM Encoder-Decoder model (Mask-CTC)
class_docstr_tok: ["Hybrid","CTC\/Masked","LM","Encoder-Decoder","model","(","Mask-CTC",")"]
docstr: Frontend + Encoder + Decoder + Calc loss
  
  Args:
      speech: (Batch, Length, ...)
      speech_lengths: (Batch, )
      text: (Batch, Length)
      text_lengths: (Batch,)
docstr_tok: ["Frontend","+","Encoder","+","Decoder","+","Calc","loss","Args",":","speech",":","(","Batch",",","Length",",","...",")","speech_lengths",":","(","Batch",",",")","text",":","(","Batch",",","Length",")","text_lengths",":","(","Batch",",",")"]
code: def forward(
      self,
      speech: torch.Tensor,
      speech_lengths: torch.Tensor,
      text: torch.Tensor,
      text_lengths: torch.Tensor,
      **kwargs,
  ) -> Tuple[torch.Tensor, Dict[str, torch.Tensor], torch.Tensor]:
      """Frontend + Encoder + Decoder + Calc loss
  
      Args:
          speech: (Batch, Length, ...)
          speech_lengths: (Batch, )
          text: (Batch, Length)
          text_lengths: (Batch,)
      """
      assert text_lengths.dim() == 1, text_lengths.shape
      # Check that batch_size is unified
      assert (
          speech.shape[0]
          == speech_lengths.shape[0]
          == text.shape[0]
          == text_lengths.shape[0]
      ), (
          speech.shape,
          speech_lengths.shape,
          text.shape,
          text_lengths.shape,
      )
      batch_size = speech.shape[0]
  
      # For data-parallel
      text = text[:, : text_lengths.max()]
  
      # Define stats to report
      loss_mlm, acc_mlm = None, None
      loss_ctc, cer_ctc = None, None
      stats = dict()
  
      # 1. Encoder
      encoder_out, encoder_out_lens = self.encode(
          speech, speech_lengths
      )
      intermediate_outs = None
      if isinstance(encoder_out, tuple):
          intermediate_outs = encoder_out[1]
          encoder_out = encoder_out[0]
  
      # 2. CTC branch
      if self.ctc_weight != 0.0:
          loss_ctc, cer_ctc = self._calc_ctc_loss(
              encoder_out, encoder_out_lens, text, text_lengths
          )
  
          # Collect CTC branch stats
          stats["loss_ctc"] = (
              loss_ctc.detach() if loss_ctc is not None else None
          )
          stats["cer_ctc"] = cer_ctc
  
      # 2a. Intermediate CTC (optional)
      loss_interctc = 0.0
      if self.interctc_weight != 0.0 and intermediate_outs is not None:
          for layer_idx, intermediate_out in intermediate_outs:
              # we assume intermediate_out has the same length & padding
              # as those of encoder_out
              loss_ic, cer_ic = self._calc_ctc_loss(
                  intermediate_out, encoder_out_lens, text, text_lengths
              )
              loss_interctc = loss_interctc + loss_ic
  
              # Collect Intermedaite CTC stats
              stats["loss_interctc_layer{}".format(layer_idx)] = (
                  loss_ic.detach() if loss_ic is not None else None
              )
              stats["cer_interctc_layer{}".format(layer_idx)] = cer_ic
  
          loss_interctc = loss_interctc / len(intermediate_outs)
  
          # calculate whole encoder loss
          loss_ctc = (
              1 - self.interctc_weight
          ) * loss_ctc + self.interctc_weight * loss_interctc
  
      # 3. MLM decoder branch
      if self.ctc_weight != 1.0:
          loss_mlm, acc_mlm = self._calc_mlm_loss(
              encoder_out, encoder_out_lens, text, text_lengths
          )
  
      # 4. CTC/MLM loss definition
      if self.ctc_weight == 0.0:
          loss = loss_mlm
      elif self.ctc_weight == 1.0:
          loss = loss_ctc
      else:
          loss = (
              self.ctc_weight * loss_ctc
              + (1 - self.ctc_weight) * loss_mlm
          )
  
      # Collect MLM branch stats
      stats["loss_mlm"] = (
          loss_mlm.detach() if loss_mlm is not None else None
      )
      stats["acc_mlm"] = acc_mlm
  
      # Collect total loss stats
      stats["loss"] = loss.detach()
  
      # force_gatherable: to-device and to-tensor if scalar for DataParallel
      loss, stats, weight = force_gatherable(
          (loss, stats, batch_size), loss.device
      )
      return loss, stats, weight
  
imports: import logging
  from contextlib import contextmanager
  from itertools import groupby
  from typing import Dict, List, Optional, Tuple, Union
  import numpy
  import torch
  from packaging.version import parse
  from typeguard import check_argument_types
  from espnet2.asr.ctc import CTC
  from espnet2.asr.decoder.mlm_decoder import MLMDecoder
  from espnet2.asr.encoder.abs_encoder import AbsEncoder
  from espnet2.asr.espnet_model import ESPnetASRModel
  from espnet2.asr.frontend.abs_frontend import AbsFrontend
  from espnet2.asr.postencoder.abs_postencoder import AbsPostEncoder
  from espnet2.asr.preencoder.abs_preencoder import AbsPreEncoder
  from espnet2.asr.specaug.abs_specaug import AbsSpecAug
  from espnet2.layers.abs_normalize import AbsNormalize
  from espnet2.text.token_id_converter import TokenIDConverter
  from espnet2.torch_utils.device_funcs import force_gatherable
  from espnet.nets.beam_search import Hypothesis
  from espnet.nets.e2e_asr_common import ErrorCalculator
  from espnet.nets.pytorch_backend.maskctc.add_mask_token import mask_uniform
  from espnet.nets.pytorch_backend.nets_utils import th_accuracy
  from espnet.nets.pytorch_backend.transformer.label_smoothing_loss import LabelSmoothingLoss
label_desc: Use image node_id 2 for calling the MaskCTCModel obj's underlying member method code with example usage: obj.forward(speech, speech_lengths, text, text_lengths) and returns: loss,  stats,  weight