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

面向多类别多标签排序任务的TensorFlow损失与激活函数选型

类别排序任务的模型适配方案

任务本质

这是典型的**排序学习(Learning to Rank)**任务:核心目标是让模型输出的类别分数符合给定的相对排名,而非精确匹配标签数值或完成单一分类。你的标签数值仅作为排名的标识,实际只需要区分「类别3 > 类别5 > 类别1,其余类别无排序意义」的关系。

现有方案问题分析

  • 回归方案(Linear+MSE):MSE损失会强迫模型拟合标签里的无意义数值(比如0.75、0.875),而非学习排名关系。模型为了最小化整体误差,会倾向输出所有类别的均值(接近0),完全无法捕捉排序逻辑。
  • 多分类方案(Softmax+交叉熵):交叉熵本质是单标签分类任务,只会最大化最高排名类别的概率,完全忽略其他类的相对顺序,因此只能预测第1名,其余排名被压制。
  • 余弦相似度方案:余弦相似度仅衡量向量整体相似性,对中间排名的细微分数差异不敏感,因此只能区分极端的第1名和最后一名,无法学习中间类别的顺序。

适配的激活函数

直接使用Linear(线性)激活即可。排序任务需要模型输出连续的分数值,用来体现类别的相对顺序,不需要做概率归一化(Softmax会压缩分数差距),也不需要限制输出范围。

损失函数选择

1. 自定义成对排序损失(Pairwise Loss)

核心逻辑是:让排名靠前的类别,模型输出的分数必须高于排名靠后的类别。以下两种实现方式可选:

Hinge版(硬排序,适用于明确的顺序要求)
import tensorflow as tf

def pairwise_hinge_loss(y_true, y_pred):
    # 先将原标签转为排名标识:1=第1名,2=第2名,3=第3名,0=无关
    # 示例:原标签[0.0,0.75,0.0,1.0,0.0,0.875,0.0,0.0,0.0,0.0] → 排名标识[0,3,0,1,0,2,0,0,0,0]
    pos_mask = y_true > 0
    # 获取所有需要排序的类别索引
    ranked_indices = tf.where(pos_mask)[:, 0]
    ranked_true = tf.gather(y_true, ranked_indices)
    
    loss = 0.0
    n = tf.size(ranked_indices)
    # 遍历所有需要排序的类别对
    for i in range(n):
        for j in range(n):
            # 如果i的排名比j高(ranked_true[i] < ranked_true[j],因为1是最高)
            if ranked_true[i] < ranked_true[j]:
                # 强制score[i] - score[j] ≥1,否则产生损失
                loss += tf.maximum(0.0, 1.0 - (y_pred[ranked_indices[i]] - y_pred[ranked_indices[j]]))
    # 平均损失
    return loss / tf.cast(n * (n-1)/2, tf.float32) if n>1 else 0.0
Logistic版(软排序,适用于允许微小误差的场景)
import tensorflow as tf

def pairwise_log_loss(y_true, y_pred):
    pos_mask = y_true > 0
    ranked_indices = tf.where(pos_mask)[:, 0]
    ranked_true = tf.gather(y_true, ranked_indices)
    
    loss = 0.0
    n = tf.size(ranked_indices)
    for i in range(n):
        for j in range(n):
            if ranked_true[i] < ranked_true[j]:
                # 用sigmoid函数衡量顺序错误的概率,将其转为损失
                loss += tf.math.log(1 + tf.math.exp(-(y_pred[ranked_indices[i]] - y_pred[ranked_indices[j]])))
    return loss / tf.cast(n * (n-1)/2, tf.float32) if n>1 else 0.0

2. 列表排序损失(Listwise Loss)

直接优化排序任务的核心指标NDCG(归一化折扣累积增益),衡量预测排名与真实排名的契合度:

import tensorflow as tf

def ndcg_loss(y_true, y_pred):
    # 将排名标识转为相关性分数:第1名=3,第2名=2,第3名=1,无关=0
    true_relevance = tf.where(y_true == 1, 3.0,
                              tf.where(y_true == 2, 2.0,
                                       tf.where(y_true == 3, 1.0, 0.0)))
    
    def compute_ndcg(scores, relevance):
        # 按分数降序排序,获取排序后的相关性
        sorted_indices = tf.argsort(scores, direction='DESCENDING')
        sorted_rel = tf.gather(relevance, sorted_indices)
        # 计算DCG
        gain = tf.pow(2.0, sorted_rel) - 1.0
        discount = tf.math.log(tf.range(1, tf.size(sorted_rel)+1, dtype=tf.float32) + 1.0) / tf.math.log(2.0)
        dcg = tf.reduce_sum(gain / discount)
        # 计算理想DCG(按真实相关性降序)
        ideal_indices = tf.argsort(relevance, direction='DESCENDING')
        ideal_rel = tf.gather(relevance, ideal_indices)
        ideal_gain = tf.pow(2.0, ideal_rel) - 1.0
        ideal_dcg = tf.reduce_sum(ideal_gain / discount)
        return dcg / ideal_dcg if ideal_dcg != 0 else 0.0
    
    # 损失为1 - 预测NDCG,让模型最大化NDCG
    pred_ndcg = compute_ndcg(y_pred, true_relevance)
    return 1.0 - pred_ndcg

关键注意事项

  1. 标签预处理:必须先将原标签的数值转为明确的排名标识(比如第1名赋值1,第2名赋值2,以此类推,无关类别赋值0),让模型清晰理解排序目标。
  2. 无关类别处理:在损失计算中仅关注需要排序的类别对,忽略无关类别,避免其干扰模型学习排名关系。

内容的提问来源于stack exchange,提问作者Ajay Chinni

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 17:45:28