You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将TensorFlow的ApproxNDCGLoss转换为PyTorch版本?

将TensorFlow的ApproxNDCGLoss转换为PyTorch实现

核心原理回顾

ApproxNDCGLoss是归一化折损累积增益(NDCG)的可微近似,解决了传统NDCG因排序操作不可微无法用于端到端训练的问题。它通过成对比较样本的标签与预测分数,用可微函数(如sigmoid)近似“正确排序”的指示函数,从而构建可反向传播的损失。

PyTorch实现代码

import torch
import torch.nn as nn

class ApproxNDCGLoss(nn.Module):
    def __init__(self, temperature=1.0, use_rank_discount=True):
        super().__init__()
        self.temperature = temperature
        self.use_rank_discount = use_rank_discount

    def _calc_approx_dcg(self, scores, labels):
        # 计算两两样本的标签差与分数差
        label_diff = labels.unsqueeze(1) - labels.unsqueeze(0)
        score_diff = scores.unsqueeze(1) - scores.unsqueeze(0)
        
        # 仅保留标签i > 标签j的有效样本对
        valid_mask = (label_diff > 0).float()
        
        # 用sigmoid近似"score_i > score_j"的指示函数
        approx_correct = torch.sigmoid(score_diff / self.temperature)
        
        # 计算秩折扣因子(可选)
        if self.use_rank_discount:
            # 用softmax近似排序后的秩位置
            sorted_scores, sort_indices = scores.sort(descending=True)
            rank_weights = torch.exp(sorted_scores / self.temperature)
            cumulative_ranks = torch.cumsum(rank_weights, dim=0)
            sample_ranks = cumulative_ranks.gather(0, sort_indices.argsort())
            discount = 1.0 / torch.log2(sample_ranks + 2.0)
            discount = discount.unsqueeze(1)
        else:
            discount = 1.0
        
        # 累加得到近似DCG
        dcg = torch.sum(valid_mask * approx_correct * discount, dim=1)
        return dcg.mean()

    def _calc_idcg(self, labels):
        # 计算理想DCG:标签降序排列后的标准DCG
        sorted_labels, _ = labels.sort(descending=True)
        rank = torch.arange(1, sorted_labels.size(0)+1, device=labels.device)
        discount = 1.0 / torch.log2(rank + 1.0)
        idcg = torch.sum(sorted_labels * discount)
        return idcg.clamp(min=1e-10)  # 避免除以0

    def forward(self, scores, labels):
        approx_dcg = self._calc_approx_dcg(scores, labels)
        idcg = self._calc_idcg(labels)
        approx_ndcg = approx_dcg / idcg
        # 损失取1 - 近似NDCG,通过最小化该值实现最大化NDCG
        return 1.0 - approx_ndcg

关键细节说明

  • 温度参数:控制sigmoid函数的陡峭程度,值越小越接近阶跃函数(近似真实NDCG),但可能梯度不稳定;值越大函数越平滑,梯度更稳定但近似精度略有下降。
  • 秩折扣开关:开启后完全贴合标准NDCG的秩权重逻辑,关闭则简化为仅关注排序顺序的损失。
  • 成对比较过滤:只处理标签更重要的样本应排在前面的情况,减少无效计算。
  • IDCG归一化:确保损失值落在0-1区间,便于训练过程中的监控。

使用示例

# 初始化损失函数
loss_fn = ApproxNDCGLoss(temperature=0.5)

# 模拟输入:5个样本的预测分数与真实相关性标签
scores = torch.tensor([0.2, 0.8, 0.5, 0.9, 0.3], dtype=torch.float32)
labels = torch.tensor([0, 2, 1, 2, 0], dtype=torch.float32)

# 计算损失
loss = loss_fn(scores, labels)
print(f"ApproxNDCG Loss: {loss.item()}")

内容的提问来源于stack exchange,提问作者Celso França

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 23:07:28