rank的相关loss

1、相关loss

1.1、loss相关简介

排序优化时,主要从三个角度来考虑构建loss,分别为pointwise、pairwise、listwise。pointwise将排序所有query当成一个整体,计算每个<query,doc>对的loss,相当于一个二分问题。pairwise以每个query为维度,构建<query,doc1,doc2>这样的元组,构建doc和doc之间的偏序关系。listwise以query为维度,将此query下的doc作为一个list进行建模。常见的这几种loss包括如下几种,来源于TF-Ranking代码库。

1.2、pointwise

ClickEMLoss:假设点击是由分解模型 P(examination) *P(relevance) 生成的,它们是分别由 `exam_logits` 和 `rel_logits` 确定的潜在变量

SigmoidCrossEntropyLoss:将<query,doc>当成二分类问题,计算logits和labels之间的交叉熵

MeanSquaredLoss:将<query,doc>当成回归问题,计算logits和labels之间的平方差

OrdinalLoss:ordinal_size标签的等级,将回归问题转化为分类问题

MultiClassLoss:将<query,doc>当成多分类问题,计算logits和labels之间的交叉熵

1.3、pairwise

PairwiseLogisticLoss:log(1 + exp(-pairwise_logits)).

PairwiseHingeLoss:Hinge(l_i > l_j) = max(0, 1 - (s_i - s_j))

PairwiseSoftZeroOneLoss:PairwiseHingeLoss的一个变种,当 if (s_i - s_j <0)时,loss为tf.sigmoid(-pairwise_logits)

PairwiseMSELoss:面向回归模型的loss构建,loss为logits和label的平方误差,tf.math.square(pairwise_logit_diff -pairwise_label_diff)

1.4、listwise

SoftmaxLoss:每个list doc的相关性与logits的交叉熵, softmax_cross_entropy_with_logits_v2(
        labels_for_softmax, logits_for_softmax)

PolyOneSoftmaxLoss:为理解和改进常用的loss提供了一个框架,灵感来自于损失函数的泰勒展开式。poly_one loss增加了多项式展开的第一项表述。

pt = tf.reduce_sum(labels_for_softmax * tf.nn.softmax(logits_for_softmax), axis=-1)
ce = tf.compat.v1.nn.softmax_cross_entropy_with_logits_v2(labels_for_softmax, logits_for_softmax)
losses = ce + self._epsilon * (1 - pt)

https://arxiv.org/pdf/2204.12511.pdf

CircleLoss:基于对的优化的方法目的都是最大化类内相似性 S_{p} 同时最小化类间相似性 S_{n} 。为了重新加权那些欠优化的分数,文中提出了Circle loss, 之所以叫这个名字是因为其决策边界是一个圆。L_circle = log(1 + sum_{i is p,j is n}
                   exp(gamma * (a_j * (s_j - d_n) - a_i * (s_i - d_p))))

https://arxiv.org/pdf/2002.10857.pdf

UniqueSoftmaxLoss:主要解决3个问题,1)、排序概率忽略了联系;2)、不利于高相关性的文档;3)、假设宽松:不同步的文档采用假设是独立的。

      -sum_i (2^l_i - 1) * log(exp(s_i) / (sum_j exp(s_j) + exp(s_i)))
https://arxiv.org/pdf/2001.01828.pdf

MixtureEMLoss:融合多个排序策略

ListMLELoss:ListMLE核心思想与ListNet类似,

相同点:都是通过概率模型输出的排序概率分布来近似真实概率分布

不同点

  1. 损失函数:ListNet选择的是交叉熵损失ce_loss,ListMLE选择的是似然损失likelihood loss
  2. 概率分布:ListNet一般选用top one probability,ListMLE保留了Permutation Probability的特性,使得排序概率相比ListNet中的排序概率更加准确,因为ListNet实现时本质上是直接使用了SoftMax对排序概率Permutation Probability进行了近似上是直接使用了SoftMax对排序概率Permutation Probability进行了近似

ApproxNDCGLoss:将ndcg指标中的位置转化为可导的计算方式,计算ndcg,直接对排序指标ndcg进行优化

ApproxMRRLoss:将mrr指标中的位置转化为可导的计算方式,计算mrr,直接对排序指标mrr进行优化

2、loss的定义及具体计算

2.1、基础实现

2.1.1、_RankingLoss rank相关loss的基础实现

class _RankingLoss(object, metaclass=abc.ABCMeta):
  """Interface for ranking loss."""

  def __init__(self, name, lambda_weight=None, temperature=1.0, ragged=False):
    """Constructor.

    Args:
      name: A string used as the name for this loss.
      lambda_weight: A `_LambdaWeight` object.
      temperature: A float number to modify the logits=logits/temperature.
      ragged: A boolean indicating whether the input tensors are ragged.
    """
    self._name = name
    self._lambda_weight = lambda_weight
    self._temperature = temperature
    self._ragged = ragged

  @property
  def name(self):
    """The loss name."""
    return self._name

  def _prepare_and_validate_params(self, labels, logits, weights, mask):
    """Prepares and validate input parameters.

    Args:
      labels: A `Tensor` of the same shape as `logits` representing graded
        relevance.
      logits: A `Tensor` with shape [batch_size, list_size]. Each value is the
        ranking score of the corresponding item.
      weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise
        weights, or a `Tensor` with shape [batch_size, list_size] for item-wise
        weights.
      mask: A `Tensor` of the same shape as logits indicating which entries are
        valid for computing the loss.

    Returns:
      A tuple (labels, logits, weights, mask) of `tf.Tensor` objects that are
      ready to be used in the loss.
    """
    if self._ragged:
      labels, logits, weights, mask = utils.ragged_to_dense(
          labels, logits, weights)

    if mask is None:
      mask = utils.is_label_valid(labels)

    if weights is None:
      weights = 1.0

    labels = tf.convert_to_tensor(labels)
    logits = tf.convert_to_tensor(logits)
    weights = tf.convert_to_tensor(weights)
    mask = tf.convert_to_tensor(mask)

    return labels, logits, weights, mask

  def compute_unreduced_loss(self, labels, logits, mask=None):
    """Computes the unreduced loss.

    Args:
      labels: A `Tensor` or `RaggedTensor` of the same shape as `logits`
        representing graded relevance.
      logits: A `Tensor` or `RaggedTensor` with shape [batch_size, list_size].
        Each value is the ranking score of the corresponding item.
      mask: An optional `Tensor` of the same shape as logits indicating which
        entries are valid for computing the loss. Will be ignored if the loss
        was constructed with ragged=True.

    Returns:
      A tuple(losses, loss_weights) that have the same shape.
    """
    labels, logits, _, mask = self._prepare_and_validate_params(
        labels, logits, None, mask)
    return self._compute_unreduced_loss_impl(labels, logits, mask)

  @abc.abstractmethod
  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    """Implementation for the unreduced loss.

    Args:
      labels: A `Tensor` of the same shape as `logits` representing graded
        relevance.
      logits: A `Tensor` with shape [batch_size, list_size]. Each value is the
        ranking score of the corresponding item.
      mask: An optional `Tensor` of the same shape as logits indicating which
        entries are valid for computing the loss.

    Returns:
      A tuple(losses, loss_weights) that have the same shape.
    """
    raise NotImplementedError('Calling an abstract method.')

  def normalize_weights(self, labels, weights):
    """Normalizes weights.

    This is needed for `tf.estimator` given that the reduction may be
    `SUM_OVER_NONZERO_WEIGHTS`.

    This method is also needed to compute normalized weights when calling
    `compute_unreduced_loss`, which is done in the tf.keras losses.

    Args:
      labels: A `Tensor` of shape [batch_size, list_size] representing graded
        relevance.
      weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise
        weights, or a `Tensor` with shape [batch_size, list_size] for item-wise
        weights.

    Returns:
      The normalized weights.
    """
    if self._ragged:
      labels, _, weights, _ = utils.ragged_to_dense(labels, None, weights)
    return self._normalize_weights_impl(labels, weights)

  def _normalize_weights_impl(self, labels, weights):
    """See `normalize_weights`."""
    del labels
    return 1.0 if weights is None else weights

  def get_logits(self, logits):
    """Computes logits rescaled by temperature.

    Args:
      logits: A `Tensor` with shape [batch_size, list_size]. Each value is the
        ranking score of the corresponding item.

    Returns:
      Tensor of rescaled logits.
    """
    if not tf.is_tensor(logits):
      logits = tf.convert_to_tensor(value=logits)
    return logits / self._temperature

  def compute(self, labels, logits, weights, reduction, mask=None):
    """Computes the reduced loss for tf.estimator (not tf.keras).

    Note that this function is not compatible with keras.

    Args:
      labels: A `Tensor` of the same shape as `logits` representing graded
        relevance.
      logits: A `Tensor` with shape [batch_size, list_size]. Each value is the
        ranking score of the corresponding item.
      weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise
        weights, or a `Tensor` with shape [batch_size, list_size] for item-wise
        weights.
      reduction: One of `tf.losses.Reduction` except `NONE`. Describes how to
        reduce training loss over batch.
      mask: A `Tensor` of the same shape as logits indicating which entries are
        valid for computing the loss.

    Returns:
      Reduced loss for training and eval.
    """
    logits = self.get_logits(logits)
    losses, loss_weights = self._compute_unreduced_loss_impl(
        labels, logits, mask)
    weights = tf.multiply(
        self._normalize_weights_impl(labels, weights), loss_weights)
    return tf.compat.v1.losses.compute_weighted_loss(
        losses, weights, reduction=reduction)

  @abc.abstractmethod
  def compute_per_list(self, labels, logits, weights, mask=None):
    """Computes the per-list loss.

    Args:
      labels: A `Tensor` of the same shape as `logits` representing graded
        relevance.
      logits: A `Tensor` with shape [batch_size, list_size]. Each value is the
        ranking score of the corresponding item.
      weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise
        weights, or a `Tensor` with shape [batch_size, list_size] for item-wise
        weights.
      mask: A `Tensor` of the same shape as logits indicating which entries are
        valid for computing the loss.

    Returns:
      A pair of `Tensor` objects of shape [batch_size] containing per-list
      losses and weights.
    """
    raise NotImplementedError('Calling an abstract method.')

  def eval_metric(self, labels, logits, weights, mask=None):
    """Computes the eval metric for the loss in tf.estimator (not tf.keras).

    Note that this function is not compatible with keras.

    Args:
      labels: A `Tensor` of the same shape as `logits` representing graded
        relevance.
      logits: A `Tensor` with shape [batch_size, list_size]. Each value is the
        ranking score of the corresponding item.
      weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise
        weights, or a `Tensor` with shape [batch_size, list_size] for item-wise
        weights.
      mask: A `Tensor` of the same shape as logits indicating which entries are
        valid for computing the metric.

    Returns:
      A metric op.
    """
    losses, loss_weights = self._compute_unreduced_loss_impl(
        labels, logits, mask)
    weights = tf.multiply(
        self._normalize_weights_impl(labels, weights), loss_weights)
    return tf.compat.v1.metrics.mean(losses, weights)

2.1.2、_prepare_and_validate_params 参数检验及校验

        label mask获取有效valid label。weight、labels、logits转化为tensor过程,大概率是为了兼容list测试的过程

2.1.3、_compute_unreduced_loss计算未reduce的loss

        _compute_unreduced_loss计算未reduce的loss,和logits等有相同的维度。调用类中的_compute_unreduced_loss_impl函数实现。这个函数在具体的继承类中被改写

2.1.4、normalize_weights归一化权重

       normalize_weights将权重进行归一化,调用类中_normalize_weights_impl函数实现,这个函数在具体的继承类中被改写

2.1.5、temperature调节logits

         通过调用函数get_logits实现,logits/_temperature调整logits的分数。logit除以temperature引入的原因语言模型采样策略 - 知乎

2.1.6、计算loss

def compute(self, labels, logits, weights, reduction, mask=None):
    """Computes the reduced loss for tf.estimator (not tf.keras).

    Note that this function is not compatible with keras.

    Args:
      labels: A `Tensor` of the same shape as `logits` representing graded
        relevance.
      logits: A `Tensor` with shape [batch_size, list_size]. Each value is the
        ranking score of the corresponding item.
      weights: A scalar, a `Tensor` with shape [batch_size, 1] for list-wise
        weights, or a `Tensor` with shape [batch_size, list_size] for item-wise
        weights.
      reduction: One of `tf.losses.Reduction` except `NONE`. Describes how to
        reduce training loss over batch.
      mask: A `Tensor` of the same shape as logits indicating which entries are
        valid for computing the loss.

    Returns:
      Reduced loss for training and eval.
    """
    logits = self.get_logits(logits)
    losses, loss_weights = self._compute_unreduced_loss_impl(
        labels, logits, mask)
    weights = tf.multiply(
        self._normalize_weights_impl(labels, weights), loss_weights)
    return tf.compat.v1.losses.compute_weighted_loss(
        losses, weights, reduction=reduction)

