class_docstr: This implementation is slightly different from the original code in that
  1). we allow re-selecting the verbalizer after a fixed training steps.
  The original implementation only performs one step selection after getting
  the initial logits on the training data. To adopt their implementation,
  please only do ``optimize()`` after the first pass of training data.
  
  2). We strictly follows the probility calculation in Equation (3) in the
  paper, which take softmax over the logits.
  
  3). We do not implements the ``combine_patterns'' if-branch. Since it's
  not a pure verbalizer type, and doesn't yield much improvement. However,
  it can be achieve by using EnsembleTrainer to pass text wrapped by
  multiple templates together with this verbalizer.
  
  We use a probs_buffer to store the probability :math:`q_{P,t}(1|\mathbf{x})` that to be used in later verbalizer selection,
  and a label_buffer to store the label :math:`y` that to be used in later verbalizer selection.
  
  Args:
      num_candidates (:obj:`int`, optional): the number of candidates for further selection based on Section 4.1
      label_word_num_per_class (:obj:`int`, optional): set to be greater than 1 to support Multi-Verbalizers in Section 4.2
      num_searches (:obj:`int`, optional): Maximnum number of label_words search. After reaching this number, the verbalizer will use the same label_words as the previous iterations.
      search_id (:obj:`int`, optional): the id of current search, used to determine when to stop label words searching.
      score_fct (:obj:`str`, optional): the scoring function of label words selection. ``llr`` means log likelihood ratio, corresponding to Equation (7); ``ce`` means cross entropy, corresponding to Equation (6). As the paper points out, ``llr'' is significantly better than 'ce', we only keep it to match the original code.
      balance (:obj:`book`, optional): whether to perform normalization of unbalanced training dataset, as Equation (5).
code: def _get_top_words(
      self,
      probs: torch.Tensor,
      candidates: List[torch.Tensor],
      balance: bool = True,
      words_per_label: int = 10,
      score_fct: Optional[str] = "llr",
  ):
      label_words_ids = []
      for label_id in range(self.num_classes):
          label_mask = (self.labels_buffer == label_id).to(torch.float)
          probs_per_label = probs[:, candidates[label_id]]
          if score_fct == "llr":
              s = self._log_likelihood_ratio(
                  probs_per_label, label_mask, balance
              )
          elif score_fct == "ce":
              s = self._cross_entropy(
                  probs_per_label, label_mask, balance
              )
          else:
              raise ValueError(
                  f"Score function '{score_fct}' not implemented"
              )
          sorted_ids = torch.argsort(s, descending=True)[
              :words_per_label
          ]
          selected_ids = candidates[label_id][sorted_ids]
          label_words_ids.append(selected_ids)
      label_words_ids = torch.vstack(label_words_ids)
      return label_words_ids
  
imports: from transformers.tokenization_utils import PreTrainedTokenizer
  from openprompt.data_utils import InputFeatures
  from openprompt import Verbalizer
  from typing import List, Optional, Dict
  import torch
  import torch.nn
  import torch.nn.functional
  from openprompt.utils.logging import logger
label_desc: Use image node_id 10 for calling the AutomaticVerbalizer obj's underlying member method code with example usage: obj._get_top_words(probs, candidates, balance, words_per_label, score_fct) and returns: label_words_ids