TensorFlow:交叉熵损失函数

损失loss示例

loss_mask = tf.greater(click_label, -1000)
loss_mask = tf.logical_and(loss_mask, tf.logical_not(is_othsene_mask))

label = tf.cast(tf.greater(label, 0.5), label.dtype)
loss = tf.reshape(tf.nn.sigmoid_cross_entropy_with_logits(labels=label, logits=logit), [-1, 1])
loss = (1.0 - fake_impr_mask) * loss + fake_impr_mask * loss * fake_impr_coef
loss = tf.reduce_sum(tf.boolean_mask(loss, loss_mask))

Tensorflow:tensor数据类型转换、计算和变换-CSDN博客​​​​​​​

基础

损失函数

[机器学习中的损失函数]

示例说明:计算multilabel时的BinaryCrossentropy

tf.keras.losses.BinaryCrossentropy原码:

@tf_export('keras.backend.binary_crossentropy')
def binary_crossentropy(target, output, from_logits=False):
  """Binary crossentropy between an output tensor and a target tensor.

  Arguments:
      target: A tensor with the same shape as `output`.
      output: A tensor.
      from_logits: Whether `output` is expected to be a logits tensor.
          By default, we consider that `output`
          encodes a probability distribution.

  Returns:
      A tensor.
  """
  # Note: nn.sigmoid_cross_entropy_with_logits
  # expects logits, Keras expects probabilities.
  if not from_logits:
    # transform back to logits
    epsilon_ = _to_tensor(epsilon(), output.dtype.base_dtype)
    output = clip_ops.clip_by_value(output, epsilon_, 1 - epsilon_)
    output = math_ops.log(output / (1 - output))
  return nn.sigmoid_cross_entropy_with_logits(labels=target, logits=output)

tf.nn.sigmoid_cross_entropy_with_logits的原码逻辑:

For brevity, let x = logits, z = labels. The logistic loss is
  z * -log(sigmoid(x)) + (1 - z) * -log(1 - sigmoid(x))

normalized cross entropy

def normalized_cross_entropy(self, predict_tensor, target_tensor, real_p=0.187):
    per_entry_cross_ent = tf.nn.sigmoid_cross_entropy_with_logits(logits=predict_tensor, labels=target_tensor)
    hmax_entry = -  real_p * tf.log(real_p) - (1 - real_p) * tf.log(1.0 - real_p)
    normalize_entry_cross_ent = per_entry_cross_ent / hmax_entry
    return normalize_entry_cross_ent

[损失函数loss_loss函数_-柚子皮-的博客-CSDN博客]

示例

1 如果输入是未激活的logits = tf.layers.dense(output, units=num_labels, activation=None)

loss = tf.keras.losses.binary_crossentropy(labels=labels, logits=logits, from_logits=True)

或者 loss = tf.nn.sigmoid_cross_entropy_with_logits(labels=labels, logits=logits)。

即Use an activation function and use the raw outputs (logits) for the sigmoid_crossentropy_with_logits function.

2 如果输入是激活后的outputs = tf.layers.dense(output, units=num_labels, activation='sigmoid')

则loss = tf.keras.losses.binary_crossentropy(labels, outputs)。

其内部逻辑为先将outputs通过反sigmoid操作转成logits,再使用loss = tf.nn.sigmoid_cross_entropy_with_logits(labels, logits)。

所以有时为了效率,如果能直接用logits还是用1来实现Crossentropy计算吧。

-柚子皮-

TensorFlow交叉熵函数

        TensorFlow针对分类问题,实现了四个交叉熵函数,分别是tf.nn.sigmoid_cross_entropy_with_logitstf.nn.softmax_cross_entropy_with_logitstf.nn.sparse_softmax_cross_entropy_with_logitstf.nn.weighted_cross_entropy_with_logits