语言模型采样策略 - 知乎通过调用_compute_unreduced_loss_impl计算loss,然后reduce计算得到loss

2.1.7、以list为单元计算loss

  compute_per_list以list为单元计算loss,返回为batch_size的大小

2.1.8、评估均值loss

以加权loss为评估标准tf.compat.v1.metrics.mean(losses, weights)

2.2、_PointwiseLoss继承_RankingLoss 实现pointwise基类

2.2.1、_PointwiseLoss函数实现

class _PointwiseLoss(_RankingLoss):
  """Interface for pointwise loss."""

  def _normalize_weights_impl(self, labels, weights):
    """See _RankingLoss."""
    if weights is None:
      weights = 1.
    return tf.compat.v1.where(
        utils.is_label_valid(labels),
        tf.ones_like(labels) * weights, tf.zeros_like(labels))

  def compute_per_list(self, labels, logits, weights, mask=None):
    """See `_RankingLoss`."""
    # Prepare input params.
    labels, logits, weights, mask = self._prepare_and_validate_params(
        labels, logits, weights, mask)

    # Pointwise losses and weights will be of shape [batch_size, list_size].
    losses, loss_weights = self._compute_unreduced_loss_impl(
        labels, logits, mask)
    weights = tf.multiply(
        self._normalize_weights_impl(labels, weights), loss_weights)

    # Compute the weighted per-item loss.
    weighted_per_item_loss = tf.math.multiply(losses, weights)

    # Sum the inner dimensions to obtain per-list weights. For pointwise losses
    # this typically indicates the (weighted) number of items per list.
    per_list_weights = tf.reduce_sum(weights, axis=1)

    # This computes the per-list losses by summing all weighted per-item losses.
    per_list_losses = tf.reduce_sum(weighted_per_item_loss, axis=1)

    # Normalize the per-list losses so that lists with different numbers of
    # items have comparable losses. The different numbers of items is reflected
    # in the per-list weights.
    per_list_losses = tf.math.divide_no_nan(per_list_losses, per_list_weights)
    return per_list_losses, per_list_weights

2.2.2、继承_PointwiseLoss的类

ClickEMLoss :假设点击是由分解模型 P(examination) *P(relevance) 生成的,它们是分别由 `exam_logits` 和 `rel_logits` 确定的潜在变量。 

https://static.googleusercontent.com/media/research.google.com/zh-CN//pubs/archive/46485.pdf

SigmoidCrossEntropyLoss:tf.compat.v1.nn.sigmoid_cross_entropy_with_logits(
        labels=labels, logits=logits)

MeanSquaredLoss: losses = tf.compat.v1.squared_difference(labels, logits)

OrdinalLoss: losses =tf.where(
        tf.expand_dims(mask, -1),
        tf.compat.v1.nn.sigmoid_cross_entropy_with_logits(
            labels=ordinals,
            logits=logits),
        0.0)

ordinal_size标签的等级,将回归问题转化为分类问题,

处理分级问题的利器Ordinal Regression - 知乎

MultiClassLoss:losses = tf.keras.losses.CategoricalCrossentropy(
        from_logits=self._from_logits,
        label_smoothing=self._label_smoothing,
        axis=-1,reduction=tf.keras.losses.Reduction.NONE,
        name='categorical_crossentropy')(classes, logits, tf.cast(mask, dtype=tf.float32))

一文弄懂各种loss function - 知乎

Keras中的多分类损失函数categorical_crossentropy_keras 多分类损失函数_赵大寳Note的博客-CSDN博客

2.3、_PairwiseLoss继承_RankingLoss实现pairwise的基类

2.3.1、_PairwiseLoss的实现

pairwiseloss的基础实现,其他pairwise loss的实现都继承该类

class _PairwiseLoss(_RankingLoss, metaclass=abc.ABCMeta):
  """Interface for pairwise ranking loss."""

  @abc.abstractmethod
  def _pairwise_loss(self, pairwise_logits):
    """The loss of pairwise logits with l_i > l_j."""
    raise NotImplementedError('Calling an abstract method.')

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    """See `_RankingLoss`."""
    if mask is None:
      mask = utils.is_label_valid(labels)
    ranks = _compute_ranks(logits, mask)
    pairwise_labels, pairwise_logits = _pairwise_comparison(
        labels, logits, mask)
    pairwise_weights = pairwise_labels
    if self._lambda_weight is not None:
      pairwise_weights *= self._lambda_weight.pair_weights(labels, ranks)

    pairwise_weights = tf.stop_gradient(
        pairwise_weights, name='weights_stop_gradient')
    return self._pairwise_loss(pairwise_logits), pairwise_weights

  def compute_per_list(self, labels, logits, weights, mask=None):
    """See `_RankingLoss`."""
    # Prepare input params.
    labels, logits, weights, mask = self._prepare_and_validate_params(
        labels, logits, weights, mask)

    # Pairwise losses and weights will be of shape
    # [batch_size, list_size, list_size].
    losses, loss_weights = self._compute_unreduced_loss_impl(
        labels, logits, mask)
    weights = tf.multiply(
        self._normalize_weights_impl(labels, weights), loss_weights)

    # Compute the weighted per-pair loss.
    weighted_per_pair_loss = tf.math.multiply(losses, weights)

    # Sum the inner dimensions to obtain per-list weights. For pairwise losses
    # this typically indicates the (weighted) number of pairwise preferences per
    # list.
    per_list_weights = tf.reduce_sum(weights, axis=[1, 2])

    # This computes the per-list losses by summing all weighted pairwise losses.
    per_list_losses = tf.reduce_sum(weighted_per_pair_loss, axis=[1, 2])

    # Normalize the per-list losses so that lists with different numbers of
    # pairs have comparable losses. The different numbers of pairs is reflected
    # in the per-list weights.
    per_list_losses = tf.math.divide_no_nan(per_list_losses, per_list_weights)

    return per_list_losses, per_list_weights

  def _normalize_weights_impl(self, labels, weights):
    """See _RankingLoss."""
    # The `weights` is item-wise and is applied non-symmetrically to update
    # pairwise_weights as
    #   pairwise_weights(i, j) = w_i * pairwise_weights(i, j).
    # This effectively applies to all pairs with l_i > l_j. Note that it is
    # actually symmetric when `weights` are constant per list, i.e., listwise
    # weights.
    if weights is None:
      weights = 1.
    weights = tf.compat.v1.where(
        utils.is_label_valid(labels),
        tf.ones_like(labels) * weights, tf.zeros_like(labels))
    return tf.expand_dims(weights, axis=2)

2.3.2、mask获取

通过判断label是否大于0来判断是否被mask

def is_label_valid(labels):
  """Returns a boolean `Tensor` for label validity."""
  labels = tf.convert_to_tensor(value=labels)
  return tf.greater_equal(labels, 0.)

将mask为true的地方赋值为logits的大小,非mask的的地方logits赋值为最小的值,并减去一个较小的值

2.3.3、计算排序位置 _compute_ranks

def _compute_ranks(logits, is_valid):
  """Computes ranks by sorting valid logits.

  Args:
    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the
      ranking score of the corresponding item.
    is_valid: A `Tensor` of the same shape as `logits` representing validity of
      each entry.

  Returns:
    The `ranks` Tensor.
  """
  _check_tensor_shapes([logits, is_valid])
  # Only sort entries with is_valid = True.
  scores = tf.compat.v1.where(
      is_valid, logits, -1e-6 * tf.ones_like(logits) +
      tf.reduce_min(input_tensor=logits, axis=1, keepdims=True))
  return utils.sorted_ranks(scores)

sorted_ranks返回分数的排序位置

def sorted_ranks(scores, shuffle_ties=True, seed=None):
  """Returns an int `Tensor` as the ranks (1-based) after sorting scores.

  Example: Given scores = [[1.0, 3.5, 2.1]], the returned ranks will be [[3, 1,
  2]]. It means that scores 1.0 will be ranked at position 3, 3.5 will be ranked
  at position 1, and 2.1 will be ranked at position 2.

  Args:
    scores: A `Tensor` of shape [batch_size, list_size] representing the
      per-example scores.
    shuffle_ties: See `sort_by_scores`.
    seed: See `sort_by_scores`.

  Returns:
    A 1-based int `Tensor`s as the ranks.
  """
  with tf.compat.v1.name_scope(name='sorted_ranks'):
    batch_size, list_size = tf.unstack(tf.shape(input=scores))
    # The current position in the list for each score.
    positions = tf.tile(tf.expand_dims(tf.range(list_size), 0), [batch_size, 1])
    # For score [[1.0, 3.5, 2.1]], sorted_positions are [[1, 2, 0]], meaning the
    # largest score is at position 1, the 2nd is at position 2 and 3rd is at
    # position 0.
    sorted_positions = sort_by_scores(
        scores, [positions], shuffle_ties=shuffle_ties, seed=seed)[0]
    # The indices of sorting sorted_positions will be [[2, 0, 1]] and ranks are
    # 1-based and thus are [[3, 1, 2]].
    ranks = tf.argsort(sorted_positions) + 1
    return ranks

2.3.4、计算pairwise_labels、pairwise_logits

pairwise_labels、pairwise_logits= _pairwise_comparison(
        labels, logits, mask)

def _pairwise_comparison(labels, logits, mask, pairwise_logits_op=tf.subtract):
  r"""Returns pairwise comparison `Tensor`s.

  Given a list of n items, the labels of graded relevance l_i and the logits
  s_i, we form n^2 pairs. For each pair, we have the following:

                        /
                        | 1   if l_i > l_j for valid l_i and l_j.
  * `pairwise_labels` = |
                        | 0   otherwise
                        \
  * `pairwise_logits` = pairwise_logits_op(s_i, s_j)

  Args:
    labels: A `Tensor` with shape [batch_size, list_size].
    logits: A `Tensor` with shape [batch_size, list_size].
    mask: A `Tensor` with shape [batch_size, list_size] indicating which entries
      are valid for computing the pairwise comparisons.
    pairwise_logits_op: A pairwise function which operates on 2 tensors.

  Returns:
    A tuple of (pairwise_labels, pairwise_logits) with each having the shape
    [batch_size, list_size, list_size].
  """
  # Compute the difference for all pairs in a list. The output is a Tensor with
  # shape [batch_size, list_size, list_size] where the entry [-1, i, j] stores
  # the information for pair (i, j).
  pairwise_label_diff = _apply_pairwise_op(tf.subtract, labels)
  pairwise_logits = _apply_pairwise_op(pairwise_logits_op, logits)
  # Only keep the case when l_i > l_j.
  pairwise_labels = tf.cast(
      tf.greater(pairwise_label_diff, 0), dtype=tf.float32)
  valid_pair = _apply_pairwise_op(tf.logical_and, mask)
  pairwise_labels *= tf.cast(valid_pair, dtype=tf.float32)
  return pairwise_labels, pairwise_logits

pairwise_label_diff = _apply_pairwise_op(tf.subtract, labels) 

tf.subtract张量减法,被减张量维度可以不一样tf.expand_dims()和tf.squeeze()的用法详解_无尽的沉默的博客-CSDN博客

tf.expand_dims(tensor, 2), tf.expand_dims(tensor, 1)将labels在第2维扩展一维,在第一维扩展一维并相减,最终元素的维度保持不变

pairwise_logits = _apply_pairwise_op(pairwise_logits_op, logits)

base版本的pairwise_logits_op=tf.subtract,将logits做和labels相同的处理

def _apply_pairwise_op(op, tensor):
  """Applies the op on tensor in the pairwise manner."""
  _check_tensor_shapes([tensor])
  return op(tf.expand_dims(tensor, 2), tf.expand_dims(tensor, 1))

_apply_pairwise_op检查tensor的shape,通过subtract进行相减操作,构建pair

pairwise_labels = tf.cast(tf.greater(pairwise_label_diff, 0), dtype=tf.float32),满足条件的置为true,并强制转化为float类型。

【一看就懂】tf.logical_and(),tf.logical_or(),tf.logical_not(),tf.logical_xor()_城序猿的博客-CSDN博客

tf.cast()数据类型转换_-牧野-的博客-CSDN博客

