我有一个多标签问题,我正在尝试将TensorFlow中的排名损失实现为自定义损失。 (https://arxiv.org/pdf/1312.4894.pdf)
我制作了一个带有最后一个Sigmoid激活层的简单CNN,以使每个类具有独立的分布。
数学公式将标签分为正负两个标签。
我的问题是,实现它的正确方法是什么?
def ranking_loss(y_true, y_pred):
pos = tf.where(tf.equal(y_true, 1), y_pred, tf.zeros_like(y_pred))
neg = tf.where(tf.equal(y_true, 0), y_pred, tf.zeros_like(y_pred))
loss = tf.maximum(1.0 - tf.math.reduce_sum(pos) + tf.math.reduce_sum(neg), 0.0)
return tf.math.reduce_sum(loss)
结果是,对于每个样本,来自正类别和负类别的激活分数将独立求和。
tr = [1, 0, 0, 1]
pr = [0, 0.6, 0.55, 0.9]
t = tf.constant([tr])
p = tf.constant([pr])
print(ranking_loss(t, p))
tf.Tensor([[0. 0. 0. 0.9]], shape=(1, 4), dtype=float32) #Pos
tf.Tensor([[0. 0.6 0.55 0. ]], shape=(1, 4), dtype=float32) #Neg
tf.Tensor(1.2500001, shape=(), dtype=float32) #loss
CNN的准确度,召回率和F1性能确实很差。
改用标准的二进制交叉熵损失会产生良好的性能,这让我认为我的实现存在问题。
答案 0 :(得分:1)
我认为,根据公式,总和的扩展是错误的,并且总和tf.math.reduce_sum(pos)
和tf.math.reduce_sum(neg)
不能推入tf.maximum
中。如我所见,您的示例公式将扩展为:
max(0,1-0 + 0.6)+ max(0,1-0 + 0.55)+ max(0,1-0.9 + 0.6)+ max(0,1-0.9 + 0.55)= 4.5 >
您在注释部分中提供的第二种实现对我来说似乎很明智,并且产生了我期望的结果。但是,让我提供一种替代方法:
def ranking_loss(y_true, y_pred):
y_true_ = tf.cast(y_true, tf.float32)
partial_losses = tf.maximum(0.0, 1 - y_pred[:, None, :] + y_pred[:, :, None])
loss = partial_losses * y_true_[:, None, :] * (1 - y_true_[:, :, None])
return tf.reduce_sum(loss)
此实现可能更快。