sigmoid_cross_entropy_with_logits详解

        我们先看sigmoid_cross_entropy_with_logits[tf.nn.sigmoid_cross_entropy_with_logits],它的实现和交叉熵算法定义是一样的。

tf.nn.sigmoid_cross_entropy_with_logits(
    _sentinel=None,
    labels=None,
    logits=None,
    name=None
)

        函数的输入是logits和labels,logits就是神经网络模型中的 W * X矩阵,注意不需要经过sigmoid,而labels的shape和logits相同,就是正确的label值,例如这个模型一次要判断100张图是否包含10种动物,这两个输入的shape都是[100, 10]。注释中还提到这10个分类之间是独立的、不要求是互斥,这种问题我们成为多目标multi-label,例如判断图片中是否包含10种动物,label值可以包含多个1或0个1。

        还有一种问题是多分类问题multi-class,例如我们对年龄特征分为5段,只允许5个值有且只有1个值为1,这种问题就不可以直接用这个函数。

        sigmoid_cross_entropy_with_logits的代码实现逻辑:For brevity, let x = logitsz = labels. The logistic loss is

  z * -log(sigmoid(x)) + (1 - z) * -log(1 - sigmoid(x))
= z * -log(1 / (1 + exp(-x))) + (1 - z) * -log(exp(-x) / (1 + exp(-x)))
= z * log(1 + exp(-x)) + (1 - z) * (-log(exp(-x)) + log(1 + exp(-x)))
= z * log(1 + exp(-x)) + (1 - z) * (x + log(1 + exp(-x))
= (1 - z) * x + log(1 + exp(-x))
= x - x * z + log(1 + exp(-x))

​        可以看到这就是标准的Cross Entropy算法实现,对W * X得到的值进行sigmoid激活,保证取值在0到1之间,然后放在交叉熵的函数中计算Loss。对于二分类问题这样做没问题,因为对于多标签分类问题,目标是将每个输出标签视作一个独立伯努利分布,而且我们需要独立地惩罚每个输出节点。

        但对于前面提到的多分类就不能使用,例如年轻取值范围在0~4,目标值也在0~4,这里如果经过sigmoid后预测值就限制在0到1之间,而且公式中的1 - z就会出现负数,仔细想一下0到4之间还不存在线性关系,如果直接把label值带入计算肯定会有非常大的误差。因此对于多分类问题是不能直接代入的,那其实我们可以灵活变通,把5个年龄段的预测用onehot encoding变成5维的label,训练时当做5个不同的目标来训练即可,但不保证只有一个为1。对于这类问题TensorFlow又提供了基于Softmax的交叉熵函数。

softmax_cross_entropy_with_logits详解

        将弃用,取代为tf.nn.softmax_cross_entropy_with_logits_v2

tf.nn.softmax_cross_entropy_with_logits_v2(
    labels,
    logits,
    axis=None,
    name=None,
    dim=None
)

        Softmax本身的算法很简单,就是把所有值用e的n次方计算出来,求和后算每个值占的比率,保证总和为1,一般我们可以认为Softmax出来的就是confidence也就是概率,算法实现如下。

  This function performs the equivalent of
      softmax = tf.exp(logits) / tf.reduce_sum(tf.exp(logits), axis)

        ​softmax_cross_entropy_with_logits和sigmoid_cross_entropy_with_logits很不一样,输入是类似的logits和lables的shape一样,但这里要求分类的结果是互斥的,保证只有一个字段有值,例如CIFAR-10中图片只能分一类而不像前面判断是否包含多类动物。想一下问什么会有这样的限制?在函数头的注释中我们看到,这个函数传入的logits是unscaled的,既不做sigmoid也不做softmax,因为函数实现会在内部更高效得使用softmax,对于任意的输入经过softmax都会变成和为1的概率预测值,这个值就可以代入变形的Cross Entroy算法- y * ln(a) - (1 - y) * ln(1 - a)算法中,得到有意义的Loss值了。

        如果是多目标问题,经过softmax就不会得到多个和为1的概率,而且label有多个1也无法计算交叉熵,因此这个函数只适合单目标的二分类或者多分类问题。并且它只计算了某个类别标签为1时的loss及梯度,而忽略了为0时的loss,而每个输出又相互独立,不像softmax函数那样有归一化的限制。所以multi-label是一定不能使用CE作为loss函数的

        ​再补充一点,对于多分类问题,例如我们的年龄分为5类,并且人工编码为0、1、2、3、4,因为输出值是5维的特征,因此我们需要人工做onehot encoding分别编码为00001、00010、00100、01000、10000,才可以作为这个函数的输入。理论上我们不做onehot encoding也可以,做成和为1的概率分布也可以,但需要保证是和为1,和不为1的实际含义不明确,TensorFlow的C++代码实现计划检查这些参数,可以提前提醒用户避免误用。

sparse_softmax_cross_entropy_with_logits详解

        sparse_softmax_cross_entropy_with_logits是softmax_cross_entropy_with_logits的易用版本,除了输入参数不同,作用和算法实现都是一样的。

        前面提到softmax_cross_entropy_with_logits的输入必须是类似onehot encoding的多维特征,但CIFAR-10、ImageNet和大部分分类场景都只有一个分类目标(单分类),label值都是从0编码的整数,每次转成onehot encoding比较麻烦。 

        sparse_softmax_cross_entropy_with_logits,它的第一个参数logits和前面一样,shape是[batch_size, num_classes];而第二个参数labels的shape改为[batch_size],值必须是从0开始编码的int32或int64,且值范围是[0, num_class)。最后在内部高效实现类似的onehot encoding,这只是简化用户的输入而已,当然如果用户已经做了onehot encoding那可以直接使用不带“sparse”的softmax_cross_entropy_with_logits函数。

-柚子皮-

keras中两种交叉熵损失函数

categorical cross entropy [tf.keras.losses.CategoricalCrossentropy]和 binary cross entropy[tf.keras.losses.BinaryCrossentropy],以下简称CE和BCE. 关于这两个函数的忠告就是:"CE用于多分类, BCE适用于二分类, 千万别用混了."

CE:

CE(x)=-\sum_{i=1}^C y_i \log f_i(x) 。。。。。。(1)

其中, x表示输入样本, C为待分类的类别总数, 这里我们以手写数字识别任务(MNIST-based)为例, 其输入出的类别数为10, 对应的C=10.  y_i 为第i个类别对应的真实标签,  f_i(x) 为对应的模型输出值.

BCE:

BCE(x)_i = -[y_i \log f_i(x) + (1-y_i) \log (1-f_i(x))] .。。。。。。(2)

其中  i \in [1, C] , 即每个类别输出节点都对应一个BCE值.

看到这里, 大家会发现两者的shape并不相同,对于单个样本而言,CE是一个数值,而BCE是一个向量,其维度与输出类别的个数相同,即为C。但在Keras中,最终使用的是均值,即:

BCE(x) = \frac {\sum_{i=1}^C BCE(x)_i}{C}

那如果是batch的情况呢?Keras中的做法是对batch中所有样本的loss求均值:

CE(x)_{final}=\frac {\sum_{b=1}^{N}CE(x^{(b)})}{N}

BCE(x)_{final}=\frac {\sum_{b=1}^{N}BCE(x^{(b)})}{N}

在tensorflow中则只提供原始的BCE(sigmoid_cross_entropy_with_logits)和CE(softmax_cross_entropy_with_logits_v2),这也给开发人员提供了更大的灵活性。

另外,再补充一点,keras使用tensorflow作为backend时,默认情况下CE的实现调用的是自己内部实现的计算方法,而没有像之前想象的那样调用的tensorflow对应的函数。

[keras中两种交叉熵损失函数的探讨]

from:-柚子皮-

ref: [TensorFlow四种Cross Entropy算法实现和应用]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值