class_docstr: Conditional random field.
  This module implements a conditional random field [LMP01]_. The forward computation
  of this class computes the log likelihood of the given sequence of tags and
  emission score tensor. This class also has `~CRF.decode` method which finds
  the best tag sequence given an emission score tensor using `Viterbi algorithm`_.
  Args:
      num_tags: Number of tags.
      batch_first: Whether the first dimension corresponds to the size of a minibatch.
  Attributes:
      start_transitions (`~torch.nn.Parameter`): Start transition score tensor of size
          ``(num_tags,)``.
      end_transitions (`~torch.nn.Parameter`): End transition score tensor of size
          ``(num_tags,)``.
      transitions (`~torch.nn.Parameter`): Transition score tensor of size
          ``(num_tags, num_tags)``.
  .. [LMP01] Lafferty, J., McCallum, A., Pereira, F. (2001).
     "Conditional random fields: Probabilistic models for segmenting and
     labeling sequence data". *Proc. 18th International Conf. on Machine
     Learning*. Morgan Kaufmann. pp. 282–289.
  .. _Viterbi algorithm: https://en.wikipedia.org/wiki/Viterbi_algorithm
code: def _viterbi_decode(
      self, emissions: torch.FloatTensor, mask: torch.ByteTensor
  ) -> List[List[int]]:
      # emissions: (seq_length, batch_size, num_tags)
      # mask: (seq_length, batch_size)
      assert emissions.dim() == 3 and mask.dim() == 2
      assert emissions.shape[:2] == mask.shape
      assert emissions.size(2) == self.num_tags
      assert mask[0].all()
  
      seq_length, batch_size = mask.shape
  
      # Start transition and first emission
      # shape: (batch_size, num_tags)
      score = self.start_transitions + emissions[0]
      history = []
  
      # score is a tensor of size (batch_size, num_tags) where for every batch,
      # value at column j stores the score of the best tag sequence so far that ends
      # with tag j
      # history saves where the best tags candidate transitioned from; this is used
      # when we trace back the best tag sequence
  
      # Viterbi algorithm recursive case: we compute the score of the best tag sequence
      # for every possible next tag
      for i in range(1, seq_length):
          # Broadcast viterbi score for every possible next tag
          # shape: (batch_size, num_tags, 1)
          broadcast_score = score.unsqueeze(2)
  
          # Broadcast emission score for every possible current tag
          # shape: (batch_size, 1, num_tags)
          broadcast_emission = emissions[i].unsqueeze(1)
  
          # Compute the score tensor of size (batch_size, num_tags, num_tags) where
          # for each sample, entry at row i and column j stores the score of the best
          # tag sequence so far that ends with transitioning from tag i to tag j and emitting
          # shape: (batch_size, num_tags, num_tags)
          next_score = (
              broadcast_score + self.transitions + broadcast_emission
          )
  
          # Find the maximum score over all possible current tag
          # shape: (batch_size, num_tags)
          next_score, indices = next_score.max(dim=1)
  
          # Set score to the next score if this timestep is valid (mask == 1)
          # and save the index that produces the next score
          # shape: (batch_size, num_tags)
          score = torch.where(mask[i].unsqueeze(1), next_score, score)
          history.append(indices)
  
      # End transition score
      # shape: (batch_size, num_tags)
      score += self.end_transitions
  
      # Now, compute the best path for each sample
  
      # shape: (batch_size,)
      seq_ends = mask.long().sum(dim=0) - 1
      best_tags_list = []
  
      for idx in range(batch_size):
          # Find the tag which maximizes the score at the last timestep; this is our best tag
          # for the last timestep
          _, best_last_tag = score[idx].max(dim=0)
          best_tags = [best_last_tag.item()]
  
          # We trace back where the best last tag comes from, append that to our best tag
          # sequence, and trace it back again, and so on
          for hist in reversed(history[: seq_ends[idx]]):
              best_last_tag = hist[idx][best_tags[-1]]
              best_tags.append(best_last_tag.item())
  
          # Reverse the order because we start from the last timestep
          best_tags.reverse()
          best_tags_list.append(best_tags)
  
      return best_tags_list
  
file_path: nlp-architect/nlp_architect/nn/torch/layers/crf.py
imports: from typing import List, Optional
  import torch
  import torch.nn
label_desc: Use image node_id 9 for calling the CRF obj's underlying member method code with example usage: obj._viterbi_decode(emissions, mask) and returns: best_tags_list