docstr: We re-tokenize and re-numberize the raw context (doc_str) here to get doc_tokens to get
  access to start_idx and end_idx mappings.  At this point, ans_token_start is the start index
  of the answer within tokens and ans_token_end is the end index. We calculate the offset of doc_tokens
  within tokens.
  Then we find the start_idx and end_idx
  as well as the corresponding span in the raw text using the answer token indices.
docstr_tok: ["We","re-tokenize","and","re-numberize","the","raw","context","(","doc_str",")","here","to","get","doc_tokens","to","get","access","to","start_idx","and","end_idx","mappings",".","At","this","point",",","ans_token_start","is","the","start","index","of","the","answer","within","tokens","and","ans_token_end","is","the","end","index",".","We","calculate","the","offset","of","doc_tokens","within","tokens",".","Then","we","find","the","start_idx","and","end_idx","as","well","as","the","corresponding","span","in","the","raw","text","using","the","answer","token","indices","."]
code: def _unnumberize(self, preds, tokens, doc_str):
      """
      We re-tokenize and re-numberize the raw context (doc_str) here to get doc_tokens to get
      access to start_idx and end_idx mappings.  At this point, ans_token_start is the start index
      of the answer within tokens and ans_token_end is the end index. We calculate the offset of doc_tokens
      within tokens.
      Then we find the start_idx and end_idx
      as well as the corresponding span in the raw text using the answer token indices.
      """
      # start_idx and end_idx are lists of char start and end positions in doc_str.
      doc_tokens, start_idxs, end_idxs = self.tensorizer._lookup_tokens(
          doc_str
      )
  
      # find the offsets of doc_tokens in tokens
      try:
          offset_end = (
              tokens.index(self.tensorizer.vocab.get_pad_index()) - 1
          )
      except ValueError:
          offset_end = len(tokens) - 1
      offset_start = list(
          map(
              lambda x: tokens[x:offset_end]
              == doc_tokens[: offset_end - x],
              range(offset_end),
          )
      ).index(True)
  
      # find each answer's char idxs and strings as well
      pred_labels = self._process_pred(preds[offset_start:offset_end])
      token_range = list(zip(start_idxs, end_idxs))
  
      pred_slots = parse_slot_string(
          merge_token_labels_to_slot(
              token_range,
              pred_labels,
              self.tensorizer.use_bio_labels,
          )
      )
      ans_strs = []
      ans_start_char_idxs = []
      ans_end_char_idxs = []
      for slot in pred_slots:
          # if its not an answer span, skip
          if slot.label in map(
              str,
              [
                  self.tensorizer.labels_vocab.pad_token,
                  Slot.NO_LABEL_SLOT,
              ],
          ):
              continue
          ans_strs.append(doc_str[slot.start : slot.end])
          ans_start_char_idxs.append(slot.start)
          ans_end_char_idxs.append(slot.end)
  
      return ans_strs, ans_start_char_idxs, ans_end_char_idxs
  
imports: import re
  import string
  from typing import Dict, List, Set, Tuple, Union
  import numpy
  from pytext.common.constants import Stage
  from pytext.metric_reporters.channel import Channel, ConsoleChannel, FileChannel
  from pytext.metric_reporters.metric_reporter import MetricReporter
  from pytext.metrics.squad_metrics import SquadMetrics
  from pytext.utils.data import merge_token_labels_to_slot, parse_slot_string, Slot
  from scipy.optimize import linear_sum_assignment
label_desc: Use image node_id 11 for calling the MultiSpanQAMetricReporter obj's underlying member method code with example usage: obj._unnumberize(preds, tokens, doc_str) and returns: ans_strs,  ans_start_char_idxs,  ans_end_char_idxs