tf.greater(v1,v2)和tf.where(p1,p2,p3)_牛客博客

2.3.5、计算pairwise_weights

代码中有多种pairwise_weights实现方式

pairwise_weights = pairwise_labels
    if self._lambda_weight is not None:
      pairwise_weights *= self._lambda_weight.pair_weights(labels, ranks)

2.3.6、pairwise_weight 梯度不反向传导

pairwise_weights = tf.stop_gradient(pairwise_weights, name='weights_stop_gradient')

Tensorflow中k.gradients()和tf.stop_gradient()的深入理解_tensorflow stop gradient_码农的科研笔记的博客-CSDN博客

2.3.7、compute_per_list以list为单元计算

def compute_per_list(self, labels, logits, weights, mask=None):
    """See `_RankingLoss`."""
    # Prepare input params.
    labels, logits, weights, mask = self._prepare_and_validate_params(
        labels, logits, weights, mask)

    # Pairwise losses and weights will be of shape
    # [batch_size, list_size, list_size].
    losses, loss_weights = self._compute_unreduced_loss_impl(
        labels, logits, mask)
    weights = tf.multiply(
        self._normalize_weights_impl(labels, weights), loss_weights)

    # Compute the weighted per-pair loss.
    weighted_per_pair_loss = tf.math.multiply(losses, weights)

    # Sum the inner dimensions to obtain per-list weights. For pairwise losses
    # this typically indicates the (weighted) number of pairwise preferences per
    # list.
    per_list_weights = tf.reduce_sum(weights, axis=[1, 2])

    # This computes the per-list losses by summing all weighted pairwise losses.
    per_list_losses = tf.reduce_sum(weighted_per_pair_loss, axis=[1, 2])

    # Normalize the per-list losses so that lists with different numbers of
    # pairs have comparable losses. The different numbers of pairs is reflected
    # in the per-list weights.
    per_list_losses = tf.math.divide_no_nan(per_list_losses, per_list_weights)

    return per_list_losses, per_list_weights

2.3.8、继承_PairtwiseLoss的类

PairwiseHingeLoss: Hinge(l_i > l_j) = max(0, 1 - (s_i - s_j)). So a
  correctly ordered pair has 0 loss if (s_i - s_j >= 1). Otherwise the loss
  increases linearly with s_i - s_j. When the list_size is 2, this reduces to
  the standard hinge loss.

loss=tf.nn.relu(1 - pairwise_logits)

PairwiseLogisticLoss: log(1 + exp(-pairwise_logits)).

 loss= tf.nn.relu(-pairwise_logits) + tf.math.log1p(tf.exp(-tf.abs(pairwise_logits)))

PairwiseSoftZeroOneLoss:PairwiseHingeLoss的一个变种,当 if (s_i - s_j <0)时,loss为tf.sigmoid(-pairwise_logits)

loss=tf.compat.v1.where(
        tf.greater(pairwise_logits, 0), 1. - tf.sigmoid(pairwise_logits),
        tf.sigmoid(-pairwise_logits))

PairwiseMSELoss:改写了_compute_unreduced_loss_impl函数,pairwise_mse_loss = tf.math.square(pairwise_logit_diff -pairwise_label_diff),这个loss面向回归的模型

def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    """See `_RankingLoss`."""
    if mask is None:
      mask = utils.is_label_valid(labels)

    # Compute loss.
    pairwise_label_diff = _apply_pairwise_op(tf.subtract, labels)
    pairwise_logit_diff = _apply_pairwise_op(tf.subtract, logits)
    pairwise_mse_loss = tf.math.square(pairwise_logit_diff -
                                       pairwise_label_diff)
    valid_pair = _apply_pairwise_op(tf.logical_and, mask)

    # Compute weights.
    pairwise_weights = tf.ones_like(pairwise_mse_loss)
    batch_size, list_size = tf.unstack(tf.shape(input=labels))
    # Excluding the self pairs.
    pairwise_weights -= tf.eye(
        list_size, batch_shape=[batch_size], dtype=pairwise_weights.dtype)
    # Including only valid pairs
    pairwise_weights *= tf.cast(valid_pair, tf.float32)
    if self._lambda_weight is not None:
      ranks = _compute_ranks(logits, mask)
      pairwise_weights *= self._lambda_weight.pair_weights(labels, ranks)
    pairwise_weights = tf.stop_gradient(
        pairwise_weights, name='weights_stop_gradient')

    return pairwise_mse_loss, pairwise_weights

2.4、_ListwiseLoss继承_RankingLoss实现listwise的基类

2.4.1、ListwiseLoss的实现

class _ListwiseLoss(_RankingLoss):
  """Interface for listwise loss."""

  def _normalize_weights_impl(self, labels, weights):
    """See `_RankingLoss`."""
    if weights is None:
      return 1.0
    else:
      weights = tf.convert_to_tensor(value=weights)
      labels = tf.convert_to_tensor(value=labels)
      is_valid = utils.is_label_valid(labels)
      labels = tf.where(is_valid, labels, tf.zeros_like(labels))
      return tf.compat.v1.math.divide_no_nan(
          tf.reduce_sum(input_tensor=(weights * labels), axis=1, keepdims=True),
          tf.reduce_sum(input_tensor=labels, axis=1, keepdims=True))

  def compute_per_list(self, labels, logits, weights, mask=None):
    """See `_RankingLoss`."""
    # Prepare input params.
    labels, logits, weights, mask = self._prepare_and_validate_params(
        labels, logits, weights, mask)

    # Listwise losses and weights will be of shape [batch_size, 1].
    losses, loss_weights = self._compute_unreduced_loss_impl(
        labels, logits, mask)
    weights = tf.multiply(
        self._normalize_weights_impl(labels, weights), loss_weights)

    # This removes the inner dimension of size 1 to make the output shape
    # [batch_size].
    per_list_losses = tf.squeeze(losses, axis=1)
    per_list_weights = tf.squeeze(weights, axis=1)
    return per_list_losses, per_list_weights

相比于pairwise,listwise少了构建pair的过程,直接在list维度进行计算。

2.4.2、继承_ListwiseLoss的listwise类

CircleLoss:https://arxiv.org/abs/2002.10857

Circle Loss: 一个基于对优化的统一视角-CVPR2020 - 知乎

L_circle = log(1 + sum_{i, j} I_{y_i > y_j}
                   exp(gamma * (a_j * (s_j - d_n) - a_i * (s_i - d_p))))

class CircleLoss(_ListwiseLoss):
  """Implements circle loss.

  This is the Circle loss originally proposed by Sun et al.
  ["Circle Loss: A Unified Perspective of Pair Similarity Optimization"]. See
  https://arxiv.org/abs/2002.10857.

  For a model that outputs similarity scores `s` on data point with
  corresponding label y, the circle loss from Eq.(6) in the paper is
    L_circle = log(1 + sum_{i is p,j is n}
                   exp(gamma * (a_j * (s_j - d_n) - a_i * (s_i - d_p)))),
  defined for the binary label, p for data points with positive labels and n for
  data points with negative labels.
    a_i = relu(1 + margin - s_i)
    a_j = relu(s_j + margin)
    d_p = 1 - margin
    d_n = margin
  We can extend to non-binary labels with an indiactor function,
    L_circle = log(1 + sum_{i, j} I_{y_i > y_j}
                   exp(gamma * (a_j * (s_j - d_n) - a_i * (s_i - d_p)))),
  Note the loss takes only the similarity scores. We will clip any score value
  beyond 0 and 1 to confine the scores in [0, 1], please be aware of that.
  """

  def __init__(self,
               name,
               lambda_weight=None,
               gamma=64,
               margin=0.25,
               ragged=False):
    """Initializer.

    Args:
      name: A string used as the name for this loss.
      lambda_weight: A `_LambdaWeight` object.
      gamma: A float parameter used in circle loss.
      margin: A float parameter defining the margin in circle loss.
      ragged: A boolean indicating whether the input tensors are ragged.
    """
    super().__init__(
        name, lambda_weight=lambda_weight, temperature=1.0, ragged=ragged)
    self._margin = margin
    self._gamma = gamma

  def get_logits(self, logits):
    """See `_RankingLoss`."""
    # Add a clip to confine scores in [0, 1].
    return tf.clip_by_value(tf.convert_to_tensor(value=logits), 0., 1.)

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    """See `_RankingLoss`."""
    if mask is None:
      mask = utils.is_label_valid(labels)

    def circle_loss_pairwise_op(score_i, score_j):
      alpha_i = tf.stop_gradient(
          tf.nn.relu(1 - score_i + self._margin), name='circle_loss_alpha_pos')
      alpha_j = tf.stop_gradient(
          tf.nn.relu(score_j + self._margin), name='circle_loss_alpha_neg')
      return alpha_i * (1 - score_i - self._margin) + alpha_j * (
          score_j - self._margin)

    pairwise_labels, pairwise_logits = _pairwise_comparison(
        labels, logits, mask, pairwise_logits_op=circle_loss_pairwise_op)
    pairwise_weights = tf.stop_gradient(
        pairwise_labels, name='weights_stop_gradient')
    # TODO: try lambda_weights for circle loss.
    # Pairwise losses and weights will be of shape
    # [batch_size, list_size, list_size].
    losses = tf.exp(self._gamma * pairwise_logits)

    # This computes the per-list losses and weights for circle loss.
    per_list_losses = tf.math.log1p(
        tf.reduce_sum(tf.math.multiply(losses, pairwise_weights), axis=[1, 2]))
    per_list_weights = tf.reduce_sum(
        pairwise_weights, axis=[1, 2]) / tf.reduce_sum(
            tf.cast(pairwise_weights > 0, tf.float32), axis=[1, 2])

    # Return per-list losses and weights with shape [batch_size, 1].
    return tf.expand_dims(per_list_losses,
                          1), tf.expand_dims(per_list_weights, 1)

使用circle_loss_pairwise_op计算alpha_i,alpha_j,将compute_per_list函数在_compute_unreduced_loss_impl这个函数里直接使用,获得list维度的loss。

SoftmaxLoss:https://dl.acm.org/doi/pdf/10.1145/3341981.3344221

label的softmax,logits的softmax求交叉熵loss:losses = tf.compat.v1.nn.softmax_cross_entropy_with_logits_v2(
        labels_for_softmax, logits_for_softmax)

具体实现如下:

class SoftmaxLoss(_ListwiseLoss):
  """Implements softmax loss."""

  def precompute(self, labels, logits, weights, mask=None):
    """Precomputes Tensors for softmax cross entropy inputs."""
    if mask is None:
      mask = utils.is_label_valid(labels)
    ranks = _compute_ranks(logits, mask)
    # Reset the masked labels to 0 and reset the masked logits to a logit with
    # ~= 0 contribution in softmax.
    labels = tf.compat.v1.where(mask, labels, tf.zeros_like(labels))
    logits = tf.compat.v1.where(mask, logits,
                                tf.math.log(_EPSILON) * tf.ones_like(logits))
    if self._lambda_weight is not None and isinstance(self._lambda_weight,
                                                      DCGLambdaWeight):
      labels = self._lambda_weight.individual_weights(labels, ranks)
    if weights is not None:
      labels *= weights
    return labels, logits

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    """See `_RankingLoss`."""
    if mask is None:
      mask = utils.is_label_valid(labels)
    label_sum = tf.reduce_sum(input_tensor=labels, axis=1, keepdims=True)
    # Padding for rows with label_sum = 0.
    nonzero_mask = tf.greater(tf.reshape(label_sum, [-1]), 0.0)
    padded_labels = tf.compat.v1.where(nonzero_mask, labels,
                                       _EPSILON * tf.ones_like(labels))
    padded_labels = tf.compat.v1.where(mask, padded_labels,
                                       tf.zeros_like(padded_labels))
    padded_label_sum = tf.reduce_sum(
        input_tensor=padded_labels, axis=1, keepdims=True)
    labels_for_softmax = tf.math.divide_no_nan(padded_labels, padded_label_sum)
    logits_for_softmax = logits
    # Padded labels have 0 weights in label_sum.
    weights_for_softmax = tf.reshape(label_sum, [-1])
    losses = tf.compat.v1.nn.softmax_cross_entropy_with_logits_v2(
        labels_for_softmax, logits_for_softmax)
    return losses, weights_for_softmax

  def compute(self, labels, logits, weights, reduction, mask=None):
    """See `_RankingLoss`."""
    labels, logits, weights, mask = self._prepare_and_validate_params(
        labels, logits, weights, mask)
    logits = self.get_logits(logits)
    labels, logits = self.precompute(labels, logits, weights, mask)
    losses, weights = self._compute_unreduced_loss_impl(labels, logits, mask)
    return tf.compat.v1.losses.compute_weighted_loss(
        losses, weights, reduction=reduction)

  def eval_metric(self, labels, logits, weights, mask=None):
    """See `_RankingLoss`."""
    labels, logits, weights, mask = self._prepare_and_validate_params(
        labels, logits, weights, mask)
    logits = self.get_logits(logits)
    labels, logits = self.precompute(labels, logits, weights, mask)
    losses, weights = self._compute_unreduced_loss_impl(labels, logits, mask)
    return tf.compat.v1.metrics.mean(losses, weights)

  def compute_per_list(self, labels, logits, weights, mask=None):
    """See `_RankingLoss`."""
    # Prepare input params.
    labels, logits, weights, mask = self._prepare_and_validate_params(
        labels, logits, weights, mask)

    # As opposed to the other listwise losses, SoftmaxLoss returns already
    # squeezed losses, which can be returned directly.
    logits = self.get_logits(logits)
    labels, logits = self.precompute(labels, logits, weights, mask)
    return self._compute_unreduced_loss_impl(labels, logits, mask)

  def compute_unreduced_loss(self, labels, logits, mask=None):
    """See `_RankingLoss`."""
    labels, logits, _, mask = self._prepare_and_validate_params(
        labels, logits, None, mask)
    logits = self.get_logits(logits)
    labels, logits = self.precompute(labels, logits, weights=None, mask=mask)
    return self._compute_unreduced_loss_impl(labels, logits, mask)

