几种listwise的loss实现

一、前言

        本文实现的listwise loss目前应用于基于ListwWise的召回模型中,在召回中,一般分为用户侧和item侧,模型最终分别输出user_vector和item_vector,在pointwise模型中,这两个vector的shape都为(batch_size,dim),直接求两者的内积或余弦,然后过sigmoid,最后采用交叉熵获得loss进行反向传播。而在listwise模型中,item_vector的shape变为(batch_size,list_size,dim)其中,list_size 为序的长度。这样,在listwise模型中,user_vector与item_vector求内积后,得到的结果的shape为(batch_size,list_size),而输入的label也是(batch_size,list_size),所以有了输出结果与label,据此可以设计listwise loss。

二、ListWise Loss

1.KL 散度 loss

分别对模型输出结果与label,进行softmax就可以分别得到rank_logits(也就是该item排在当前位置的概率)与lable_logits(也就是真实该item排在当前位置的概率),举例:

为了方便batch_size设为1

lable=[5,4,3,1] (对应:下单,加车,点击与曝光的得分)

label_logits=tf.softmax(label)

pred=[a,b,c,d,e]

rank_logits=tf.softmax(pred)

这样我们得到两个概率,衡量两个概率分布差别,自然会想到KL散度。

kl散度代码实现:

def kl_loss(label_prob,pred_prob):
    """
    :param label_prob: 真实概率分布
    :param pred_prob: 预测概率分布
    :return:
    """
    p_q = label_prob/tf.clip_by_value(pred_prob,1e-8,1)
    kl_loss=tf.reduce_sum(label_prob * tf.log(p_q), axis=-1)
    with tf.Session() as sess:
        kl_loss=sess.run(kl_loss)
    return kl_loss
  • kl散度用作loss的局限性:

1、lk散度是用来衡量两个概率的分布的差别,而真实的推荐场景中,其实只要满足rank_logits 的顺序与label_logits相同,它的loss就应该为0,并不需要强调概率接近。

2、计算kl loss 需要提前设计一个真实的排序得分,比如:下单,加车,点击与曝光分别为5,4,3,1分。然后使预测的rank_logits尽量与这个分布相近。然而这个分布是人为定义的,不是真实的分布。

  • kl散度用作loss的优势:

1、实现简单,计算速度快

2、设计不同的得分,相当于设计不同的样本权重,当需要提高转化率,则可以把下单的分数设置高一点

2.ListMLE loss

 o= [o1, o2, · · · , omn ]表示答案中的正确序列,k=1时,表示o1被正确放在第1位的概率;k=2时,表示o2在除去o1的剩下候选中,被选做第2位的概率;依次类推,最后概率乘起来;因为概率乘可能会导致数值过小,所以在第一个公式中套上log。可不太好理解,举个例子:

label=[2,5,3,1]

logits=[0.7,1.1,2.1,0.5]

先给label 排序 [5,3,2,1],按照label的排序方式,给logits排序则变成[1.1,2.1,0.7,0.5](可以理解为把label与logits看成两列,按label那列由大到小排序)

然后套公式计算:

o1放首位的概率 po1=1.1/(1.1+2.1+0.7+0.5)\

去掉o1,o2放首位的概率po2=2.1/(2.1+0.7+0.5),依次类推

最后loss=-log(po1*po2...*pon)

 MLELoss代码实现:

# 参考:https://zhuanlan.zhihu.com/p/66514492
import tensorflow as tf
def mle_loss(true_scores,rank_scores):
    '''
    :param true_scores: 真实得分(分值不重要,能确定先后顺序就行),shape (bt,list_size)
    :param rank_scores: 预测得分, shape (bt,list_size)
    :return:
    '''
    # batch_size=4
    list_size=3   #list 不一定为真实的,也可以指定任意值(mle 可以理解为该item排在首位的概率的联乘)
    index = tf.argsort(true_scores, direction='DESCENDING')
    # print(index)[1,2,0] [1,0,2]
    #按真实得分对rank_scores排序
    S_predict=tf.batch_gather(rank_scores, index)
    # S_predict=tf.gather(rank_scores, index,axis=-1,batch_dims=0)
    #分子
    initm_up=tf.constant([[1.0]]) #shape 为 1,1
    for i in range(list_size):#3为 list_size
        # index=tf.constant([[i]]*batch_size,dtype=tf.int32) #shape 为 bt,1
        # a=tf.batch_gather(S_predict,index)
        a=tf.slice(S_predict,[0,i],[-1,1]) #shape 为 bt,1
        initm_up=initm_up*a #bt,1

    #分母
    initm_down=tf.constant([[1.0]]) #shape 为 1,1
    for i in range(list_size):
        b=tf.reduce_sum(tf.slice(S_predict, [0, i], [-1, list_size - i]), axis=-1,keep_dims=True)  # bt
        initm_down*=b
    loss=tf.log(tf.divide(initm_up,initm_down))
    mleloss=-tf.reduce_mean(loss)
    return mleloss

3、ListNet Loss

  • ListNet损失,同样先构造答案序列中每个item的得分,如[y1, y2, y3] = [0, 0.5, 1]

第一个公式计算答案中每个item被当成首个item的概率;

第二个公式计算预测结果中每个item被当成首个item的概率;

第三个公式是前面两个概率分布的交叉熵

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值