def compute_unreduced_loss(self, labels, logits): """See `_RankingLoss`.""" alpha = self._params.get('alpha', 10.0) is_valid = utils.is_label_valid(labels) labels = tf.compat.v1.where(is_valid, labels, tf.zeros_like(labels)) logits = tf.compat.v1.where( is_valid, 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. / utils.approx_ranks(logits, alpha=alpha) 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])
def compute_unreduced_loss(self, labels, logits, weights): """See `_RankingLoss`.""" alpha = self._params.get('alpha') is_label_valid = utils.is_label_valid(labels) labels = tf.where(is_label_valid, labels, tf.zeros_like(labels)) logits = tf.where( is_label_valid, 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) if weights is None: weights = tf.ones_like(label_sum) weights = tf.squeeze(weights) nonzero_mask = tf.greater(tf.reshape(label_sum, [-1]), 0.0) labels = tf.where(nonzero_mask, labels, _EPSILON * tf.ones_like(labels)) weights = tf.where(nonzero_mask, weights, tf.zeros_like(weights)) gains = tf.pow(2., tf.cast(labels, dtype=tf.float32)) - 1. ranks = utils.approx_ranks(logits, alpha=alpha) discounts = 1. / tf.math.log1p(ranks) dcg = tf.reduce_sum(input_tensor=gains * discounts, axis=-1) cost = -dcg * tf.squeeze(utils.inverse_max_dcg(labels)) return cost, weights
def test_approx_ranks(self): logits = [[1., 3., 2., 0.], [4., 2., 1.5, 3.]] target_ranks = [[3., 1., 2., 4.], [1., 3., 4., 2.]] approx_ranks = utils.approx_ranks(logits, 100.) with tf.compat.v1.Session() as sess: approx_ranks = sess.run(approx_ranks) self.assertAllClose(approx_ranks, target_ranks)
def _approx_ndcg_loss( labels, logits, weights=None, reduction=core_losses.Reduction.SUM, name=None, alpha=10.): """Computes ApproxNDCG loss. ApproxNDCG ["A general approximation framework for direct optimization of information retrieval measures" by Qin et al.] is a smooth approximation to NDCG. 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. If None, the weight of a list in the mini-batch is set to the sum of the labels of the items in that list. reduction: One of `tf.losses.Reduction` except `NONE`. Describes how to reduce training loss over batch. name: A string used as the name for this loss. alpha: The exponent in the generalized sigmoid function. Returns: An op for the ApproxNDCG loss. """ with ops.name_scope(name, 'approx_ndcg_loss', (labels, logits, weights)): is_label_valid = utils.is_label_valid(labels) labels = array_ops.where(is_label_valid, labels, array_ops.zeros_like(labels)) logits = array_ops.where( is_label_valid, logits, -1e3 * array_ops.ones_like(logits) + math_ops.reduce_min( logits, axis=-1, keepdims=True)) label_sum = math_ops.reduce_sum(labels, 1, keepdims=True) if weights is None: weights = array_ops.ones_like(label_sum) weights = array_ops.squeeze(weights) nonzero_mask = math_ops.greater(array_ops.reshape(label_sum, [-1]), 0.0) labels, logits, weights = [ array_ops.boolean_mask(x, nonzero_mask) for x in [labels, logits, weights] ] gains = math_ops.pow(2., math_ops.to_float(labels)) - 1. ranks = utils.approx_ranks(logits, alpha=alpha) discounts = 1. / math_ops.log1p(ranks) dcg = math_ops.reduce_sum(gains * discounts, -1) cost = -dcg * array_ops.squeeze(utils.inverse_max_dcg(labels)) return core_losses.compute_weighted_loss( cost, weights=weights, reduction=reduction)
def compute_unreduced_loss(labels, logits): """See `_RankingLoss`.""" alpha = 10.0 is_valid = utils.is_label_valid(labels) labels = tf.compat.v1.where(is_valid, labels, tf.zeros_like(labels)) logits = tf.compat.v1.where( is_valid, 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)) gains = tf.pow(2., tf.cast(labels, dtype=tf.float32)) - 1. ranks = utils.approx_ranks(logits, alpha=alpha) discounts = 1. / tf.math.log1p(ranks) dcg = tf.reduce_sum(input_tensor=gains * discounts, axis=-1, keepdims=True) cost = -dcg * utils.inverse_max_dcg(labels) return cost, tf.reshape(tf.cast(nonzero_mask, dtype=tf.float32), [-1, 1])