PolyOneSoftmaxLoss

PolyLoss:一种将分类损失函数加入泰勒展开式的损失函数 - 知乎

https://zhuanlan.zhihu.com/p/534094714

论文笔记: ICLR 2022 | POLYLOSS: A POLYNOMIAL EXPANSION PERSPECTIVE OF CLASSIFICATION LOSS FUNCTIONS - 知乎

PolyLoss 为理解和改进常用的ce和focal loss提供了一个框架,灵感来自于下面这两个损失函数的泰勒展开式:

pt = tf.reduce_sum(labels_for_softmax * tf.nn.softmax(logits_for_softmax), axis=-1)
ce = tf.compat.v1.nn.softmax_cross_entropy_with_logits_v2(labels_for_softmax, logits_for_softmax)
losses = ce + self._epsilon * (1 - pt)

详细实现

class PolyOneSoftmaxLoss(SoftmaxLoss):
  """Implements poly1 softmax loss."""

  def __init__(self,
               name,
               lambda_weight=None,
               epsilon=1.0,
               temperature=1.0,
               ragged=False):
    """Constructor.

    Args:
      name: A string used as the name for this loss.
      lambda_weight: A `_LambdaWeight` object.
      epsilon: A float number for contribution of the first polynomial.
      temperature: A float number to modify the logits=logits/temperature.
      ragged: A boolean indicating whether the input tensors are ragged.
    """
    super().__init__(
        name,
        lambda_weight=lambda_weight,
        temperature=temperature,
        ragged=ragged)
    self._epsilon = epsilon

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    """See `_RankingLoss`."""
    if mask is None:
      mask = utils.is_label_valid(labels)
    label_sum = tf.reduce_sum(input_tensor=labels, axis=1, keepdims=True)
    # Padding for rows with label_sum = 0.
    nonzero_mask = tf.greater(tf.reshape(label_sum, [-1]), 0.0)
    padded_labels = tf.compat.v1.where(nonzero_mask, labels,
                                       _EPSILON * tf.ones_like(labels))
    padded_labels = tf.compat.v1.where(mask, padded_labels,
                                       tf.zeros_like(padded_labels))
    padded_label_sum = tf.reduce_sum(
        input_tensor=padded_labels, axis=1, keepdims=True)
    labels_for_softmax = tf.math.divide_no_nan(padded_labels, padded_label_sum)
    logits_for_softmax = logits
    # Padded labels have 0 weights in label_sum.
    weights_for_softmax = tf.reshape(label_sum, [-1])
    pt = tf.reduce_sum(
        labels_for_softmax * tf.nn.softmax(logits_for_softmax), axis=-1)
    ce = tf.compat.v1.nn.softmax_cross_entropy_with_logits_v2(
        labels_for_softmax, logits_for_softmax)
    losses = ce + self._epsilon * (1 - pt)
    return losses, weights_for_softmax

UniqueSoftmaxLoss:https://arxiv.org/pdf/2001.01828.pdf

 Given the labels l_i and the logits s_i, the unique softmax loss is defined as
      -sum_i (2^l_i - 1) * log(exp(s_i) / (sum_j exp(s_j) + exp(s_i))),
  where j is over the documents with l_j < l_i.

class UniqueSoftmaxLoss(_ListwiseLoss):
  """Implements unique rating softmax loss."""

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    """See `_RankingLoss`."""
    if mask is None:
      mask = utils.is_label_valid(labels)
    labels = tf.compat.v1.where(mask, labels, tf.zeros_like(labels))
    logits = tf.compat.v1.where(mask, logits,
                                tf.math.log(_EPSILON) * tf.ones_like(logits))
    pairwise_labels, _ = _pairwise_comparison(labels, logits, mask)
    # Used in denominator to compute unique softmax probability for each doc.
    denominator_logits = tf.expand_dims(logits, axis=1) * pairwise_labels
    denominator_logits = tf.concat(
        [denominator_logits, tf.expand_dims(logits, axis=2)], axis=2)
    denominator_mask = tf.concat(
        [pairwise_labels,
         tf.expand_dims(tf.ones_like(logits), axis=2)], axis=2)
    denominator_logits = tf.where(
        tf.greater(denominator_mask, 0.0), denominator_logits, -1e-3 +
        tf.reduce_min(denominator_logits) * tf.ones_like(denominator_logits))
    logits_max = tf.reduce_max(denominator_logits, axis=-1, keepdims=True)
    # Subtract the max so that exp(denominator_logits) is numerically valid.
    denominator_logits -= logits_max
    logits -= tf.squeeze(logits_max, axis=-1)
    # Set gains for loss weights.
    gains = tf.pow(2.0, labels) - 1
    # Compute the softmax loss for each doc.
    per_doc_softmax = -logits + tf.math.log(
        tf.reduce_sum(tf.exp(denominator_logits) * denominator_mask, axis=-1))
    losses = tf.reduce_sum(per_doc_softmax * gains, axis=1, keepdims=True)
    return losses, tf.ones_like(losses)

MixtureEMLoss:多多个模型预测的logits融合

class MixtureEMLoss(_ListwiseLoss):
  """Implements the Mixture EM loss with examination and relevance.

  An Expecatation-Maximization (EM) algorithm is used for estimation and this
  function.
  """

  def __init__(self, name, temperature=1.0, alpha=1.0, ragged=False):
    super().__init__(name, None, temperature, ragged)
    self._alpha = alpha

  def _compute_model_prob(self, per_list_logodds):
    """Computes the probability of models in EM.

    Args:
      per_list_logodds: A `Tensor` with shape [batch_size, 1, model_num].

    Returns:
      A `Tensor` of probability with shape [batch_size, 1, model_num].
    """
    with tf.compat.v1.name_scope(name='compute_model_prob'):
      return tf.stop_gradient(
          tf.exp(-self._alpha *
                 (per_list_logodds -
                  tf.reduce_min(per_list_logodds, axis=2, keepdims=True))))

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    """Computes the loss for each element.

    Args:
      labels: A `Tensor` with shape [batch_size, list_size] representing clicks.
      logits: A `Tensor` with shape [batch_size, list_size, model_num], where
        the 3rd-dim is dimension for the models to mix.
      mask: A `Tensor` of the same shape as labels indicating which entries are
        valid for computing the loss.

    Returns:
      A tuple(losses, loss_weights).
    """
    if mask is None:
      mask = utils.is_label_valid(labels)
    labels = tf.compat.v1.where(mask, labels, tf.zeros_like(labels))
    # The loss in the M step.
    # shape = [batch_size, list_size, model_num]
    losses = tf.stack([
        tf.compat.v1.nn.sigmoid_cross_entropy_with_logits(
            labels=labels, logits=model_logits)
        for model_logits in tf.unstack(logits, axis=-1)
    ],
                      axis=2)
    losses = tf.where(
        tf.expand_dims(mask, axis=-1), losses,
        tf.zeros_like(losses, dtype=tf.float32))

    # The model probability in the E step.
    losses_no_gradient = tf.stop_gradient(losses)
    # shape = [batch_size, 1, model_num]
    per_list_logodds = tf.reduce_sum(losses_no_gradient, axis=1, keepdims=True)
    model_prob = self._compute_model_prob(per_list_logodds)
    prob_norm = tf.reduce_sum(model_prob, axis=2, keepdims=True)

    label_sum = tf.reduce_sum(input_tensor=labels, axis=1, keepdims=True)
    nonzero_mask = tf.greater(label_sum, 0.0)
    return tf.reshape(
        tf.reduce_sum(losses * model_prob / prob_norm, axis=[1, 2]),
        [-1, 1]), tf.cast(
            nonzero_mask, dtype=tf.float32)

ListMLELoss:ListNet和ListMLE - 知乎

排序学习-3.排序学习模型 - 知乎

Learning to Rank学习笔记--ListwiseRank - 知乎

在已知排序下,直接优化概率的负对数

class ListMLELoss(_ListwiseLoss):
  """Implements ListMLE loss."""

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    """See `_RankingLoss`."""
    if mask is None:
      mask = utils.is_label_valid(labels)
    # Reset the masked labels to 0 and reset the masked logits to a logit with
    # ~= 0 contribution.
    labels = tf.compat.v1.where(mask, labels, tf.zeros_like(labels))
    logits = tf.compat.v1.where(mask, logits,
                                tf.math.log(_EPSILON) * tf.ones_like(logits))
    scores = tf.compat.v1.where(
        mask, labels,
        tf.reduce_min(input_tensor=labels, axis=1, keepdims=True) -
        1e-6 * tf.ones_like(labels))
    # Use a fixed ops-level seed and the randomness is controlled by the
    # graph-level seed.
    sorted_labels, sorted_logits = utils.sort_by_scores(
        scores, [labels, logits], shuffle_ties=True, seed=37)

    raw_max = tf.reduce_max(input_tensor=sorted_logits, axis=1, keepdims=True)
    sorted_logits = sorted_logits - raw_max
    sums = tf.cumsum(tf.exp(sorted_logits), axis=1, reverse=True)
    sums = tf.math.log(sums) - sorted_logits

    if self._lambda_weight is not None and isinstance(self._lambda_weight,
                                                      ListMLELambdaWeight):
      batch_size, list_size = tf.unstack(tf.shape(input=sorted_labels))
      sums *= self._lambda_weight.individual_weights(
          sorted_labels,
          tf.tile(tf.expand_dims(tf.range(list_size) + 1, 0), [batch_size, 1]))

    negative_log_likelihood = tf.reduce_sum(
        input_tensor=sums, axis=1, keepdims=True)
    return negative_log_likelihood, tf.ones_like(negative_log_likelihood)

ApproxNDCGLoss:将ndcg可微分化函数表示,然后作为loss进行优化

排序学习-4.ApproxNDCG与NeuralNDCG - 知乎

https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-2008-164.pdf 这篇paper阐述了位置使用可微函数替代的近似,可以是sigmiod,也可以是其他的,论文只是以sigmiod为例进行阐述

 https://dl.acm.org/doi/pdf/10.1145/3331184.3331347

ApproxNDCGLoss的实现

class ApproxNDCGLoss(_ListwiseLoss):
  """Implements ApproxNDCG loss."""

  # Use a different default temperature.
  def __init__(self, name, lambda_weight=None, temperature=0.1, ragged=False):
    """See `_ListwiseLoss`."""
    super().__init__(name, lambda_weight, temperature, ragged)

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    """See `_RankingLoss`."""
    if mask is None:
      mask = utils.is_label_valid(labels)
    labels = tf.compat.v1.where(mask, labels, tf.zeros_like(labels))
    logits = tf.compat.v1.where(
        mask, logits, -1e3 * tf.ones_like(logits) +
        tf.reduce_min(input_tensor=logits, axis=-1, keepdims=True))

    label_sum = tf.reduce_sum(input_tensor=labels, axis=1, keepdims=True)
    nonzero_mask = tf.greater(tf.reshape(label_sum, [-1]), 0.0)
    labels = tf.compat.v1.where(nonzero_mask, labels,
                                _EPSILON * tf.ones_like(labels))
    ranks = approx_ranks(logits)

    return -ndcg(labels, ranks), tf.reshape(
        tf.cast(nonzero_mask, dtype=tf.float32), [-1, 1])

