class_docstr: This module uses the "forward-backward" algorithm to compute
  the log-likelihood of its inputs assuming a conditional random field model.
  
  See, e.g. http://www.cs.columbia.edu/~mcollins/fb.pdf
  
  This is a weighted version of `ConditionalRandomField` which accepts a `label_weights`
  parameter to be used in the loss function in order to give different weights for each
  token depending on its label. The method implemented here is based on the paper
  *Weighted conditional random fields for supervised interpatient heartbeat
  classification* proposed by De Lannoy et. al (2019).
  See https://perso.uclouvain.be/michel.verleysen/papers/ieeetbe12gdl.pdf for more details.
  
  There are two other sample weighting methods implemented. You can find more details
  about them in: https://eraldoluis.github.io/2022/05/10/weighted-crf.html
  
  # Parameters
  
  num_tags : `int`, required
      The number of tags.
  label_weights : `List[float]`, required
      A list of weights to be used in the loss function in order to
      give different weights for each token depending on its label.
      `len(label_weights)` must be equal to `num_tags`. This is useful to
      deal with highly unbalanced datasets. The method implemented here was based on
      the paper *Weighted conditional random fields for supervised interpatient heartbeat
      classification* proposed by De Lannoy et. al (2019).
      See https://perso.uclouvain.be/michel.verleysen/papers/ieeetbe12gdl.pdf
  constraints : `List[Tuple[int, int]]`, optional (default = `None`)
      An optional list of allowed transitions (from_tag_id, to_tag_id).
      These are applied to `viterbi_tags()` but do not affect `forward()`.
      These should be derived from `allowed_transitions` so that the
      start and end transitions are handled correctly for your tag type.
  include_start_end_transitions : `bool`, optional (default = `True`)
      Whether to include the start and end transition parameters.
docstr: Computes the (batch_size,) denominator term for the log-likelihood, which is the
  sum of the likelihoods across all possible state sequences.
  
  Compute this value using the scaling trick instead of the log domain trick, since
  this is necessary to implement the label-weighting method by Lannoy et al. (2012).
code: def _input_likelihood_lannoy(
      self,
      logits: torch.Tensor,
      tags: torch.Tensor,
      mask: torch.BoolTensor,
  ) -> torch.Tensor:
      """
      Computes the (batch_size,) denominator term for the log-likelihood, which is the
      sum of the likelihoods across all possible state sequences.
  
      Compute this value using the scaling trick instead of the log domain trick, since
      this is necessary to implement the label-weighting method by Lannoy et al. (2012).
      """
      batch_size, sequence_length, num_tags = logits.size()
  
      # Transpose batch size and sequence dimensions
      mask = mask.transpose(0, 1).contiguous()
      logits = logits.transpose(0, 1).contiguous()
      tags = tags.transpose(0, 1).contiguous()
  
      # insert an 1-sized second dimension to match z.shape
      label_weights = self.label_weights.view(num_tags, 1)
  
      # emit_scores.shape = (batch_size, num_tags)
      emit_scores = logits[0]
  
      # Initial alpha is the (batch_size, num_tags) tensor of likelihoods combining the
      # transitions to the initial states and the logits for the first timestep.
      # alpha.shape = (batch_size, num_tags)
      if self.include_start_end_transitions:
          alpha = torch.exp(
              self.start_transitions.view(1, num_tags) + emit_scores
          )
      else:
          alpha = torch.exp(emit_scores)
  
      # z.shape = (batch_size, 1)
      z = alpha.sum(dim=1, keepdim=True)
      alpha = alpha / z
      sum_log_z = torch.log(z) * label_weights[tags[0]]
  
      # For each i we compute logits for the transitions from timestep i-1 to timestep i.
      # We do so in a (batch_size, num_tags, num_tags) tensor where the axes are
      # (instance, current_tag, next_tag)
      for i in range(1, sequence_length):
          # multiply the logits by the label weights
          # logits[i].shape: (batch_size, num_tags)
          # emit_scores = torch.mul(logits[i], label_weights)
          emit_scores = logits[i]
  
          # The emit scores are for time i ("next_tag") so we broadcast along the current_tag axis.
          emit_scores = emit_scores.view(batch_size, 1, num_tags)
          # Transition scores are (current_tag, next_tag) so we broadcast along the instance axis.
          transition_scores = self.transitions.view(
              1, num_tags, num_tags
          )
          # Alpha is for the current_tag (i-1), so we broadcast along the next_tag axis.
          broadcast_alpha = alpha.view(batch_size, num_tags, 1)
  
          # Add all the scores together and logexp over the current_tag axis.
          inner = broadcast_alpha * torch.exp(
              emit_scores + transition_scores
          )
  
          # In valid positions (mask == True) we want to take the logsumexp over the current_tag dimension
          # of `inner`. Otherwise (mask == False) we want to retain the previous alpha.
          alpha = inner.sum(dim=1) * mask[i].view(
              batch_size, 1
          ) + alpha * (~mask[i]).view(batch_size, 1)
  
          # scale alphas to avoid underflow (sum of alphas equal to 1)
          z = alpha.sum(dim=1, keepdim=True)
          alpha = alpha / z
          # weight z (normalization factor) according to the current tag
          sum_log_z += torch.log(z) * label_weights[tags[i]]
  
      # Every sequence needs to end with a transition to the stop_tag.
      if self.include_start_end_transitions:
          alpha = alpha * torch.exp(
              self.end_transitions.view(1, num_tags)
          )
          z = alpha.sum(dim=1, keepdim=True)
          # alpha = alpha / z  # this step is unnecessary since alpha is not used anymore
          sum_log_z += torch.log(z)
  
      return sum_log_z.squeeze(1)
  
file_path: allennlp/allennlp/modules/conditional_random_field/conditional_random_field_wlannoy.py
imports: from typing import List, Tuple
  import torch
  from allennlp.common.checks import ConfigurationError
  from allennlp.modules.conditional_random_field.conditional_random_field import ConditionalRandomField
label_desc: Use image node_id 3 for calling the ConditionalRandomFieldWeightLannoy obj's underlying member method code with example usage: obj._input_likelihood_lannoy(logits, tags, mask) and returns: sum_log_z