Keras自定义损失函数错误“未提供渐变”

时间:2020-09-13 18:36:10

标签: python tensorflow keras

问题描述

我正在尝试使用基于TensorFlow 2.3.0的Keras训练网络。任务是创建新图片。在第一个简单的原型/概念证明中,我试图训练网络以仅具有给定数量的非黑色像素来创建图片。因此,我需要定义一个自定义损失函数。这样做,我得到了ValueError: No gradients provided for any variable,但我还无法解决。

最重要的是,我希望有一种方法来编写此损失函数,而不必急于运行(see my previous question)

代码段

def custom_loss(y_true, y_pred):
    ndarray = y_pred.numpy()
    mse = np.zeros(ndarray.shape[0])
    for i in range(ndarray.shape[0]):
        true_area = int(y_true[i][0] * 100000).numpy()
        pic = ndarray[i, :, :, :]
        img_np = (pic * 255).astype(np.uint8)
        img = tf.keras.preprocessing.image.array_to_img(img_np)
        count_area = count_nonblack_pil(img)
        mse[i] = ((count_area - true_area) / 100000)**2
        #img.save(f"custom_loss {i:03d} True {true_area:06d} Count {count_area:06d} MSE {mse:0.4f}.jpg")
    return mse

if __name__ == '__main__':
    tf.config.run_functions_eagerly(True)
    ...
    model.compile(loss=custom_loss, optimizer="adam", run_eagerly=True)
    model.fit(x=train_data, y=train_data, batch_size=16, epochs=10)

运行此代码会给我错误消息:

ValueError: No gradients provided for any variable: ['dense/kernel:0', 'dense/bias:0', 'conv2d/kernel:0', 'conv2d/bias:0', 'conv2d_1/kernel:0', 'conv2d_1/bias:0', 'conv2d_2/kernel:0', 'conv2d_2/bias:0', 'conv2d_3/kernel:0', 'conv2d_3/bias:0'].

到目前为止我尝试过的一切

错误听起来像损失函数不可微,但是为什么不应该呢?

谷歌搜索一个解决方案,我发现了一个建议,即我可能有missed to pass the labels,也有here,但是我已经通过保存一些带有标签的图片进行了检查(请参见上面代码中注释的行) 。这样就好了!

除此之外,我没有找到任何有用的提示,总之,谷歌点击量并不太多……(似乎是我想做的异国情调?)。有什么想法吗?

编辑

感谢您的快速反馈,对于未能非常清楚地描述损失函数的任务,我们深表歉意。

我有一个模型可以基于单个浮点输入创建完整的533x800 RGB图片,并将其作为y_true传递给损失函数。模型创建的图片也以y_pred的形式传递给损失函数。现在,损失函数调用一个小函数count_nonblack_pil来计算y_pred中非黑色像素的数量。然后,将损耗计算为y_true与计数像素之间的平方差。通过最小化这种差异,我希望对模型进行训练,以便能够创建具有许多接近输入值的非黑色像素的图片。并没有真正的用处,但只是一个简单的概念证明,可以用来以后计划使用不同的损失函数(我想使用其他经过训练的模型来为更有用和更复杂的任务计算损失)。

希望如此。为了更加清楚:

y_true size : 16
y_pred size : 20467200

y_pred包含16幅533x800图片,其中包含3种颜色,即20467200。 y_true仅包含像素的16个目标值。

编辑:解决方案

我现在已经了解了这个问题,并用JimBiardCics进行了概括:“请记住,您编写的python函数(custom_loss)被调用来生成和编译C函数。训练。调用python custom_loss函数时,参数是没有附加数据的张量对象。K.eval调用将失败,K.shape调用也会失败。“

2 个答案:

答案 0 :(得分:0)

自定义损失应仅使用张量流操作。 Tensorflow尚无法(尚未)从numpy或任何其他库中计算操作的梯度。您将不得不将所有计算更改为某些tf.op函数。

请注意,急于执行并不能解决这个问题。

答案 1 :(得分:0)

引发错误,因为这些操作不是图形的一部分,因此无法区分。您想要做的事情不需要Eager,而是一系列的tensorflow方法可以完成您想要的事情。

由于算法的确切细节有点模糊,因此我将提出一个更简单的解决方案。看来您的总体目标是生成相似的图像,但与原始图像相比减少黑色图像的数量。 您可以通过保留原始损失但增加罚款来做到这一点。我假设您需要MSE损失,但这没关系,因为您可以使用其他任何东西:

Loss = alpha * MSE + beta * nr_of_black_pixels_in_pred 

使用alphabeta来调整各自的影响力。这可以通过这样的损失来实现:

def custom_loss(y_true, y_pred):
    alpha, beta = 0.8, 0.2 # percentage influence
    mse = tf.keras.losses.mean_squared_error(y_true, y_pred)
    count = tf.where(y_pred==0., tf.ones_like(y_pred), tf.zeros_like(y_pred))
    bp = tf.math.reduce_sum(count)
    return alpha * mse + beta * bp

好处是您现在甚至可以如果要包含“黑色”像素值,请说y_pred<50.

为什么这样做有效,为什么这是一个更好的解决方案?如果我们仅惩罚黑色像素,则网络可能会仅生成白色图像以获得最佳的损失(或将所有像素的值设置为0到1)。这些“欺骗”解决方案可能都不是理想的。因此,我们需要保留原始损失以保留原始行为,并对其进行罚款。

附加的新惩罚现在可以自动减少tf.where中布尔条件为真的频率。由于此数字和损失的比例完全不同,因此您可能必须另外对惩罚进行标准化。 alphabeta也将需要耐心和根据经验进行优化。这些类型的参数只能在很小的范围内正常工作,您需要找到它们。为此,我建议添加一个自定义指标,以打印出罚款占总损失的百分比。由于规模不同,可能有必要将beta设置为一个很小的数字(但这是高度特定于应用程序的。)