根据logits得分计算位置信息

def approx_ranks(logits):
  r"""Computes approximate ranks given a list of logits.

  Given a list of logits, the rank of an item in the list is one plus the total
  number of items with a larger logit. In other words,

    rank_i = 1 + \sum_{j \neq i} I_{s_j > s_i},

  where "I" is the indicator function. The indicator function can be
  approximated by a generalized sigmoid:

    I_{s_j < s_i} \approx 1/(1 + exp(-(s_j - s_i)/temperature)).

  This function approximates the rank of an item using this sigmoid
  approximation to the indicator function. This technique is at the core
  of "A general approximation framework for direct optimization of
  information retrieval measures" by Qin et al.

  Args:
    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the
      ranking score of the corresponding item.

  Returns:
    A `Tensor` of ranks with the same shape as logits.
  """
  list_size = tf.shape(input=logits)[1]
  x = tf.tile(tf.expand_dims(logits, 2), [1, 1, list_size])
  y = tf.tile(tf.expand_dims(logits, 1), [1, list_size, 1])
  pairs = tf.sigmoid(y - x)
  return tf.reduce_sum(input_tensor=pairs, axis=-1) + .5

利用得到的sigmiod表示的位置排序,计算ndcg指标

def ndcg(labels, ranks=None, perm_mat=None):
  """Computes NDCG from labels and ranks.

  Args:
    labels: A `Tensor` with shape [batch_size, list_size], representing graded
      relevance.
    ranks: A `Tensor` of the same shape as labels, or [1, list_size], or None.
      If ranks=None, we assume the labels are sorted in their rank.
    perm_mat: A `Tensor` with shape [batch_size, list_size, list_size] or None.
      Permutation matrices with rows correpond to the ranks and columns
      correspond to the indices. An argmax over each row gives the index of the
      element at the corresponding rank.

  Returns:
    A `tensor` of NDCG, ApproxNDCG, or ExpectedNDCG of shape [batch_size, 1].
  """
  if ranks is not None and perm_mat is not None:
    raise ValueError('Cannot use both ranks and perm_mat simultaneously.')

  if ranks is None:
    list_size = tf.shape(labels)[1]
    ranks = tf.range(list_size) + 1
  discounts = 1. / tf.math.log1p(tf.cast(ranks, dtype=tf.float32))
  gains = _safe_default_gain_fn(tf.cast(labels, dtype=tf.float32))
  if perm_mat is not None:
    gains = tf.reduce_sum(
        input_tensor=perm_mat * tf.expand_dims(gains, 1), axis=-1)
  dcg = tf.reduce_sum(input_tensor=gains * discounts, axis=-1, keepdims=True)
  normalized_dcg = dcg * inverse_max_dcg(labels, gain_fn=_safe_default_gain_fn)

为了避免极值出现的情况,采用了安全的gain计算逻辑

def _safe_default_gain_fn(labels):
  """Calculates safe gain functions for NDCG.

  In applications such as distillation, the labels could have extreme values
  that might result in numerical error when using the original gain function.
  This should only be applied to NDCG related losses, but not DCG ones. It
  should be applied on both the numerator and the denominator of NDCG.

  Args:
    labels: A `Tensor` with shape [batch_size, list_size], representing graded
      relevance.
  Returns:
    A `tensor` of safe gain function values of shape [batch_size, list_size].
  """
  max_labels = tf.reduce_max(labels, axis=-1, keepdims=True)
  gains = tf.pow(2., labels - max_labels) - tf.pow(2., -max_labels)
  return gains

根据labels排序计算最理想状态下的dcg

def inverse_max_dcg(labels,
                    gain_fn=lambda labels: tf.pow(2.0, labels) - 1.,
                    rank_discount_fn=lambda rank: 1. / tf.math.log1p(rank),
                    topn=None):
  """Computes the inverse of max DCG.

  Args:
    labels: A `Tensor` with shape [batch_size, list_size]. Each value is the
      graded relevance of the corresponding item.
    gain_fn: A gain function. By default this is set to: 2^label - 1.
    rank_discount_fn: A discount function. By default this is set to:
      1/log(1+rank).
    topn: An integer as the cutoff of examples in the sorted list.

  Returns:
    A `Tensor` with shape [batch_size, 1].
  """
  ideal_sorted_labels, = utils.sort_by_scores(labels, [labels], topn=topn)
  rank = tf.range(tf.shape(input=ideal_sorted_labels)[1]) + 1
  discounted_gain = gain_fn(ideal_sorted_labels) * rank_discount_fn(
      tf.cast(rank, dtype=tf.float32))
  discounted_gain = tf.reduce_sum(
      input_tensor=discounted_gain, axis=1, keepdims=True)
  return tf.compat.v1.where(
      tf.greater(discounted_gain, 0.), 1. / discounted_gain,
      tf.zeros_like(discounted_gain))


 

ApproxMRRLoss:近似mrr指标优化,将位置倒数用可微分可导函数表示,计算mrr指标,表示为待优化的loss。

MRR排序指标:平均倒数排名(Mean Reciprocal Rank,MRR),该指标反应的是我们找到的这些item是否摆在用户更明显的位置,强调位置关系,顺序性。公式如下,N表示推荐次数,��表示用户真实访问的item在推荐列表中的位置,如果没在推荐序列中,则p为无穷大,1/p为0。 

https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-2008-164.pdf

class ApproxMRRLoss(_ListwiseLoss):
  """Implements ApproxMRR loss."""

  # Use a different default temperature.
  def __init__(self, name, lambda_weight=None, temperature=0.1, ragged=False):
    """See `_ListwiseLoss`."""
    super().__init__(name, lambda_weight, temperature, ragged)

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    """See `_RankingLoss`."""
    if mask is None:
      mask = utils.is_label_valid(labels)
    labels = tf.compat.v1.where(mask, labels, tf.zeros_like(labels))
    logits = tf.compat.v1.where(
        mask, logits, -1e3 * tf.ones_like(logits) +
        tf.math.reduce_min(input_tensor=logits, axis=-1, keepdims=True))

    label_sum = tf.math.reduce_sum(input_tensor=labels, axis=1, keepdims=True)

    nonzero_mask = tf.math.greater(tf.reshape(label_sum, [-1]), 0.0)
    labels = tf.compat.v1.where(nonzero_mask, labels,
                                _EPSILON * tf.ones_like(labels))

    rr = 1. / approx_ranks(logits)
    rr = tf.math.reduce_sum(input_tensor=rr * labels, axis=-1, keepdims=True)
    mrr = rr / tf.math.reduce_sum(input_tensor=labels, axis=-1, keepdims=True)
    return -mrr, tf.reshape(tf.cast(nonzero_mask, dtype=tf.float32), [-1, 1])

NeuralSortNDCGLoss:NDCG指标loss化的扩展版

class NeuralSortNDCGLoss(_ListwiseLoss):
  """Implements PiRank-NDCG loss.

  The PiRank-NDCG loss is a differentiable approximation of the NDCG metric
  using the NeuralSort trick, which generates a permutation matrix based on
  ranking scores. Please refer to https://arxiv.org/abs/2012.06731 for the
  PiRank method. For PiRank-NDCG in specific,
    NDCG_metric = - sum_i (2^y_i - 1) / log(1 + r_i) / maxDCG,
  where y_i and r_i are the label and the score rank of the ith document
  respectively. This metric can be also written as the sum over rank r with an
  indicator function I,
    NDCG_metric = - sum_{i,r} (2^y_i - 1) / log(1 + r) * I(r, r_i) / maxDCG,
  where the indicator function I(r, r_i) = 1 if r = r_i and 0 otherwise, which
  is the permutation matrix.

  Approximated with a differentiable permutation matrix using neural sort,
    PiRank-NDCG = - sum_{i,r} (2^y_i - 1) / log(1 + r) * P(r, i) / maxDCG,
  where P(r, i) is the approximation of the permutation matrix.
  """

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    """See `_RankingLoss`."""
    if mask is None:
      mask = utils.is_label_valid(labels)
    labels = tf.compat.v1.where(mask, labels, tf.zeros_like(labels))
    logits = tf.compat.v1.where(mask, logits, tf.zeros_like(logits))

    label_sum = tf.reduce_sum(input_tensor=labels, axis=1, keepdims=True)
    nonzero_mask = tf.greater(tf.reshape(label_sum, [-1]), 0.0)
    # shape = [batch_size, list_size].
    labels = tf.compat.v1.where(nonzero_mask, labels,
                                _EPSILON * tf.ones_like(labels))
    # shape = [batch_size, list_size, list_size].
    smooth_perm = neural_sort(logits, mask=mask)

    return -ndcg(
        labels, perm_mat=smooth_perm), tf.reshape(
            tf.cast(nonzero_mask, dtype=tf.float32), [-1, 1])


def neural_sort(logits, name=None, mask=None):
  r"""Generate the permutation matrix from logits by deterministic neuralsort.

  The sort on a list of logits can be approximated by a differentiable
  permutation matrix using Neural Sort (https://arxiv.org/abs/1903.08850).
  The approximation is achieved by constructing a list of functions on logits,
    fn_i(k) = (list_size + 1 - 2*i) * logit_k - sum_j |logit_k - logit_j|,
  whose value is maximal when k is at the ith largest logit.
  So that the permutation matrix can be expressed as
           / 1 if j = argmax_k fn_i(k)
    P_ij = |                           = one_hot(argmax(fn_i(j))).
           \ 0 otherwise
  And the differentiable approximation of the matrix is applied with softmax,
    P^_ij = softmax(fn_i(j) / temperature),
  where the parameter temperature tunes the smoothiness of the approximation.

  #### References
  [1]: Aditya Grover, Eric Wang, Aaron Zweig, Stefano Ermon.
       Stochastic Optimization of Sorting Networks via Continuous Relaxations.
       https://arxiv.org/abs/1903.08850

  Args:
    logits: A `Tensor` with shape [batch_size, list_size]. Each value is the
      ranking score of the corresponding item. (We are using logits here,
      noticing the original paper is using probability weights, i.e., the
      exponentials of the logits).
    name: A string used as the name for this loss.
    mask: A `Tensor` with the same shape as logits indicating which entries are
      valid for computing the neural_sort. Invalid entries are pushed to the
      end.

  Returns:
    A tensor of permutation matrices whose dimension is [batch_size, list_size,
    list_size].
  """
  with tf.compat.v1.name_scope(name, 'neural_sort', [logits]):
    if mask is None:
      mask = tf.ones_like(logits, dtype=tf.bool)

    # Reset logits to 0 and compute number of valid entries for each list in the
    # batch.
    logits = tf.where(mask, logits, tf.zeros_like(logits))
    num_valid_entries = tf.reduce_sum(
        tf.cast(mask, dtype=tf.int32), axis=1, keepdims=True)

    # Compute logit differences and mask out invalid entries.
    logit_diff = tf.abs(tf.expand_dims(logits, 2) - tf.expand_dims(logits, 1))
    valid_pair_mask = _apply_pairwise_op(tf.logical_and, mask)
    logit_diff = tf.where(valid_pair_mask, logit_diff,
                          tf.zeros_like(logit_diff))
    # shape = [batch_size, 1, list_size].
    logit_diff_sum = tf.reduce_sum(
        input_tensor=logit_diff, axis=1, keepdims=True)

    # Compute masked range so that masked items do not influence scaling.
    masked_range = tf.cumsum(tf.cast(mask, dtype=tf.int32), axis=1)
    scaling = tf.cast(
        num_valid_entries + 1 - 2 * masked_range, dtype=tf.float32)
    # shape = [batch_size, list_size].
    scaling = tf.expand_dims(scaling, 2)
    # shape = [batch_size, list_size, list_size].
    # Use broadcast to align the dims.
    scaled_logits = scaling * tf.expand_dims(logits, 1)

    p_logits = scaled_logits - logit_diff_sum

    # Masked entries will be forcefully kept in-place by setting their values to
    # -inf everywhere, except for masked rows where they share equal probability
    # with other masked items.
    p_logits = tf.where(valid_pair_mask, p_logits, -math.inf)
    p_logits = tf.where(
        _apply_pairwise_op(tf.logical_or, mask), p_logits,
        tf.zeros_like(p_logits))

    # By swapping the rows of masked items to the end of the permutation matrix,
    # we force masked items to be placed last.
    sorted_mask_indices = tf.argsort(
        tf.cast(mask, dtype=tf.int32),
        axis=1,
        direction='DESCENDING',
        stable=True)
    p_logits = tf.gather(p_logits, sorted_mask_indices, batch_dims=1, axis=1)

    smooth_perm = tf.nn.softmax(p_logits, -1)

    return smooth_perm

