【问题标题】:Keras custom loss function with different weights per example每个示例具有不同权重的 Keras 自定义损失函数
【发布时间】:2018-09-08 01:58:47
【问题描述】:

我正在尝试在 Keras 中实现一个自定义损失函数,其中每个单独的示例(不是类)具有不同的权重。

确切地说,考虑到通常的 y_true(例如 )和 y_pred(例如 ),我我正在尝试创建权重(例如)并将它们与binary_crossentropy损失函数一起使用。我试过了:

import numpy as np
from keras import backend as K

def my_binary_crossentropy(y_true, y_pred):
    base_factor = 0.9
    num_examples = K.int_shape(y_true)[0]

    out = [ K.pow(base_factor, num_examples - i - 1) for i in range(num_examples) ]
    forgetting_factors = K.stack(out)

    return K.mean(
        forgetting_factors * K.binary_crossentropy(y_true, y_pred),
        axis=-1
    )

在这个简单的例子中效果很好:

y_true = K.variable( np.array([1,1,0]) )
y_pred = K.variable( np.array([1,0.2,0.8]) )
print K.eval(my_binary_crossentropy(y_true, y_pred))

但是,当我将它与 model.compile(loss=my_binary_crossentropy, ...) 一起使用时,我收到以下错误:TypeError: range() integer end argument expected, got NoneType

我已经尝试了一些东西。我用 K_shape 替换了 K.int_shape 现在得到:TypeError: range() integer end argument expected, got Tensor. 我进一步用 K.arange() 替换了 range() 现在得到:TypeError: Tensor objects are not iterable when eager execution is not enabled. To iterate over this tensor use tf.map_fn

谁能帮帮我?我错过了什么?非常感谢!

【问题讨论】:

    标签: python tensorflow keras loss-function


    【解决方案1】:

    K.pow 可以将一系列指数作为参数。因此,您可以先计算指数,作为张量 ([num_examples - 1, num_examples - 2, ..., 0]),然后将此张量输入 K.pow。这里num_examples基本上就是K.shape(y_pred)[0],也是张量。

    def my_binary_crossentropy(y_true, y_pred):
        base_factor = 0.9
        num_examples = K.cast(K.shape(y_pred)[0], K.floatx())
        exponents = num_examples - K.arange(num_examples) - 1
        forgetting_factors = K.pow(base_factor, exponents)
        forgetting_factors = K.expand_dims(forgetting_factors, axis=-1)
        forgetting_factors = K.print_tensor(forgetting_factors)  # only for debugging
    
        loss = K.mean(
            forgetting_factors * K.binary_crossentropy(y_true, y_pred),
            axis=-1
        )
        loss = K.print_tensor(loss)  # only for debugging
        return loss
    

    例如,两个K.print_tensor 语句打印的输出如下:

    model = Sequential()
    model.add(Dense(1, activation='sigmoid', input_shape=(100,)))
    model.compile(loss=my_binary_crossentropy, optimizer='adam')
    
    model.evaluate(np.zeros((3, 100)), np.ones(3), verbose=0)
    [[0.809999943][0.9][1]]
    [0.56144917 0.623832464 0.693147182]
    
    model.evaluate(np.zeros((6, 100)), np.ones(6), verbose=0)
    [[0.590489924][0.656099916][0.728999913]...]
    [0.409296423 0.454773813 0.505304217...]
    

    由于舍入误差,数字不准确。 forgetting_factors(在model.evaluate 之后打印的第一行)确实是 0.9 的幂。您还可以验证返回的损失值是否衰减了 0.9 倍(0.623832464 = 0.693147182 * 0.90.56144917 = 0.693147182 * 0.9 ** 2 等)。

    【讨论】:

    • 非常感谢!你能否解释一下为什么它返回一个列表(在model.evaluate之后打印的第二行)而不是一个数字,即损失?据我了解,最终答案,即损失是此列表中的最后一个数字,这是正确的吗?
    • 打印并返回的loss 变量包含样本损失值。 Keras 损失函数返回样本损失,然后在内部对其进行平均(并乘以样本权重)。
    • 例如,在第一个 model.evaluate() 中,传入包含 3 个样本的批次,因此 loss 变量将是大小为 3 的一维张量。
    • 谢谢。我问的原因是因为我正在尝试做一个简单的测试来验证代码。考虑我原始帖子中的示例,使用原始 binary_crossentropy 会得到 1.0729587。将my_binary_crossentropybase_factor = 1.0 一起使用会得到[1.0729587 1.0729587 1.0729587]。但是设置base_factor = 0.9 给出了 [0.8690965 0.9656628 1.0729587] 而我期待 1.019311
    • 嗯,也许我错过了什么,但为什么是 1.019311?您希望权重为[0.81, 0.9, 1.0] 还是[0.9, 0.95, 1.0]
    【解决方案2】:

    在 tensorflow 中,您首先使用张量预定义图形,然后再运行它。因此,与 numpy 数组一起使用的函数不适用于 tensorflow 是很常见的。在您的情况下 num_examples 是问题所在。

    想象一下,在 tensorflow 中,这个损失函数不会在您每次需要时都被调用,而是在训练模型时构建图表,用于计算图表中的损失函数。

    因此,当 keras 想尝试在 tensorflow 中构建损失函数时,您的 y_true 是一个抽象张量,很可能对于您的第一个形状没有 None,因为尚未定义 batch_size。

    您必须以不依赖于 batch_size 的方式重写损失函数 => 删除变量 num_examples

    【讨论】:

      猜你喜欢
      • 2021-12-15
      • 2020-10-05
      • 2019-06-25
      • 1970-01-01
      • 2021-01-03
      • 2018-02-24
      • 2020-12-19
      • 2017-12-18
      • 2020-03-27
      相关资源
      最近更新 更多