class_docstr: https://arxiv.org/abs/2306.02317
  BERT-based model for Spellchecking ASR Customization.
  It takes as input ASR hypothesis and candidate customization entries.
  It labels the hypothesis with correct entry index or 0.
  Example input:   [CLS] a s t r o n o m e r s _ d i d i e _ s o m o n _ a n d _ t r i s t i a n _ g l l o [SEP] d i d i e r _ s a u m o n [SEP] a s t r o n o m i e [SEP] t r i s t a n _ g u i l l o t [SEP] ...
  Input segments:      0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0     1 1 1 1 1 1 1 1 1 1 1 1 1 1     2 2 2 2 2 2 2 2 2 2 2     3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3     4      
  Example output:      0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 3 0     ...
class_docstr_tok: ["https",":","\/\/arxiv.org\/abs\/2306.02317","BERT-based","model","for","Spellchecking","ASR","Customization",".","It","takes","as","input","ASR","hypothesis","and","candidate","customization","entries",".","It","labels","the","hypothesis","with","correct","entry","index","or","0",".","Example","input",":","[","CLS","]","a","s","t","r","o","n","o","m","e","r","s","_","d","i","d","i","e","_","s","o","m","o","n","_","a","n","d","_","t","r","i","s","t","i","a","n","_","g","l","l","o","[","SEP","]","d","i","d","i","e","r","_","s","a","u","m","o","n","[","SEP","]","a","s","t","r","o","n","o","m","i","e","[","SEP","]","t","r","i","s","t","a","n","_","g","u","i","l","l","o","t","[","SEP","]","...","Input","segments",":","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","1","1","1","1","1","1","1","1","1","1","1","1","1","1","2","2","2","2","2","2","2","2","2","2","2","3","3","3","3","3","3","3","3","3","3","3","3","3","3","3","3","4","Example","output",":","0","0","0","0","0","0","0","0","0","0","0","0","0","1","1","1","1","1","1","1","1","1","1","1","0","0","0","0","0","3","3","3","3","3","3","3","3","3","3","3","3","3","0","..."]
docstr: Lightning calls this inside the validation loop with the data from the validation dataloader
  passed in as `batch`.
docstr_tok: ["Lightning","calls","this","inside","the","validation","loop","with","the","data","from","the","validation","dataloader","passed","in","as","`","batch","`","."]
code: def validation_step(self, batch, batch_idx, split="val"):
      """
      Lightning calls this inside the validation loop with the data from the validation dataloader
      passed in as `batch`.
      """
      (
          input_ids,
          input_mask,
          segment_ids,
          input_ids_for_subwords,
          input_mask_for_subwords,
          segment_ids_for_subwords,
          character_pos_to_subword_pos,
          labels_mask,
          labels,
          spans,
      ) = batch
      logits = self.forward(
          input_ids=input_ids,
          input_mask=input_mask,
          segment_ids=segment_ids,
          input_ids_for_subwords=input_ids_for_subwords,
          input_mask_for_subwords=input_mask_for_subwords,
          segment_ids_for_subwords=segment_ids_for_subwords,
          character_pos_to_subword_pos=character_pos_to_subword_pos,
      )
      tag_preds = torch.argmax(logits, dim=2)
  
      # Update tag classification_report
      for (
          input_mask_seq,
          segment_seq,
          prediction_seq,
          label_seq,
          span_seq,
      ) in zip(
          input_mask.tolist(),
          segment_ids.tolist(),
          tag_preds.tolist(),
          labels.tolist(),
          spans.tolist(),
      ):
          # Here we want to track whether the predicted output matches ground truth labels for each whole span.
          # We construct the special input for classification report, for example:
          #   span_labels = [PLAIN, PLAIN, PLAIN, PLAIN, CUSTOM, CUSTOM]
          #   span_predictions = [PLAIN, WRONG, PLAIN, PLAIN, WRONG, CUSTOM]
          # Note that the number of PLAIN and CUSTOM occurrences in the report is not comparable,
          #   because PLAIN is for characters, and CUSTOM is for phrases.
          span_labels = []
          span_predictions = []
          plain_cid = self.semiotic_classes["PLAIN"]
          wrong_cid = self.tag_classification_report.num_classes - 1
  
          # First we loop through all predictions for input characters with label=0, they are regarded as separate spans with PLAIN class.
          # It either stays as PLAIN if the model prediction is 0, or turns to WRONG.
          for i in range(len(segment_seq)):
              if input_mask_seq[i] == 0:
                  continue
              if (
                  segment_seq[i] > 0
              ):  # token does not belong to ASR-hypothesis => it's over
                  break
              if label_seq[i] == 0:
                  span_labels.append(plain_cid)
                  if prediction_seq[i] == 0:
                      span_predictions.append(plain_cid)
                  else:
                      span_predictions.append(wrong_cid)
              # if label_seq[i] != 0 then it belongs to CUSTOM span and will be handled later
  
          # Second we loop through spans tensor which contains only spans for CUSTOM class.
          # It stays as CUSTOM if all predictions for the whole span are equal to the labels, otherwise it turns to WRONG.
          for cid, start, end in span_seq:
              if cid == -1:
                  break
              span_labels.append(cid)
              if prediction_seq[start:end] == label_seq[start:end]:
                  span_predictions.append(cid)
              else:
                  span_predictions.append(wrong_cid)
  
          if len(span_labels) != len(span_predictions):
              raise ValueError(
                  "Length mismatch: len(span_labels)="
                  + str(len(span_labels))
                  + "; len(span_predictions)="
                  + str(len(span_predictions))
              )
          self.tag_classification_report(
              torch.tensor(span_predictions).to(self.device),
              torch.tensor(span_labels).to(self.device),
          )
  
      loss = self.loss_fn(
          logits=logits, labels=labels, loss_mask=labels_mask
      )
  
      if split == "val":
          self.validation_step_outputs.append({f"{split}_loss": loss})
      elif split == "test":
          self.test_step_outputs.append({f"{split}_loss": loss})
  
      return {f"{split}_loss": loss}
  
imports: from time import perf_counter
  from typing import Dict, Optional
  import torch
  from omegaconf import DictConfig
  from pytorch_lightning import Trainer
  from nemo.collections.common.losses import CrossEntropyLoss
  from nemo.collections.nlp.data.spellchecking_asr_customization import SpellcheckingAsrCustomizationDataset, SpellcheckingAsrCustomizationTestDataset, TarredSpellcheckingAsrCustomizationDataset, bert_example
  from nemo.collections.nlp.data.text_normalization_as_tagging.utils import read_label_map
  from nemo.collections.nlp.metrics.classification_report import ClassificationReport
  from nemo.collections.nlp.models.nlp_model import NLPModel
  from nemo.collections.nlp.modules.common.token_classifier import TokenClassifier
  from nemo.collections.nlp.parts.utils_funcs import tensor2list
  from nemo.core.classes.common import PretrainedModelInfo, typecheck
  from nemo.core.neural_types import LogitsType, NeuralType
  from nemo.utils import logging
  from nemo.utils.decorators import experimental
label_desc: Use image node_id 7 for calling the SpellcheckingAsrCustomizationModel obj's underlying member method code with example usage: obj.validation_step(batch, batch_idx, split) and returns: dict