CoupledRankDistilLoss:

class CoupledRankDistilLoss(_ListwiseLoss):
  r"""Implements Coupled-RankDistil loss.

  The Coupled-RankDistil loss ([Reddi et al, 2021][reddi2021]) is the
  cross-entropy between k-Plackett's probability of logits (student) and labels
  (teacher).

  The k-Plackett's probability model is defined as:
  $$
  \mathcal{P}_k(\pi|s) = \frac{1}{(N-k)!} \\
  \frac{\prod_{i=1}^k exp(s_{\pi(i)})}{\sum_{j=k}^N log(exp(s_{\pi(i)}))}.
  $$

  The Coupled-RankDistil loss is defined as:
  $$
  \mathcal{L}(y, s) = -\sum_{\pi} \mathcal{P}_k(\pi|y) log\mathcal{P}(\pi|s) \\
  =  \mathcal{E}_{\pi \sim \matcal{P}(.|y)} [-\log \mathcal{P}(\pi|s)]
  $$

    References:
    - [RankDistil: Knowledge Distillation for Ranking, Reddi et al,
       2021][reddi2021]

  [reddi2021]: https://research.google/pubs/pub50695/
  """

  def __init__(self,
               name,
               sample_size,
               topk=None,
               temperature=1.,
               ragged=False):
    """Initializer.

    Args:
      name: A string used as the name for this loss.
      sample_size: Number of permutations to sample from teacher scores.
      topk: top-k entries over which order is matched. A penalty is applied over
        non top-k items.
      temperature: A float number to modify the logits as
        `logits=logits/temperature`.
      ragged: A boolean indicating whether the input tensors are ragged.
    """
    super().__init__(name, None, temperature, ragged)
    self._sample_size = sample_size
    self._topk = topk

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    """See `_RankingLoss`."""
    if mask is None:
      mask = utils.is_label_valid(labels)
    labels = tf.where(mask, labels, tf.zeros_like(labels))
    label_sum = tf.reduce_sum(input_tensor=labels, axis=1, keepdims=True)
    nonzero_mask = tf.greater(tf.reshape(label_sum, [-1]), 0.0)

    teacher_scores = tf.where(mask, labels,
                              tf.math.log(_EPSILON) * tf.ones_like(labels))

    student_scores = tf.where(mask, logits,
                              tf.math.log(_EPSILON) * tf.ones_like(logits))

    # Sample teacher scores.
    # [batch_size, list_size] -> [batch_size, sample_size, list_size].
    sampled_teacher_scores = tf.expand_dims(teacher_scores, 1)
    sampled_teacher_scores = tf.repeat(
        sampled_teacher_scores, [self._sample_size], axis=1)

    batch_size, list_size = tf.unstack(tf.shape(input=labels))
    sampled_teacher_scores += _sample_gumbel(
        [batch_size, self._sample_size, list_size], seed=37)
    sampled_teacher_scores = tf.math.log(
        tf.nn.softmax(sampled_teacher_scores) + _EPSILON)

    # Expand student scores.
    # [batch_size, list_size] -> [batch_size, sample_size, list_size].
    expanded_student_scores = tf.expand_dims(student_scores, 1)
    expanded_student_scores = tf.repeat(
        expanded_student_scores, [self._sample_size], axis=1)

    # Sort teacher scores and student scores to obtain top-k student scores
    # whose order is based on teacher scores.
    sorted_student_scores = utils.sort_by_scores(
        utils.reshape_first_ndims(sampled_teacher_scores, 2,
                                  [batch_size * self._sample_size]),
        [
            utils.reshape_first_ndims(expanded_student_scores, 2,
                                      [batch_size * self._sample_size])
        ],
        shuffle_ties=True,
        seed=37)[0]
    sorted_student_scores = utils.reshape_first_ndims(
        sorted_student_scores, 1, [batch_size, self._sample_size])
    topk = self._topk or list_size
    topk_student_scores = sorted_student_scores[:, :, :topk]

    # For \pi from teacher scores, compute top-k Plackett's probability as:
    # \prod_{i=1}^k exp(s_{\pi(i)}) / \sum_{j=k}^N log(exp(s_{\pi(i)})).

    # Compute the denominator mask for  \sum_{j=k}^N log(exp(s_{\pi(i)}).
    # We apply logsumexp over valid entries in this mask.
    # topk_pl_denominator_mask = batch x sample_size x valid_denom_entries,
    # where valid_denom_entries = [[1 1 1 1 1 1]
    #                             [0 1 1 1 1 1]
    #                             [0 0 1 1 1 1]].
    # An alternative implementation would be to use `cumulative_logsumexp` with
    # `reverse=True` to compute the denominator term.
    ones = tf.ones((topk, list_size), dtype=tf.float32)
    ones_upper = tf.linalg.band_part(ones, 0, -1)
    topk_pl_denominator_mask = tf.tile(
        tf.expand_dims(ones_upper, axis=0),
        [batch_size * self._sample_size, 1, 1])
    # [batch_size * sample_size, topk, list_size] ->
    # [batch_size, sample_size, topk, list_size].
    topk_pl_denominator_mask = tf.cast(
        utils.reshape_first_ndims(topk_pl_denominator_mask, 1,
                                  [batch_size, self._sample_size]),
        dtype=tf.bool)
    sorted_student_scores = tf.tile(
        tf.expand_dims(sorted_student_scores, 2), [1, 1, topk, 1])

    sorted_student_scores_denom = tf.where(
        topk_pl_denominator_mask, sorted_student_scores,
        tf.math.log(_EPSILON) * tf.ones_like(sorted_student_scores))
    logprob = topk_student_scores - tf.math.reduce_logsumexp(
        sorted_student_scores_denom, axis=3)
    # Compute log-likelihood over top-k Plackett-Luce scores.
    # [batch_size, sample_size, topk] -> [batch_size, sample_size].
    logprob = tf.reduce_sum(logprob, axis=2)

    # Compute RankDistil loss as a mean over samples.
    # [batch_size, sample_size] -> [batch_size, 1].
    nll = tf.reduce_mean(-logprob, axis=1, keepdims=True)

    return nll, tf.reshape(tf.cast(nonzero_mask, dtype=tf.float32), [-1, 1])

3、实例

3.1、各种loss基类实现

import tensorflow as tf
_EPSILON = 1e-10

def is_label_valid(labels):
  labels = tf.convert_to_tensor(value=labels)
  return tf.greater_equal(labels, 0.)

def _check_tensor_shapes(tensors):
  for tensor in tensors:
    tensor = tf.convert_to_tensor(value=tensor)
    tensor.get_shape().assert_has_rank(2)
    tensor.get_shape().assert_is_compatible_with(
        tf.convert_to_tensor(value=tensors[0]).get_shape())

def _apply_pairwise_op(op, tensor):
  _check_tensor_shapes([tensor])
  return op(tf.expand_dims(tensor, 2), tf.expand_dims(tensor, 1))

def _pairwise_comparison(labels, logits, mask, pairwise_logits_op=tf.subtract):
  pairwise_label_diff = _apply_pairwise_op(tf.subtract, labels)
  pairwise_logits = _apply_pairwise_op(pairwise_logits_op, logits)
  pairwise_labels = tf.cast(
      tf.greater(pairwise_label_diff, 0), dtype=tf.float32)
  valid_pair = _apply_pairwise_op(tf.logical_and, mask)
  pairwise_labels *= tf.cast(valid_pair, dtype=tf.float32)
  return pairwise_labels, pairwise_logits

def sort_by_scores(scores,
                   features_list,
                   topn=None,
                   shuffle_ties=True,
                   seed=None,
                   mask=None):
  with tf.compat.v1.name_scope(name='sort_by_scores'):
    scores = tf.cast(scores, tf.float32)
    scores.get_shape().assert_has_rank(2)
    list_size = tf.shape(input=scores)[1]
    if topn is None:
      topn = list_size
    topn = tf.minimum(topn, list_size)
    _, indices = tf.math.top_k(scores, topn, sorted=True)
    return [tf.gather(f, indices, batch_dims=1, axis=1) for f in features_list]

def sorted_ranks(scores, shuffle_ties=True, seed=None):
  with tf.compat.v1.name_scope(name='sorted_ranks'):
    batch_size, list_size = tf.unstack(tf.shape(input=scores))
    positions = tf.tile(tf.expand_dims(tf.range(list_size), 0), [batch_size, 1])
    sorted_positions = sort_by_scores(
        scores, [positions], shuffle_ties=shuffle_ties, seed=seed)[0]
    ranks = tf.argsort(sorted_positions) + 1
    return ranks


def _compute_ranks(logits, is_valid):
  _check_tensor_shapes([logits, is_valid])
  scores = tf.compat.v1.where(
      is_valid, logits, -1e-6 * tf.ones_like(logits) +
      tf.reduce_min(input_tensor=logits, axis=1, keepdims=True))
  return sorted_ranks(scores)

class _RankingLoss(object):
  def __init__(self, name, lambda_weight=None, temperature=1.0, ragged=False):
    self._name = name
    self._lambda_weight = lambda_weight
    self._temperature = temperature
    self._ragged = ragged

  @property
  def name(self):
    return self._name

  def _prepare_and_validate_params(self, labels, logits, weights, mask):
    labels = tf.convert_to_tensor(labels)
    logits = tf.convert_to_tensor(logits)
    if weights is None:
      weights = 1.0
    if mask is None:
      mask = is_label_valid(labels)
    weights = tf.convert_to_tensor(weights)
    mask = tf.convert_to_tensor(mask)
    return labels, logits, weights, mask

  def compute_unreduced_loss(self, labels, logits, mask=None):
    labels, logits, _, mask = self._prepare_and_validate_params(
        labels, logits, None, mask)
    return self._compute_unreduced_loss_impl(labels, logits, mask)

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    raise NotImplementedError('Calling an abstract method.')

  def normalize_weights(self, labels, weights):
    return self._normalize_weights_impl(labels, weights)

  def _normalize_weights_impl(self, labels, weights):
    del labels
    return 1.0 if weights is None else weights

  def get_logits(self, logits):
    if not tf.is_tensor(logits):
      logits = tf.convert_to_tensor(value=logits)
    return logits / self._temperature

  def compute(self, labels, logits, weights, reduction, mask=None):
    logits = self.get_logits(logits)
    losses, loss_weights = self._compute_unreduced_loss_impl(
        labels, logits, mask)
    weights = tf.multiply(
        self._normalize_weights_impl(labels, weights), loss_weights)
    return tf.compat.v1.losses.compute_weighted_loss(
        losses, weights, reduction=reduction)

  def compute_per_list(self, labels, logits, weights, mask=None):
    raise NotImplementedError('Calling an abstract method.')

  def eval_metric(self, labels, logits, weights, mask=None):
    losses, loss_weights = self._compute_unreduced_loss_impl(
        labels, logits, mask)
    weights = tf.multiply(
        self._normalize_weights_impl(labels, weights), loss_weights)
    return tf.compat.v1.metrics.mean(losses, weights)

class _PointwiseLoss(_RankingLoss):
  def _normalize_weights_impl(self, labels, weights):
    if weights is None:
      weights = 1.
    return tf.compat.v1.where(
        is_label_valid(labels),
        tf.ones_like(labels) * weights, tf.zeros_like(labels))

  def compute_per_list(self, labels, logits, weights, mask=None):
    labels, logits, weights, mask = self._prepare_and_validate_params(
        labels, logits, weights, mask)

    losses, loss_weights = self._compute_unreduced_loss_impl(
        labels, logits, mask)
    weights = tf.multiply(
        self._normalize_weights_impl(labels, weights), loss_weights)

    weighted_per_item_loss = tf.math.multiply(losses, weights)

    per_list_weights = tf.reduce_sum(weights, axis=1)

    per_list_losses = tf.reduce_sum(weighted_per_item_loss, axis=1)

    per_list_losses = tf.math.divide_no_nan(per_list_losses, per_list_weights)
    return per_list_losses, per_list_weights

