Keras 自定义损失函数错误"No gradients provided"



问题描述

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

最重要的是,我更喜欢一种不用急切地运行就可以对这个loss函数进行编码的方法(参见我之前的问题)。

代码片段

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'].

到目前为止我已经尝试了什么

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

在谷歌上搜索解决方案时,我发现了一个建议,我可能错过了传递标签的机会,这里也是,但我已经通过保存一些带有标签的图片来检查了这一点(请参阅上面代码中注释的行)。这很好用!

除此之外,我找不到任何有用的提示,总之谷歌点击量不多。。。(我想做的似乎很奇怪?)。有什么想法吗?

编辑

感谢您的快速反馈,很抱歉没有非常清楚地描述损失函数的任务,让我再试一次:

我有一个模型,它基于单个浮点输入创建了一个完整的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。CCD_ 8仅包含像素的16个目标值。

编辑:解决方案

我现在已经理解了这个问题,JimBiardCics很好地总结道:;请记住,您编写的python函数(custom_loss)是用来生成和编译C函数的。编译后的函数就是训练过程中所称的函数。当调用python custom_loss函数时,参数是没有附加数据的张量对象。K.eval调用将失败,K.shape调用也将失败">

自定义丢失应仅使用tensorflow操作。Tensorflow(目前)还不能计算numpy或任何其他库中操作的梯度。您必须将所有计算更改为某些tf.op函数。

需要注意的是,急于执行并不能解决这个问题。

抛出错误是因为这些操作不是图的一部分,因此无法区分。你想要做的并不需要Eagle,而是一系列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

好处是,如果你想包括"CCD_;"黑色";像素值。

为什么这有效,为什么这是一个更好的解决方案?如果我们只惩罚黑色像素,网络可能只生成白色图像,以获得尽可能好的损失(或将值为0的所有像素设置为1)。这些";欺骗;解决方案可能是可取的。因此,我们需要保留最初的损失来保留最初的行为,并对其进行修改

额外的新惩罚现在自动减少tf.where中布尔条件为真的频率。由于这个数字和损失是完全不同的,您可能需要额外规范处罚。CCD_ 14和CCD_。这些类型的参数在正常工作的情况下具有非常小的值范围,这是您需要找到的。为此,我建议添加一个自定义指标,以打印出罚款造成的总损失的百分比。由于规模差异,可能有必要将beta作为一个非常小的数字(但这是高度特定于应用程序的)。

相关内容

最新更新