class_docstr: Abstract TorchRankerAgent class; only meant to be extended.
  
  TorchRankerAgents aim to provide convenient functionality for building ranking
  models. This includes:
  
  - Training/evaluating on candidates from a variety of sources.
  - Computing hits@1, hits@5, mean reciprical rank (MRR), and other metrics.
  - Caching representations for fast runtime when deploying models to production.
class_docstr_tok: ["Abstract","TorchRankerAgent","class",";","only","meant","to","be","extended",".","TorchRankerAgents","aim","to","provide","convenient","functionality","for","building","ranking","models",".","This","includes",":","-","Training\/evaluating","on","candidates","from","a","variety","of","sources",".","-","Computing","hits","@","1",",","hits","@","5",",","mean","reciprical","rank","(","MRR",")",",","and","other","metrics",".","-","Caching","representations","for","fast","runtime","when","deploying","models","to","production","."]
docstr: Evaluate a single batch of examples.
docstr_tok: ["Evaluate","a","single","batch","of","examples","."]
code: def eval_step(self, batch):
      """
      Evaluate a single batch of examples.
      """
      if batch.text_vec is None and batch.image is None:
          return
      batchsize = (
          batch.text_vec.size(0)
          if batch.text_vec is not None
          else batch.image.size(0)
      )
      self.model.eval()
  
      cands, cand_vecs, label_inds = self._build_candidates(
          batch, source=self.eval_candidates, mode="eval"
      )
  
      cand_encs = None
      if self.encode_candidate_vecs and self.eval_candidates in [
          "fixed",
          "vocab",
      ]:
          # if we cached candidate encodings for a fixed list of candidates,
          # pass those into the score_candidates function
          if self.fixed_candidate_encs is None:
              self.fixed_candidate_encs = self._make_candidate_encs(
                  cand_vecs
              ).detach()
          if self.eval_candidates == "fixed":
              cand_encs = self.fixed_candidate_encs
          elif self.eval_candidates == "vocab":
              cand_encs = self.vocab_candidate_encs
  
      scores = self.score_candidates(
          batch, cand_vecs, cand_encs=cand_encs
      )
      if self.rank_top_k > 0:
          sorted_scores, ranks = scores.topk(
              min(self.rank_top_k, scores.size(1)), 1, largest=True
          )
      else:
          sorted_scores, ranks = scores.sort(1, descending=True)
  
      if self.opt.get("return_cand_scores", False):
          sorted_scores = sorted_scores.cpu()
      else:
          sorted_scores = None
  
      # Update metrics
      if label_inds is not None:
          loss = self.criterion(scores, label_inds)
          self.record_local_metric("loss", AverageMetric.many(loss))
          ranks_m = []
          mrrs_m = []
          for b in range(batchsize):
              rank = (ranks[b] == label_inds[b]).nonzero()
              rank = rank.item() if len(rank) == 1 else scores.size(1)
              ranks_m.append(1 + rank)
              mrrs_m.append(1.0 / (1 + rank))
          self.record_local_metric("rank", AverageMetric.many(ranks_m))
          self.record_local_metric("mrr", AverageMetric.many(mrrs_m))
  
      ranks = ranks.cpu()
      max_preds = self.opt["cap_num_predictions"]
      cand_preds = []
      for i, ordering in enumerate(ranks):
          if cand_vecs.dim() == 2:
              cand_list = cands
          elif cand_vecs.dim() == 3:
              cand_list = cands[i]
          # using a generator instead of a list comprehension allows
          # to cap the number of elements.
          cand_preds_generator = (
              cand_list[rank]
              for rank in ordering
              if rank < len(cand_list)
          )
          cand_preds.append(
              list(islice(cand_preds_generator, max_preds))
          )
  
      if (
          self.opt.get("repeat_blocking_heuristic", True)
          and self.eval_candidates == "fixed"
      ):
          cand_preds = self.block_repeats(cand_preds)
  
      if self.opt.get("inference", "max") == "max":
          preds = [cand_preds[i][0] for i in range(batchsize)]
      else:
          # Top-k inference.
          preds = []
          for i in range(batchsize):
              preds.append(
                  random.choice(cand_preds[i][0 : self.opt["topk"]])
              )
  
      return Output(preds, cand_preds, sorted_scores=sorted_scores)
  
imports: from typing import Optional
  from parlai.core.params import ParlaiParser
  from typing import Dict, Any
  from abc import abstractmethod
  from itertools import islice
  import os
  from tqdm import tqdm
  import random
  import torch
  from parlai.core.opt import Opt
  from parlai.utils.distributed import is_distributed
  from parlai.core.torch_agent import TorchAgent, Output
  from parlai.utils.misc import warn_once
  from parlai.utils.io import PathManager
  from parlai.utils.torch import padded_3d, total_parameters, trainable_parameters, PipelineHelper
  from parlai.utils.fp16 import FP16SafeCrossEntropy
  from parlai.core.metrics import AverageMetric
  import parlai.utils.logging
label_desc: Use image node_id 13 for calling the TorchRankerAgent obj's underlying member method code with example usage: obj.eval_step(batch) and returns: Output