class _PairwiseLoss(_RankingLoss):
  def _pairwise_loss(self, pairwise_logits):
    raise NotImplementedError('Calling an abstract method.')

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    if mask is None:
      mask = is_label_valid(labels)
    ranks = _compute_ranks(logits, mask)
    pairwise_labels, pairwise_logits = _pairwise_comparison(
        labels, logits, mask)
    pairwise_weights = pairwise_labels
    pairwise_weights = tf.stop_gradient(
        pairwise_weights, name='weights_stop_gradient')
    return self._pairwise_loss(pairwise_logits), pairwise_weights

  def compute_per_list(self, labels, logits, weights, mask=None):
    labels, logits, weights, mask = self._prepare_and_validate_params(
        labels, logits, weights, mask)
    losses, loss_weights = self._compute_unreduced_loss_impl(
        labels, logits, mask)
    weights = tf.multiply(
        self._normalize_weights_impl(labels, weights), loss_weights)
    weighted_per_pair_loss = tf.math.multiply(losses, weights)
    per_list_weights = tf.reduce_sum(weights, axis=[1, 2])
    per_list_losses = tf.reduce_sum(weighted_per_pair_loss, axis=[1, 2])
    per_list_losses = tf.math.divide_no_nan(per_list_losses, per_list_weights)
    return per_list_losses, per_list_weights

  def _normalize_weights_impl(self, labels, weights):
    if weights is None:
      weights = 1.
    weights = tf.compat.v1.where(
        is_label_valid(labels),
        tf.ones_like(labels) * weights, tf.zeros_like(labels))
    return tf.expand_dims(weights, axis=2)



class _ListwiseLoss(_RankingLoss):

  def _normalize_weights_impl(self, labels, weights):
    if weights is None:
      return 1.0
    else:
      weights = tf.convert_to_tensor(value=weights)
      labels = tf.convert_to_tensor(value=labels)
      is_valid = is_label_valid(labels)
      labels = tf.where(is_valid, labels, tf.zeros_like(labels))
      return tf.compat.v1.math.divide_no_nan(
          tf.reduce_sum(input_tensor=(weights * labels), axis=1, keepdims=True),
          tf.reduce_sum(input_tensor=labels, axis=1, keepdims=True))

  def compute_per_list(self, labels, logits, weights, mask=None):
    labels, logits, weights, mask = self._prepare_and_validate_params(
        labels, logits, weights, mask)
    losses, loss_weights = self._compute_unreduced_loss_impl(
        labels, logits, mask)
    weights = tf.multiply(
        self._normalize_weights_impl(labels, weights), loss_weights)
    per_list_losses = tf.squeeze(losses, axis=1)
    per_list_weights = tf.squeeze(weights, axis=1)
    return per_list_losses, per_list_weights

class SigmoidCrossEntropyLoss(_PointwiseLoss):
  def __init__(self, name, temperature=1.0, ragged=False):
    super().__init__(name, None, temperature, ragged)
  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    if mask is None:
      mask = is_label_valid(labels)
    labels = tf.compat.v1.where(mask, labels, tf.zeros_like(labels))
    logits = tf.compat.v1.where(mask, logits, tf.zeros_like(logits))
    losses = tf.compat.v1.nn.sigmoid_cross_entropy_with_logits(
        labels=labels, logits=logits)
    return losses, tf.cast(mask, dtype=tf.float32)

class PairwiseLogisticLoss(_PairwiseLoss):
  def _pairwise_loss(self, pairwise_logits):
    return tf.nn.relu(-pairwise_logits) + tf.math.log1p(
        tf.exp(-tf.abs(pairwise_logits)))

class SoftmaxLoss(_ListwiseLoss):
  def precompute(self, labels, logits, weights, mask=None):
    if mask is None:
      mask = is_label_valid(labels)
    ranks = _compute_ranks(logits, mask)
    # Reset the masked labels to 0 and reset the masked logits to a logit with
    # ~= 0 contribution in softmax.
    labels = tf.compat.v1.where(mask, labels, tf.zeros_like(labels))
    logits = tf.compat.v1.where(mask, logits,
                                tf.math.log(_EPSILON) * tf.ones_like(logits))
    if weights is not None:
      labels *= weights
    return labels, logits

  def _compute_unreduced_loss_impl(self, labels, logits, mask=None):
    if mask is None:
      mask = is_label_valid(labels)
    label_sum = tf.reduce_sum(input_tensor=labels, axis=1, keepdims=True)
    nonzero_mask = tf.greater(tf.reshape(label_sum, [-1]), 0.0)
    padded_labels = tf.compat.v1.where(nonzero_mask, labels,
                                       _EPSILON * tf.ones_like(labels))
    padded_labels = tf.compat.v1.where(mask, padded_labels,
                                       tf.zeros_like(padded_labels))
    padded_label_sum = tf.reduce_sum(
        input_tensor=padded_labels, axis=1, keepdims=True)
    labels_for_softmax = tf.math.divide_no_nan(padded_labels, padded_label_sum)
    logits_for_softmax = logits
    weights_for_softmax = tf.reshape(label_sum, [-1])
    losses = tf.compat.v1.nn.softmax_cross_entropy_with_logits_v2(
        labels_for_softmax, logits_for_softmax)
    return losses, weights_for_softmax

  def compute(self, labels, logits, weights, reduction, mask=None):
    labels, logits, weights, mask = self._prepare_and_validate_params(
        labels, logits, weights, mask)
    logits = self.get_logits(logits)
    labels, logits = self.precompute(labels, logits, weights, mask)
    losses, weights = self._compute_unreduced_loss_impl(labels, logits, mask)
    return tf.compat.v1.losses.compute_weighted_loss(
        losses, weights, reduction=reduction)

  def eval_metric(self, labels, logits, weights, mask=None):
    labels, logits, weights, mask = self._prepare_and_validate_params(
        labels, logits, weights, mask)
    logits = self.get_logits(logits)
    labels, logits = self.precompute(labels, logits, weights, mask)
    losses, weights = self._compute_unreduced_loss_impl(labels, logits, mask)
    return tf.compat.v1.metrics.mean(losses, weights)

  def compute_per_list(self, labels, logits, weights, mask=None):
    labels, logits, weights, mask = self._prepare_and_validate_params(
        labels, logits, weights, mask)

    logits = self.get_logits(logits)
    labels, logits = self.precompute(labels, logits, weights, mask)
    return self._compute_unreduced_loss_impl(labels, logits, mask)

  def compute_unreduced_loss(self, labels, logits, mask=None):
    labels, logits, _, mask = self._prepare_and_validate_params(
        labels, logits, None, mask)
    logits = self.get_logits(logits)
    labels, logits = self.precompute(labels, logits, weights=None, mask=mask)
    return self._compute_unreduced_loss_impl(labels, logits, mask)

3.2、pointwise测试用例

3.2.1、代码实现

# pointwise测试
import tensorflow as tf
from rank_loss import SigmoidCrossEntropyLoss
import math

def _sigmoid_cross_entropy(labels, logits):
  def per_position_loss(logit, label):
    return max(logit, 0) - logit * label + math.log(1 + math.exp(-abs(logit)))

  return sum(
      per_position_loss(logit, label) for label, logit in zip(labels, logits))

class SigmoidCrossEntropyLossTest(tf.test.TestCase):

  def test_sigmoid_cross_entropy_loss(self):
    scores = [[0.2, 0.5, 0.3], [0.2, 0.3, 0.5], [0.2, 0.3, 0.5]]
    labels = [[0., 0., 1.], [0., 0., 2.], [0., 0., 0.]]
    weights = [[2.], [1.], [1.]]
    reduction = tf.compat.v1.losses.Reduction.SUM_BY_NONZERO_WEIGHTS
    loss_fn = SigmoidCrossEntropyLoss(name=None)
    loss1=loss_fn.compute(labels, scores, None, reduction).numpy()
    loss2=(_sigmoid_cross_entropy(labels[0], scores[0]) +
          _sigmoid_cross_entropy(labels[1], scores[1]) +
         _sigmoid_cross_entropy(labels[2], scores[2])) / 9.
    print("loss1:",loss1)
    print("loss2:",loss2)

    loss3=loss_fn.compute(labels, scores, weights, reduction).numpy()
    loss4=(_sigmoid_cross_entropy(labels[0], scores[0]) * 2.0 +
         _sigmoid_cross_entropy(labels[1], scores[1]) +
         _sigmoid_cross_entropy(labels[2], scores[2])) / 9
    print("weight loss3:",loss3)
    print("weight loss4:",loss4)

  def test_sigmoid_cross_entropy_loss_should_handle_mask(self):
    scores = [[1., 3., 2.]]
    labels = [[0., 1., 1.]]
    mask = [[True, False, True]]
    reduction = tf.compat.v1.losses.Reduction.SUM_BY_NONZERO_WEIGHTS

    loss_fn = SigmoidCrossEntropyLoss(name=None)
    result = loss_fn.compute(labels, scores, None, reduction, mask).numpy()
    loss=(math.log(1. + math.exp(-2.)) + math.log(1. + math.exp(1.))) / 2.

    print("result:",result)
    print("loss:",loss)


scel=SigmoidCrossEntropyLossTest()
scel.test_sigmoid_cross_entropy_loss()
scel.test_sigmoid_cross_entropy_loss_should_handle_mask()

3.2.2、运行结果

#直接cross entropy loss的结果
loss1: 0.7310792
loss2: 0.7310792548989641

#加权;loss的结果对比
weight loss3: 0.9895871
weight loss4: 0.9895871546801003

#mask掉一部分数据的结果对比
result: 0.72009486
loss: 0.7200948492805977

3.3、pairwise测试用例

3.3.1、代码实现

# pairwise测试
import tensorflow as tf
from rank_loss import PairwiseLogisticLoss
import math

class PairwiseLogisticLossTest(tf.test.TestCase):

  def test_pairwise_logistic_loss(self):
    scores = [[1., 3., 2.], [1., 2., 3.]]
    labels = [[0., 0., 1.], [0., 0., 2.]]
    reduction = tf.compat.v1.losses.Reduction.MEAN

    loss_fn = PairwiseLogisticLoss(name=None)
    result = loss_fn.compute(labels, scores, weights=None, reduction=reduction)

    logloss = lambda x: math.log(1. + math.exp(-x))
    expected = (logloss(3. - 2.) + logloss(1. - 2.) + logloss(3. - 1.) +
                logloss(3. - 2.)) / 4.

    print("result:",result)
    print("expected:",expected)

  def test_pairwise_logistic_loss_should_handle_per_list_weights(self):
    scores = [[1., 3., 2.], [1., 2., 3.]]
    labels = [[0., 0., 1.], [0., 0., 2.]]
    weights = [[1.], [2.]]
    reduction = tf.compat.v1.losses.Reduction.MEAN

    loss_fn = PairwiseLogisticLoss(name=None)
    result = loss_fn.compute(
        labels, scores, weights=weights, reduction=reduction)

    logloss = lambda x: math.log(1. + math.exp(-x))
    expected = (1. * (logloss(3. - 2.) + logloss(1. - 2.)) + 2. *
                (logloss(3. - 2.) + logloss(3. - 1.))) / 6.
    print("result:",result)
    print("expected:",expected)

pll=PairwiseLogisticLossTest()
pll.test_pairwise_logistic_loss()
pll.test_pairwise_logistic_loss_should_handle_per_list_weights()

3.3.2、运行结果

#对齐的pair情况
result: tf.Tensor(0.5166783, shape=(), dtype=float32)
expected: 0.5166782683994103
#需要补齐的代码情况
result: tf.Tensor(0.41781712, shape=(), dtype=float32)
expected: 0.4178171286931394

3.4、listwise测试用例

3.4.1、代码实现

# listwise测试
import tensorflow as tf
from rank_loss import SoftmaxLoss
import math

def _softmax(values):
  total = sum(math.exp(v) for v in values)
  return [math.exp(v) / (1e-20 + total) for v in values]

