使用标准化二元交叉熵损失函数的模型无法收敛。

3
我试图实现归一化的二进制交叉熵用于分类任务,遵循该论文:Normalized Loss Functions for Deep Learning with Noisy Labels。 其数学公式如下所示: enter image description here 以下是我的实现代码:
import tensorflow as tf
from keras.utils import losses_utils

class NormalizedBinaryCrossentropy(tf.keras.losses.Loss):
    def __init__(
            self,
            from_logits=False,
            label_smoothing=0.0,
            axis=-1,
            reduction=tf.keras.losses.Reduction.NONE,
            name="normalized_binary_crossentropy",
            **kwargs
    ):
        super().__init__(
            reduction=reduction, name=name
        )
        self.from_logits = from_logits
        self._epsilon = tf.keras.backend.epsilon()

    def call(self, target, logits):
        if tf.is_tensor(logits) and tf.is_tensor(target):
            logits, target = losses_utils.squeeze_or_expand_dimensions(
                logits, target
            )
        logits = tf.convert_to_tensor(logits)
        target = tf.cast(target, logits.dtype)

        if self.from_logits:
            logits = tf.math.sigmoid(logits)

        logits = tf.clip_by_value(logits, self._epsilon, 1.0 - self._epsilon)

        numer = target * tf.math.log(logits) + (1 - target) * tf.math.log(1 - logits)
        denom = - (tf.math.log(logits) + tf.math.log(1 - logits))
        return - numer / denom

    def get_config(self):
        config = super().get_config()
        config.update({"from_logits": self._from_logits})
        return config

我正在使用这个损失函数来训练一个二元分类器(点击率预测器),但是模型的损失没有下降,ROC-AUC保持在大约0.49-0.5。为了验证分子的实现,我尝试去掉分母并进行训练,效果很好。

# Example Usage

labels = np.array([[0], [1], [0], [0], [0]]).astype(np.int64)

logits = np.array([[-1.024], [2.506], [1.43], [0.004], [-2.0]]).astype(np.float64)

tf_nce = NormalizedBinaryCrossentropy(
    reduction=tf.keras.losses.Reduction.NONE,
    from_logits=True
)
tf_nce(labels, logits)

#<tf.Tensor: shape=(5, 1), dtype=float64, numpy=
# array([[0.18737159],
#  [0.02945536],
#  [0.88459308],
#  [0.50144269],
#  [0.05631594]])>

我手动检查了一些极端情况,发现这个损失并没有影响到NaN或0。

有人能帮我调试为什么模型不能收敛到这个损失吗?我的损失函数或实现有什么问题吗?

编辑1:模型架构是一个带有6个任务的多门混合专家模型。所有6个任务都是二元分类,从所有任务中添加损失以获得最终损失。

2个回答

1

在上述论文中提到的一件事是,损失函数的范数应该在[0 ~ 1]之间,但由于您的损失函数违反了这个条件,另一个原因是您正在除以错误的分母,您必须将其除以您的logits的交叉熵,为此请使用您的logitsBinaryCrossEntropy()。所以,这些可能是导致您的函数不下降的原因......我对您的代码进行了一些更改,以满足这个范数属性...

import tensorflow as tf
from keras.utils import losses_utils

class NormalizedBinaryCrossentropy(tf.keras.losses.Loss):
    def __init__(
            self,
            from_logits=False,
            label_smoothing=0.0,
            axis=-1,
            reduction=tf.keras.losses.Reduction.NONE,
            name="normalized_binary_crossentropy",
            **kwargs
    ):
        super().__init__(
            reduction=reduction, name=name
        )
        self.from_logits = from_logits
        self._epsilon = tf.keras.backend.epsilon()

    def call(self, target, logits):
        if tf.is_tensor(logits) and tf.is_tensor(target):
            logits, target = losses_utils.squeeze_or_expand_dimensions(
                logits, target
            )
        logits = tf.convert_to_tensor(logits)
        target = tf.cast(target, logits.dtype)
        
        logits = tf.clip_by_value(logits, self._epsilon, 1.0 - self._epsilon)
        
        if self.from_logits:
            numer = tf.keras.losses.binary_crossentropy(target, logits,from_logits=True)[:,tf.newaxis]
            denom = -( tf.math.log(logits) + tf.math.log(1 - logits))
            return  numer * denom / tf.reduce_sum(denom)
        else:
            logits = tf.nn.log_softmax(logits)
            num = - tf.math.reduce_sum(tf.multiply(target, logits), axis=1)
            denom = -tf.math.reduce_sum(logits, axis=1)
            return num / denom

    def get_config(self):
        config = super().get_config()
        config.update({"from_logits": self._from_logits})
        return config

我已经更新了解决方案,如果您的logits是one-hot,则设置from_logit=False来计算BCE,否则设置为True


谢谢您的回复!我尝试了这个损失函数,但是损失似乎在增加,roc-auc 大约为 ~.40 - Jatin Mandav
请先阅读介绍... - Mohammad Ahmed
我建议您采用平均损失,因为我在评论中提到了它。 - Mohammad Ahmed
仍然存在相同的问题。我使用的模型是带有6个二进制分类任务的多门混合专家模型(也已添加到问题中)。每个任务的所有损失都被加在一起以得到最终损失。 - Jatin Mandav
我们必须将相应的logits乘以它们对应的目标,然后通过其长度进行归一化。这是实现的标准方法。更新了解决方案。 - Mohammad Ahmed
显示剩余6条评论

1
我会尽量避免log-Sigmoid稳定性问题,并将上述模型实现为一个使用Softmax Binary Cross Entropy的2类问题。 NormalizedCrossEntropy的定义如下:
class NormalizedCrossEntropy(keras.layers.Layer):
    def __init__(self, num_classes):
        super(NormalizedCrossEntropy, self).__init__()
        self.num_classes = num_classes

    def call(self, pred, labels):
        pred = tf.nn.log_softmax(pred, axis=1,)
        label_one_hot = tf.one_hot(labels, self.num_classes)
        numer = -1 * tf.reduce_sum(label_one_hot * pred, axis=1) 
        denom = -1* tf.reduce_sum(pred, axis=1)
        nce = numer/ denom
        return nce

示例用法:

NormalizedCrossEntropy(num_classes=2)(np.array([[-1.024, 0.5], [0.1, 2.506], [1, .0], [0., 1.], [-0.89, -2.0]]), np.array([0, 1, 0, 0, 0]) )
#array([0.89725673, 0.03348167, 0.19259584, 0.80740416, 0.16958274]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接