class SoftmaxLossTest(tf.test.TestCase):

  def test_softmax_loss(self):
    scores = [[1., 3., 2.], [1., 2., 3.], [1., 2., 3.]]
    labels = [[0., 0., 1.], [0., 0., 2.], [0., 0., 0.]]
    reduction = tf.compat.v1.losses.Reduction.SUM_BY_NONZERO_WEIGHTS

    loss_fn = SoftmaxLoss(name=None)
    result = loss_fn.compute(labels, scores, None, reduction).numpy()
    expect=-(math.log(_softmax(scores[0])[2]) +
      math.log(_softmax(scores[1])[2]) * 2.) / 2.
    print("result:",result)
    print("expect:",expect)

  def test_softmax_loss_should_handle_per_example_weights(self):
    scores = [[1., 3., 2.], [1., 2., 3.], [1., 2., 3.]]
    labels = [[0., 0., 1.], [1., 1., 2.], [0., 0., 0.]]
    example_weights = [[1., 1., 1.], [1., 2., 3.], [1., 0., 1.]]
    reduction = tf.compat.v1.losses.Reduction.SUM_BY_NONZERO_WEIGHTS
    probs = [_softmax(s) for s in scores]

    loss_fn = SoftmaxLoss(name=None)
    result = loss_fn.compute(labels, scores, example_weights, reduction).numpy()
    expect=-(math.log(probs[0][2]) * 1. + math.log(probs[1][0]) * 1. * 1. +
          math.log(probs[1][1]) * 1. * 2. + math.log(probs[1][2]) * 2. * 3.) /2.
    print("result:",result)
    print("expect:",expect)

  def test_softmax_loss_should_handle_per_list_weights(self):
    scores = [[1., 3., 2.], [1., 2., 3.], [1., 2., 3.]]
    labels = [[1., 2., 1.], [0., 0., 2.], [0., 0., 0.]]
    list_weights = [[2.], [1.], [1.]]
    reduction = tf.compat.v1.losses.Reduction.SUM_BY_NONZERO_WEIGHTS
    probs = [_softmax(s) for s in scores]

    loss_fn = SoftmaxLoss(name=None)
    result = loss_fn.compute(labels, scores, list_weights, reduction).numpy()
    expect = -(math.log(probs[0][0]) * 1. * 2. + math.log(probs[0][1]) * 2. * 2. +
          math.log(probs[0][2]) * 1. * 2. + math.log(probs[1][2]) * 2. * 1.) /2.
    print("result:",result)
    print("expect:",expect)

  def test_softmax_compute_per_list(self):
    scores = [[1., 3., 2.], [1., 2., 3.]]
    labels = [[0., 0., 1.], [0., 0., 2.]]
    per_item_weights = [[2., 3., 4.], [1., 1., 1.]]

    loss_fn = SoftmaxLoss(name=None)
    losses, weights = loss_fn.compute_per_list(labels, scores, per_item_weights)
    print("result:",losses)
    print("expected:",[1.407606, 0.407606])

  def test_softmax_loss_should_handle_mask(self):
    scores = [[1., 2., 3.]]
    labels = [[0., 1., 1.]]
    mask = [[True, False, True]]
    reduction = tf.compat.v1.losses.Reduction.SUM_BY_NONZERO_WEIGHTS

    loss_fn = SoftmaxLoss(name=None)
    result = loss_fn.compute(labels, scores, None, reduction, mask).numpy()
    expect=-(math.log(_softmax([1, 3])[1]))
    # self.assertAlmostEqual(result, -(math.log(_softmax([1, 3])[1])), places=5)
    print("result:",result)
    print("expect:",expect)

slt= SoftmaxLossTest()
slt.test_softmax_loss()
slt.test_softmax_loss_should_handle_mask()
slt.test_softmax_compute_per_list()
slt.test_softmax_loss_should_handle_per_list_weights()
slt.test_softmax_loss_should_handle_per_example_weights()

3.4.2、运行结果

#正常运行
result: 1.111409
expect: 1.1114089466665704
#带mask策略
result: 0.12692805
expect: 0.12692801104297252
#list维度loss
result: tf.Tensor([1.407606   0.40760595], shape=(2,), dtype=float32)
expected: [1.407606, 0.407606]
#样本维度加权
result: 5.03803
expect: 5.038029822221901
#list维度加权
result: 4.5380297
expect: 4.538029822221901

4、参考文献

https://github.com/frostjsy/ranking/blob/master/tensorflow_ranking/python/losses.py

几种listwise的loss实现_listwise loss_一条水里的鱼的博客-CSDN博客

PolyLoss:一种将分类损失函数加入泰勒展开式的损失函数 - 知乎

https://zhuanlan.zhihu.com/p/534094714

论文笔记: ICLR 2022 | POLYLOSS: A POLYNOMIAL EXPANSION PERSPECTIVE OF CLASSIFICATION LOSS FUNCTIONS - 知乎

https://arxiv.org/pdf/2001.01828.pdf

https://dl.acm.org/doi/pdf/10.1145/3341981.3344221

推荐系统[四]:精排-详解排序算法LTR (Learning to Rank)_ poitwise, pairwise, listwise相关评价指标,超详细知识指南。 - 知乎

排序学习调研 | 蘑菇先生学习记   (有些loss解释比较详细)

工作台 - Heywhale.com  (推荐排序实验较全面的阐述)

【推荐】pairwise、pointwise 、 listwise算法是什么?怎么理解?主要区别是什么?-CSDN博客  (3种loss的损失列的比较好)

Circle Loss: 一个基于对优化的统一视角-CVPR2020 - 知乎

排序学习-4.ApproxNDCG与NeuralNDCG - 知乎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/212297.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Sailfish OS 移动操作系统

Jolla 是一家曾经致力于开发智能手机和平板电脑的公司&#xff0c;但是这些产品并没有取得成功。后来 Jolla 将重心转向了基于 Linux 的 Sailfish OS&#xff08;旗鱼&#xff09;&#xff0c;并将其应用于现有设备上。Sailfish OS 是由 Jolla 在 MeeGo 基础上开发的移动操作系…

开源播放器GSYVideoPlayer + ViewPager2 源码解析

开源播放器GSYVideoPlayer ViewPager2 源码解析 前言一、GSYVideoPlayer&#x1f525;&#x1f525;&#x1f525;是什么&#xff1f;二、源码解析1.ViewPager2Activity 总结 前言 本文介绍GSYVideoPlayer源码中关于ViewPager2 GSYVideoPlayer 实现的滑动播放列表的实现原理。…

【PTA题目】7-18 6翻了 分数 15

7-18 6翻了 分数 15 全屏浏览题目 切换布局 作者 陈越 单位 浙江大学 “666”是一种网络用语&#xff0c;大概是表示某人很厉害、我们很佩服的意思。最近又衍生出另一个数字“9”&#xff0c;意思是“6翻了”&#xff0c;实在太厉害的意思。如果你以为这就是厉害的最高境界&…

合成相机模型【图形学】

相机在计算机图形学中有两个方面的考虑&#xff1a;相机的位置和相机的形状。 要了解后者&#xff0c;我们需要了解相机的工作原理。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - R…

聊聊测试for Jeffky

什么是测试 测试是一个系统性的过程&#xff0c;它涉及到在已开发的软件中执行程序、应用工具和技术来评估其质量、功能和性能。这个过程的目的是发现并纠正程序中的错误&#xff0c;提高软件的可靠性和稳定性&#xff0c;以满足用户的需求。 测试的分类 什么是自动化测试 自动…

MySQL 教程 1.5

MySQL 创建数据表 创建 MySQL 数据表需要以下信息&#xff1a; 表名表字段名定义每个表字段的数据类型 语法 以下为创建 MySQL 数据表的 SQL 通用语法&#xff1a; CREATE TABLE table_name (column1 datatype,column2 datatype,... ); table_name 是你要创建的表的名称。…

多表查询与子查询

问题的引出&#xff1a; 这里有一个留言板&#xff0c;其中一条评论包含了商品名称good&#xff08;商品表&#xff09;&#xff0c;留言content(留言表)。 那么请问如将这个评论从数据库查询出来&#xff1f;这就涉及到了多表查询。 多表查询是指基于两个和两个以上的表查询.…

idea通过remote远程调试云服务器

引用了第三方的包&#xff0c;调试是看不到运行流程&#xff0c;于是想到了idea的remote方法 -agentlib:jdwptransportdt_socket,servery,suspendn,address9002 写一个.sh文件并启动 nohup java -jar -agentlib:jdwptransportdt_socket,servery,suspendn,address9002 ./demo.j…

论文解读--Robust lane detection and tracking with Ransac and Kalman filter

使用随机采样一致性和卡尔曼滤波的鲁棒的车道线跟踪 摘要 在之前的一篇论文中&#xff0c;我们描述了一种使用霍夫变换和迭代匹配滤波器的简单的车道检测方法[1]。本文扩展了这项工作&#xff0c;通过结合逆透视映射来创建道路的鸟瞰视图&#xff0c;应用随机样本共识来帮助消…

基于算能的国产AI边缘计算盒子8核心A53丨17.6Tops算力

边缘计算盒子 8核心A53丨17.6Tops算力 ● 可提供17.6TOPS&#xff08;INT8&#xff09;的峰值计算能力、2.2TFLOPS&#xff08;FP32&#xff09;的高精度算力&#xff0c;单芯片最高支持32路H.264 & H.265的实时解码能力。 ● 适配Caffe/TensorFlow/MxNet/PyTorch/ ONNX/…

倒计时 1 天,2023 IoTDB 用户大会期待与您相见!

终于&#xff01;就在明天&#xff0c;2023 IoTDB 用户大会即将在北京与大家见面&#xff01; 这场筹备已久的盛会&#xff0c;汇集了超 20 位大咖嘉宾带来的精彩议题&#xff0c;届时来自美国国家工程院、清华大学软件学院的产业大拿&#xff0c;与能源电力、钢铁冶炼、城轨运…

康托展开(Cantor Expansion)

【康托展开简介】康托展开&#xff08;Cantor Expansion&#xff09;是一种特殊的哈希函数&#xff0c;是一个相对快速的判重方法&#xff0c;其时间复杂度为O(n^2)&#xff0c;其中 n 是集合中元素的个数。康托展开能够判重&#xff0c;依据的是一个集合各元素产生的全部排列中…

翻译: GPT4等大型语言模型的原理解析和未来预测慢思考和模型自我迭代和LLM安全

YouTube: Intro to Large Language Models - YouTube 1. Large Language Model LLM 大家好&#xff0c;最近我做了一个关于大型语言模型的 30 分钟演讲&#xff0c;有点像介绍性演讲&#xff0c;不幸的是&#xff0c;那个演讲没有被录制下来&#xff0c;但很多人在演讲结束后…

企业计算机服务器locked1勒索病毒数据恢复,locked1勒索病毒解密流程

随着计算机技术的不断发展&#xff0c;越来越多的企业走向数字化办公时代&#xff0c;计算机技术为企业的生产运营提供了有利条件&#xff0c;但也为企业带来了网络安全威胁。在本月&#xff0c;云天数据恢复中心陆续接到很多企业的求助&#xff0c;企业的速达办公软件遭到了lo…

Linux周期任务

我自己博客网站里的文章 Linux周期任务&#xff1a;at和crontab 每个人或多或少都有一些约会或者是工作&#xff0c;有的工作是长期周期性的&#xff0c; 例如&#xff1a; 每个月一次的工作报告每周一次的午餐会报每天需要的打卡…… 有的工作则是一次性临时的&#xff0…

面试数据库八股文十问十答第二期

面试数据库八股文十问十答第二期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1.MySQL的主从复制 MySQL的主从复制是什么&#xff1f;MySQL主从复制是一种常见的…

Kubernetes1.27容器化部署Prometheus

Kubernetes1.27容器化部署Prometheus GitHub链接根据自己的k8s版本选择对应的版本修改镜像地址部署命令对Etcd集群进行监控&#xff08;云原生监控&#xff09;创建Etcd Service创建Etcd证书的Secret创建Etcd ServiceMonitorgrafana导入模板成功截图 对MySQL进行监控&#xff0…

C语言-结构体

---------------------------- ------------------ 岁月漫长心怀热爱&#xff0c;携手共赴星辰大海 --------今天来到我们自定义类型 -----结构体的讲解 目录 结构体的类型声明和初始化 结构体的类型声明 结构体成员的直接访问 结构体成员的间接访问 嵌套结构体进行访问 使用…

一文通关物理机Ubuntu22.04融合部署OpenStack

前言 因为博主笔记本是amd的&#xff0c;就最近搞了个小主机&#xff0c;就想装个云平台玩玩&#xff0c;搞了三四天才正儿八经弄完&#xff0c;摸了一大堆错误出来&#xff0c;在文章前面我会将这些需要注意的点列举出来。 环境 物理环境&#xff1a; i5 12450H 32G内存 无线…

深度学习——第1章 深度学习的概念及神经网络的工作原理

1.1 序言——探索智能机器 千百年来&#xff0c;人类试图了解智能的机制&#xff0c;并将它复制到思维机器上。 人类从不满足于让机械或电子设备帮助做一些简单的任务&#xff0c;例如使用滑轮吊起沉重的岩石&#xff0c;使用计算器做算术。 人类希望计算机能够自动化执行更…